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() + " 加入\n");
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() + " 離開\n");
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 + "\n" );
42             } else {                            // 自己
43                 channel.writeAndFlush("[you] " + msg + "\n" );
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         // 使用'\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())    // 配置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         // 使用'\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());// 配置 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                 // 每行成一幀輸出,以"\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 }

 

 

三、運行效果 

   先運行服務端程序,然后在運行兩次客戶端程序,如下:

  服務端輸出:

  

  首先連接的客戶端輸出:

  

 

  隨便選個客戶端在控制台輸出信息並回車,如下:

   自身輸出:

  

  另一客戶端輸出:

  

 

  以上…… 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM