前言介紹:
我們的NettyServer收到數據后,需要群發給當前鏈接到服務端的所有小伙伴。
技術點:
1、ChannelGroup 【io.netty.channel.group.DefaultChannelGroup】
歡迎加入:itstack | Netty The Sniper 5360692
環境需求:
1、jdk1.7以上【jdk1.7以下只能部分支持netty】
2、Netty-all-5.0【netty3.x 4.x 5每次的變化較大,接口類名也隨着變化】
3、telnet 測試【可以現在你的win7機器上測試這個命令,用於鏈接到服務端的測試命令】【本案例中已經很不好滿足測試需求了】
4、最好下載個網絡調試助手,它能幫助你測試服務端、客戶端
代碼部分:
======================
TestNettyServerBaseDemo
src
com.itstack
ChildChannelHandler.java
MyChannelHandlerPool.java
MyServerHanlder.java
NettyServer.java
======================
ChildChannelHandler.java
- package com.itstack;
- import io.netty.buffer.ByteBuf;
- import io.netty.buffer.Unpooled;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.handler.codec.DelimiterBasedFrameDecoder;
- import io.netty.handler.codec.Delimiters;
- import io.netty.handler.codec.FixedLengthFrameDecoder;
- import io.netty.handler.codec.LineBasedFrameDecoder;
- import io.netty.handler.codec.string.StringDecoder;
- import io.netty.handler.codec.string.StringEncoder;
- public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
- @Override
- protected void initChannel(SocketChannel e) throws Exception {
- System.out.println("報告");
- System.out.println("信息:有一客戶端鏈接到本服務端");
- System.out.println("IP:" + e.localAddress().getHostName());
- System.out.println("Port:" + e.localAddress().getPort());
- System.out.println("報告完畢");
- // 解碼器
- // 基於換行符號
- e.pipeline().addLast(new LineBasedFrameDecoder(1024));
- // 基於指定字符串【換行符,這樣功能等同於LineBasedFrameDecoder】
- // e.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, false, Delimiters.lineDelimiter()));
- // 基於最大長度
- // e.pipeline().addLast(new FixedLengthFrameDecoder(4));
- // 解碼轉String
- e.pipeline().addLast(new StringDecoder());
- // 編碼器 String
- e.pipeline().addLast(new StringEncoder());
- // 在管道中添加我們自己的接收數據實現方法
- e.pipeline().addLast(new MyServerHanlder());
- }
- }
MyChannelHandlerPool.java
- package com.itstack;
- import io.netty.channel.group.ChannelGroup;
- import io.netty.channel.group.DefaultChannelGroup;
- import io.netty.util.concurrent.GlobalEventExecutor;
- /**
- *
- * 這里講ChannelGroup單獨放到一個類里,並有多個客戶端使用
- * 同時ChannelGroup是static的
- * 說明:這不是唯一的處理方式
- *
- */
- public class MyChannelHandlerPool {
- public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
- }
MyServerHanlder.java
- package com.itstack;
- import java.util.Date;
- import io.netty.buffer.ByteBuf;
- import io.netty.buffer.Unpooled;
- import io.netty.channel.ChannelHandlerAdapter;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.bytes.ByteArrayDecoder;
- public class MyServerHanlder extends ChannelHandlerAdapter{
- /*
- * channelAction
- *
- * channel 通道
- * action 活躍的
- *
- * 當客戶端主動鏈接服務端的鏈接后,這個通道就是活躍的了。也就是客戶端與服務端建立了通信通道並且可以傳輸數據
- *
- */
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- System.out.println(ctx.channel().localAddress().toString()+" channelActive");
- //添加到channelGroup 通道組
- MyChannelHandlerPool.channelGroup.add(ctx.channel());
- //通知您已經鏈接上客戶端
- String str = "您已經開啟與服務端鏈接"+" "+ctx.channel().id()+new Date()+" "+ctx.channel().localAddress();
- ctx.writeAndFlush(str);
- }
- /*
- * channelInactive
- *
- * channel 通道
- * Inactive 不活躍的
- *
- * 當客戶端主動斷開服務端的鏈接后,這個通道就是不活躍的。也就是說客戶端與服務端的關閉了通信通道並且不可以傳輸數據
- *
- */
- public void channelInactive(ChannelHandlerContext ctx) throws Exception {
- // 從channelGroup中移除,當有客戶端退出后,移除channel。
- MyChannelHandlerPool.channelGroup.remove(ctx.channel());
- System.out.println(ctx.channel().localAddress().toString()+" channelInactive");
- }
- /*
- * channelRead
- *
- * channel 通道
- * Read 讀
- *
- * 簡而言之就是從通道中讀取數據,也就是服務端接收客戶端發來的數據
- * 但是這個數據在不進行解碼時它是ByteBuf類型的后面例子我們在介紹
- *
- */
- public void channelRead(ChannelHandlerContext ctx, Object msg)
- throws Exception {
- //注意此處已經不需要手工解碼了
- System.out.println(ctx.channel().id()+""+new Date()+" "+msg);
- //通知您已經鏈接上客戶端[給客戶端穿回去的數據加個換行]
- String str = "服務端收到:"+ctx.channel().id()+new Date()+" "+msg+"\r\n";
- //收到信息后,群發給所有小伙伴
- MyChannelHandlerPool.channelGroup.writeAndFlush(str);
- }
- /*
- * channelReadComplete
- *
- * channel 通道
- * Read 讀取
- * Complete 完成
- *
- * 在通道讀取完成后會在這個方法里通知,對應可以做刷新操作
- * ctx.flush()
- *
- */
- public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
- ctx.flush();
- }
- /*
- * exceptionCaught
- *
- * exception 異常
- * Caught 抓住
- *
- * 抓住異常,當發生異常的時候,可以做一些相應的處理,比如打印日志、關閉鏈接
- *
- */
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
- throws Exception {
- ctx.close();
- System.out.println("異常信息:\r\n"+cause.getMessage());
- }
- }
NettyServer.java
- package com.itstack;
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
- public class NettyServer {
- public static void main(String[] args) {
- try {
- System.out.println("服務端開啟等待客戶端鏈接");
- new NettyServer().bing(7397);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public void bing(int port) throws Exception{
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workGroup = new NioEventLoopGroup();
- try {
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, workGroup);
- b.channel(NioServerSocketChannel.class);
- b.option(ChannelOption.SO_BACKLOG, 1024);
- b.childHandler(new ChildChannelHandler());
- // 綁定端口
- ChannelFuture f = b.bind(port).sync();
- // 等待服務端監聽端口關閉
- f.channel().closeFuture().sync();
- } finally {
- // 優雅的退出
- bossGroup.shutdownGracefully();
- workGroup.shutdownGracefully();
- }
- }
- }
1、啟動NettyServer
2、控制台輸出:
----------------------------------------------
服務端開啟等待客戶端鏈接
----------------------------------------------
3、開啟2個以上客戶端模擬軟件
4、
服務端端控制台輸出:
----------------------------------------------
報告
信息:有一客戶端鏈接到本服務端
IP:user-PC
Port:7397
報告完畢
user-PC/192.168.30.223:7397 channelActive
defa23d9Tue Dec 30 16:54:51 CST 2014 群號:5360692
defa23d9Tue Dec 30 16:54:51 CST 2014 群號:5360692
defa23d9Tue Dec 30 16:54:52 CST 2014 群號:5360692
defa23d9Tue Dec 30 16:54:53 CST 2014 群號:5360692
報告
信息:有一客戶端鏈接到本服務端
IP:localhost.localdomain
Port:7397
報告完畢
localhost.localdomain/127.0.0.1:7397 channelActive
5f735249Tue Dec 30 16:55:02 CST 2014 1
5f735249Tue Dec 30 16:55:03 CST 2014 1
5f735249Tue Dec 30 16:55:04 CST 2014 1
5f735249Tue Dec 30 16:55:04 CST 2014 1
5f735249Tue Dec 30 16:55:04 CST 2014 1
5f735249Tue Dec 30 16:55:05 CST 2014 1
5f735249Tue Dec 30 16:55:05 CST 2014 1
5f735249Tue Dec 30 16:55:05 CST 2014 1
5f735249Tue Dec 30 16:55:05 CST 2014 1
defa23d9Tue Dec 30 16:55:10 CST 2014 群號:5360692
----------------------------------------------
其中一個客戶端控制輸出:
---------------------------------------------
- 您已經開啟與服務端鏈接 defa23d9Tue Dec 30 16:54:49 CST 2014 user-PC/192.168.30.223:7397
- 服務端收到:defa23d9Tue Dec 30 16:54:51 CST 2014 群號:5360692
- 服務端收到:defa23d9Tue Dec 30 16:54:51 CST 2014 群號:5360692
- 服務端收到:defa23d9Tue Dec 30 16:54:52 CST 2014 群號:5360692
- 服務端收到:defa23d9Tue Dec 30 16:54:53 CST 2014 群號:5360692
- 服務端收到:5f735249Tue Dec 30 16:55:02 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:03 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:04 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:04 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:04 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:05 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:05 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:05 CST 2014 1
- 服務端收到:5f735249Tue Dec 30 16:55:05 CST 2014 1
- 服務端收到:defa23d9Tue Dec 30 16:55:10 CST 2014 群號:5360692
- ---------------------------------------------
