Netty的群发实现2

来源:互联网 发布:朝鲜脱北者 知乎 编辑:程序博客网 时间:2024/06/08 23:06

下面代码需要talent 进行模拟客户端。采用的是netty4.1.16 JDK1.8

我用的Xshell一样可以实现

具体代码实现如下

package qunfa;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 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();        }    }    public static void main(String[] args) {        try {            System.out.println("服务端开启等待客户端链接");            new NettyServer().bing(2333);        } catch (Exception e) {            e.printStackTrace();        }    }}
package qunfa;import io.netty.channel.ChannelInitializer;import io.netty.channel.socket.SocketChannel;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("报告完毕");        // 解码器        // 以"\n"或者"\r\n"作为分隔符,解决粘包和半包问题        e.pipeline().addLast(new LineBasedFrameDecoder(1024));        // 基于指定字符串【换行符,这样功能等同于LineBasedFrameDecoder,以/r/n或者/n作为分隔符】        //e.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, false, Delimiters.lineDelimiter()));        // 基于最大长度        //e.pipeline().addLast(new FixedLengthFrameDecoder(4));        e.pipeline().addLast(new StringDecoder());        e.pipeline().addLast(new StringEncoder());        e.pipeline().addLast(new MyServerHanlder());    }}

package qunfa;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);}

package qunfa;import java.util.Date;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;//public class MyServerHanlder extends ChannelHandlerAdapter{public class MyServerHanlder extends ChannelInboundHandlerAdapter{    /*    * channelAction    *    * channel 通道    * action 活跃的    *    * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据    *    */    @Override    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 不活跃的    *    * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据    *    */    @Override    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类型的后面例子我们在介绍    *    */    @Override    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()    *    */    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {        ctx.flush();    }    /*    * exceptionCaught    *    * exception异常    * Caught抓住    *    * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接    *    */    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        ctx.close();        System.out.println("异常信息:\r\n"+cause.getMessage());    }}