Netty 消息转发
梳理一下Netty的模型
- Netty抽象出两组线程池,boosGroup负责客户端的连接, workGroup负责线程的连接,两者都是NioEventLoopGroup。
- NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环组都有多个NioEventLoop。
- 每个boosGroup中循环NioEventLoop的步骤
a) 轮询accept事件
b) 处理accept事件,处理client客户端的连接,生成NioSocketChannel,并将其则测到Work Group上的摸某个NioEventLoop上的selecor
c) 处理任务队列的任务,即runTask - 每个workGoup循环执行NioEventLoop的步骤
a) 论寻 read/ write事件
b) 处理I/O事件,即read/ write事件,在对应的channel上处理
c) 处理任务队列的任务 - 每个workGroup 处理任务时,会使用pipline管道,pipline管道中包含了channel,即通过pipline可以获取对应的通道,管道中维护了很多处理器。
关于Netty的消息转发示例
(1) 编写server端
package src.com.nettys.chart;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* 群聊的服务端
*
*/
public class ChartServer {
private int port;
public ChartServer(int port) {
this.port = port;
}
/**
* 服务端 ......
*/
public void runServer() {
NioEventLoopGroup boosGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boosGroup , workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG , 128)
.childOption(ChannelOption.SO_KEEPALIVE , true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("decoder" , new StringDecoder());
pipeline.addLast("encoder" , new StringEncoder());
pipeline.addLast(new ChartServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture();
}catch (Exception e) {
boosGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new ChartServer(7001).runServer();
}
}
handler处理器
package src.com.nettys.chart;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 服务端消息处理
*/
public class ChartServerHandler extends SimpleChannelInboundHandler<String> {
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
/**
* 建立连接 .. 第一个处理
* 将当前channel 加入到 channelGroup
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
/**
* 将该客户的信息加入到客户端
*/
channel.writeAndFlush("[客户端]" + channel.remoteAddress() + "加入聊天,时间 : " + simpleDateFormat.format(new Date()));
channelGroup.add(channel);
}
/**
* 移除客户端
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.writeAndFlush("[客户端]" + channel.remoteAddress() + "下线了,时间: " + simpleDateFormat.format(new Date()));
System.out.println("通道组: " + channelGroup.size());
}
/**
* 上线了
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + " 上线了~");
}
/**
* 下线了
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + " 离线了~");
}
/**
* 表示channel 处理消息
* @param channelHandlerContext
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
Channel channel = channelHandlerContext.channel();
channelGroup.forEach(ch -> {
if (channel != ch) { // 不是当前客户, 转发消息
ch.writeAndFlush("[客户] 转发消息: " + channel.remoteAddress() + "") ;
} else {
ch.writeAndFlush("[自己] 发送了消息" + channel.remoteAddress());
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
(2) 编写客户端
package src.com.nettys.chart;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;
/**
* 聊天的客户端
*/
public class ChartClient {
private int port;
private String host;
public ChartClient(int port, String host) {
this.port = port;
this.host = host;
}
public void runCilent() {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("encoder" , new StringEncoder());
pipeline.addLast("decoder" , new StringDecoder());
pipeline.addLast(new ChartClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
// 得到channel
Channel channel = channelFuture.channel();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
channel.writeAndFlush(line + "\r\n");
}
}catch (Exception e) {
}finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new ChartClient(7001,"127.0.0.1" ).runCilent();
}
}
处理事件的handler
package src.com.nettys.chart;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.concurrent.EventExecutorGroup;
public class ChartClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
System.out.println(msg.trim());
}
}