这节讲解基于 Netty 快速实现一个聊天小程序。
一、服务端
1. SimpleChatServerHandler(处理器类)
该类主要实现了接收来自客户端的消息并转发给其他客户端。
1 /** 2 ?* 服务端处理器 3 ?*/ 4 public class SimpleChatServerHandler extends SimpleChannelInboundHandler<String> { 5 ????public static ChannelGroup channels ?6 ????????= new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 7 ?????8 ????/** 9 ?????* 收到新的客户端连接时调用10 ?????* 将客户端channel存入列表,并广播消息11 ?????*/12 ????@Override13 ????public void handlerAdded(ChannelHandlerContext ctx) throws Exception {14 ????????Channel incoming = ctx.channel();15 ????????// 广播加入消息16 ????????channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");17 ????????channels.add(incoming); ???????// 存入列表 ???18 ????}19 ????20 ????/**21 ?????* 客户端连接断开时调用22 ?????* 广播消息23 ?????*/24 ????@Override25 ????public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {26 ????????Channel incoming = ctx.channel();27 ????????// 广播离开消息28 ????????channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");29 ????????// channel会自动从ChannelGroup中删除 30 ????}31 ????32 ????/**33 ?????* 收到消息时调用34 ?????* 将消息转发给其他客户端35 ?????*/36 ????@Override37 ????protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {38 ????????Channel incoming = ctx.channel();39 ????????for(Channel channel : channels) { ???????// 遍历所有连接的客户端40 ????????????if(channel != incoming) { ???????????// 其他客户端41 ????????????????channel.writeAndFlush("[" + incoming.remoteAddress() + "] " + msg + "\n" );42 ????????????} else { ???????????????????????????// 自己43 ????????????????channel.writeAndFlush("[you] " + msg + "\n" );44 ????????????}45 ????????}46 ????}47 ????48 ????/**49 ?????* 监听到客户端活动时调用50 ?????*/51 ????@Override52 ????public void channelActive(ChannelHandlerContext ctx) throws Exception {53 ????????Channel incoming = ctx.channel();54 ????????System.out.println("SimpleChatClient: " + incoming.remoteAddress() + " 在线");55 ????}56 ????57 ????/**58 ?????* 监听到客户端不活动时调用59 ?????*/60 ????@Override61 ????public void channelInactive(ChannelHandlerContext ctx) throws Exception {62 ????????Channel incoming = ctx.channel();63 ????????System.out.println("SimpleChatClient: " + incoming.remoteAddress() + " 掉线");64 ????}65 ????66 ????/**67 ?????* 当Netty由于IO错误或者处理器在处理事件抛出异常时调用68 ?????* 关闭连接69 ?????*/70 ????@Override71 ????public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {72 ????????Channel incoming = ctx.channel();73 ????????System.out.println("SimpleChatClient: " + incoming.remoteAddress() + " 异常");74 ????}75 }
2. SimpleChatServerInitializer(配置 Channel 类)
该类添加分隔符协议处理类,解码、编码器还有自定义处理器。
1 /** 2 ?* 服务器配置初始化 3 ?* 添加多个处理器 4 ?*/ 5 public class SimpleChatServerInitializer extends ChannelInitializer<SocketChannel> { 6 ?7 ????@Override 8 ????protected void initChannel(SocketChannel ch) throws Exception { 9 ????????ChannelPipeline pipeline = ch.pipeline();10 ????????// 添加处理类11 ????????// 使用‘\r‘‘\n‘分割帧12 ????????pipeline.addLast("framer", 13 ????????????????new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));14 ????????// 解码、编码器15 ????????pipeline.addLast("decoder", new StringDecoder());16 ????????pipeline.addLast("encoder", new StringEncoder());17 ????????// 处理器18 ????????pipeline.addLast("handler", new SimpleChatServerHandler());19 ????????20 ????????System.out.println("SimpleChatClient: " + ch.remoteAddress() + "连接上");21 ????}22 23 }
3. SimpleChatServer(服务端主程序)
启动服务端。
1 /** 2 ?* 服务端 main 启动 3 ?*/ 4 public class SimpleChatServer { 5 ????private int port; ???????// 端口 6 ?????7 ????public SimpleChatServer(int port) { 8 ????????this.port = port; 9 ????}10 ????11 ????// 配置并开启服务器12 ????public void run() throws Exception {13 ????????EventLoopGroup bossGroup = new NioEventLoopGroup(); ???????// 用来接收进来的连接14 ????????EventLoopGroup workerGroup = new NioEventLoopGroup(); ???// 用来处理已接收的连接15 ????????16 ????????try {17 ????????????ServerBootstrap sb = new ServerBootstrap(); ???????????// 启动NIO服务的辅助启动类18 ????????????sb.group(bossGroup, workerGroup)19 ????????????????.channel(NioServerSocketChannel.class) ???????????????// 设置如何接受连接20 ????????????????.childHandler(new SimpleChatServerInitializer()) ???// 配置Channel21 ????????????????.option(ChannelOption.SO_BACKLOG, 128) ???????????????// 设置缓冲区22 ????????????????.childOption(ChannelOption.SO_KEEPALIVE, true); ???// 启用心跳机制23 ????????????24 ????????????System.out.println("SimpleChatServer 启动了");25 ????????????ChannelFuture future = sb.bind(port).sync(); ???????// 绑定端口,开始接收连接26 ????????????future.channel().closeFuture().sync(); ???????????????// 等待关闭服务器(不会发生)27 ????????} finally {28 ????????????workerGroup.shutdownGracefully();29 ????????????bossGroup.shutdownGracefully();30 ????????????System.out.println("SimpleChatServer 关闭了");31 ????????}32 ????}33 ????34 ????public static void main(String[] args) throws Exception {35 ????????int port = 8080;36 ????????new SimpleChatServer(port).run(); ????// 开启服务器37 ????}38 }
二、客户端
1. SimpleChatClientHandler(处理器类)
直接输出收到的消息。
1 /** 2 ?* 客户端处理类 3 ?* 直接输出收到的消息 4 ?*/ 5 public class SimpleChatClientHandler extends SimpleChannelInboundHandler<String> { 6 ?7 ????@Override 8 ????protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { 9 ????????System.out.println(msg); ???// 直接输出消息 ???????10 ????}11 12 }
2. SimpleChatClientInitializer(配置 Channel 类)
与服务端类似。
1 /** 2 ?* 客户端配置初始化 3 ?* 与服务端类似 4 ?*/ 5 public class SimpleChatClientInitializer extends ChannelInitializer<SocketChannel> { 6 ?7 ????@Override 8 ????protected void initChannel(SocketChannel ch) throws Exception { 9 ????????ChannelPipeline pipeline = ch.pipeline();10 ????????// 添加处理类11 ????????// 使用‘\r‘‘\n‘分割帧12 ????????pipeline.addLast("framer", 13 ????????????????new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));14 ????????// 解码、编码器15 ????????pipeline.addLast("decoder", new StringDecoder());16 ????????pipeline.addLast("encoder", new StringEncoder());17 ????????// 处理器18 ????????pipeline.addLast("handler", new SimpleChatClientHandler());19 ????}20 21 }
3. SimpleChatClient(客户端主程序)
接收来自控制台的消息,每帧以 "\r\n" 结尾,再发给服务端。
1 /** 2 ?* 客户端 3 ?* 开启客户端,接收控制台输入并发送给服务端 4 ?*/ 5 public class SimpleChatClient { 6 ????private final String host; ???????// IP 7 ????private final int port; ???????// 端口 8 ?????9 ????public SimpleChatClient(String host, int port) {10 ????????this.host = host;11 ????????this.port = port;12 ????}13 ????14 ????// 配置并运行客户端15 ????public void run() throws Exception {16 ????????EventLoopGroup group = new NioEventLoopGroup();17 ????????try {18 ????????????Bootstrap b = new Bootstrap(); ???????// 客户端辅助启动类19 ????????????b.group(group) ???????????????????????????????????// 客户端只需要一个用来接收并处理连接20 ????????????????.channel(NioSocketChannel.class) ???????????// 设置如何接受连接21 ????????????????.handler(new SimpleChatClientInitializer());// 配置 channel22 ????????????// 连接服务器23 ????????????Channel channel = b.connect(host, port).sync().channel();24 ????????????// 读取控制台输入字符25 ????????????BufferedReader in = new BufferedReader(new InputStreamReader(System.in));26 ????????????while(true) {27 ????????????????// 每行成一帧输出,以"\r\n"结尾28 ????????????????channel.writeAndFlush(in.readLine() + "\r\n");29 ????????????}30 ????????} catch (Exception e) {31 ????????????e.printStackTrace(); ???????// 输出异常32 ????????} finally {33 ????????????group.shutdownGracefully(); ???// 关闭34 ????????}35 ????}36 ????37 ????public static void main(String[] args) throws Exception {38 ????????new SimpleChatClient("localhost", 8080).run(); ???????// 启动客户端39 ????}40 41 }
三、运行效果
先运行服务端程序,然后在运行两次客户端程序,如下:
服务端输出:
首先连接的客户端输出:
随便选个客户端在控制台输出信息并回车,如下:
自身输出:
另一客户端输出:
以上……
Netty 聊天小程序
原文地址:https://www.cnblogs.com/coderJiebao/p/Netty11.html