zoukankan      html  css  js  c++  java
  • Netty 聊天小程序

      这节讲解基于 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     @Override
    13     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    14         Channel incoming = ctx.channel();
    15         // 广播加入消息
    16         channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入
    ");
    17         channels.add(incoming);        // 存入列表    
    18     }
    19     
    20     /**
    21      * 客户端连接断开时调用
    22      * 广播消息
    23      */
    24     @Override
    25     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    26         Channel incoming = ctx.channel();
    27         // 广播离开消息
    28         channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开
    ");
    29         // channel会自动从ChannelGroup中删除 
    30     }
    31     
    32     /**
    33      * 收到消息时调用
    34      * 将消息转发给其他客户端
    35      */
    36     @Override
    37     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 + "
    " );
    42             } else {                            // 自己
    43                 channel.writeAndFlush("[you] " + msg + "
    " );
    44             }
    45         }
    46     }
    47     
    48     /**
    49      * 监听到客户端活动时调用
    50      */
    51     @Override
    52     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     @Override
    61     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     @Override
    71     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         // 使用'
    ''
    '分割帧
    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())    // 配置Channel
    21                 .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         // 使用'
    ''
    '分割帧
    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(客户端主程序)

       接收来自控制台的消息,每帧以 " " 结尾,再发给服务端。

     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());// 配置 channel
    22             // 连接服务器
    23             Channel channel = b.connect(host, port).sync().channel();
    24             // 读取控制台输入字符
    25             BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    26             while(true) {
    27                 // 每行成一帧输出,以"
    "结尾
    28                 channel.writeAndFlush(in.readLine() + "
    ");
    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 }

    三、运行效果 

       先运行服务端程序,然后在运行两次客户端程序,如下:

      服务端输出:

      

      首先连接的客户端输出:

      

      随便选个客户端在控制台输出信息并回车,如下:

       自身输出:

      

      另一客户端输出:

      

      以上…… 

  • 相关阅读:
    Spring Web Flow 简介
    LeetCode:按序打印【1114】
    Java基础教程:多线程基础(5)——倒计时器(CountDownLatch)
    React:快速上手(8)——前后端分离的跨域访问与会话保持
    SpringBoot学习笔记:自定义拦截器
    Java进阶教程:垃圾回收
    SpringMVC:学习笔记(12)——ThreadLocal实现会话共享
    Node.js学习笔记(4):Yarn简明教程
    Docker:学习笔记(1)——核心概念及Ubuntu安装
    Java基础教程:内部类
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/Netty11.html
Copyright © 2011-2022 走看看