- 聊天室实现
- 实验过程
- 过程分析
- 入站和出战
1. 聊天室实现
- 服务端启动类:MyChatServer
-
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class MyChatServer { public static void main(String[] args) throws Exception{ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workGroup).channel(NioServerSocketChannel.class). childHandler(new MyChatServerInitailizer()); ChannelFuture channelFuture = serverBootstrap.bind(8899).sync(); channelFuture.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
-
-
服务端initChannel:MyChatServerInitailizer
-
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; public class MyChatServerInitailizer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new DelimiterBasedFrameDecoder(4096,Delimiters.lineDelimiter())); //负责按/r/n拆包 pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));//Byte自动转String pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));//Byte自动转String pipeline.addLast(new MyChatServerHandler());//Inbound类型Handler } }
-
-
服务端Handler:MyChatServerHandler
-
import io.netty.channel.Channel; 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 MyChatServerHandler extends SimpleChannelInboundHandler<String> { private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { final Channel channel = ctx.channel(); channelGroup.forEach(ch->{ if(channel != ch){ ch.writeAndFlush(channel.remoteAddress()+"发送消息:"+msg+"\n"); }else{ ch.writeAndFlush("【自己】"+msg+"\n"); } }); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); channelGroup.add(channel); System.out.println(new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(new Date()) + "————handlerAdded执行:"); channelGroup.writeAndFlush("【服务器】-"+channel.remoteAddress()+"加入\n"); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); System.out.println(new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(new Date()) + "————handlerRemoved执行:"); channelGroup.writeAndFlush("【服务器】-"+channel.remoteAddress()+"离开\n"); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); System.out.println(new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(new Date()) + "————channelActive执行:"); System.out.println(channel.remoteAddress()+"上线"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); System.out.println(new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(new Date()) + "————channelInactive执行:"); System.out.println(channel.remoteAddress()+"下线"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
-
-
客户端启动类:MyCahtClient
-
import java.io.BufferedReader; import java.io.InputStreamReader; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class MyCahtClient { public static void main(String[] args) throws Exception{ EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new MyCahtClientInitailizer()); Channel channel = bootstrap.connect("localhost",8899).channel(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); for(;;){ channel.writeAndFlush(br.readLine()+"\r\n"); } }finally { eventLoopGroup.shutdownGracefully(); } } }
-
-
客户端MyChatServerInitailizer
-
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; public class MyCahtClientInitailizer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new DelimiterBasedFrameDecoder(4096,Delimiters.lineDelimiter())); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new MyChatClientHandler()); } }
-
-
客户端Handler:MyChatClientHandler
-
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import java.text.SimpleDateFormat; import java.util.Date; public class MyChatClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(new Date()) + "————" +msg); } }
-
2. 实验过程
- 先起了服务端
- 起了三个客户端
- 客户端1 发送了两条数据
- 关闭客户端3
- 截图
服务端:
客户端1:
客户端2:
3.过程分析
- 从服务端的角度
- 启动了serverBootstrap引导类,绑定了Handler,绑定端口8899,等待连接
- client启动,执行到 channel.writeAndFlush(); 但还未输入文字
- 此时channel已经打开,Server先后执行了,handlerAdded(),channelActive()方法
- 待补充
4.入站和出站
- 如果是Client输入msg到Server,即Client写出(走Client的OutboundHandler),Server读入(走Server的InboundHandler)。
- 如果是Server把某个client的msg发给各个client,即Server写入(走Server的InboundHandler),Client读入(走Client的InboundHandler)
- 总结
- 1. 入站操作主要是指读取数据的操作;而出站操作主要是指写入数据的操作
2. 入站会从先读取,再执行入站的Handler;出站会先执行出站的Handler,再写入
- 1. 入站操作主要是指读取数据的操作;而出站操作主要是指写入数据的操作