• netty 聊天室 建立通道 发送消息 解决粘包问题


    效果图:

    服务端:

    package cn.itcast.netty.chat;
    
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.channel.group.ChannelGroup;
    import io.netty.channel.group.DefaultChannelGroup;
    import io.netty.util.concurrent.GlobalEventExecutor;
    
    import java.util.ArrayList;
    import java.util.List;
    
    //自定义一个服务器端业务处理类
    public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
    
        public static List<Channel> channels = new ArrayList<>();
    
        @Override  //通道就绪
        public void channelActive(ChannelHandlerContext ctx)  {
            Channel inChannel=ctx.channel();
            channels.add(inChannel);
            System.out.println("[Server]:"+inChannel.remoteAddress().toString().substring(1)+"上线");
        }
        @Override  //通道未就绪
        public void channelInactive(ChannelHandlerContext ctx)  {
            Channel inChannel=ctx.channel();
            channels.remove(inChannel);
            System.out.println("[Server]:"+inChannel.remoteAddress().toString().substring(1)+"离线");
        }
        @Override  //读取数据
        protected void channelRead0(ChannelHandlerContext ctx, String s)  {
            Channel inChannel=ctx.channel();
            for(Channel channel:channels){
                if(channel!=inChannel){
                    channel.writeAndFlush("["+inChannel.remoteAddress().toString().substring(1)+"]"+"说:"+s+"
    ");
                }
            }
        }
    
    }
    package cn.itcast.netty.chat;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.DelimiterBasedFrameDecoder;
    import io.netty.handler.codec.Delimiters;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    //聊天程序服务器端
    public class ChatServer {
    
        private int port; //服务器端端口号
    
        public ChatServer(int port) {
            this.port = port;
        }
    
        public void run() throws Exception {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) {
                                ChannelPipeline pipeline=ch.pipeline();
                                //往pipeline链中添加一个解码器
                                pipeline.addLast("decoder",new StringDecoder());
                                //往pipeline链中添加一个编码器
                                pipeline.addLast("encoder",new StringEncoder());
                                //往pipeline链中添加自定义的handler(业务处理类)
                                pipeline.addLast(new ChatServerHandler());
            }
        });
                System.out.println("Netty Chat Server启动......");
        ChannelFuture f = b.bind(port).sync();
                f.channel().closeFuture().sync();
            } finally {
                workerGroup.shutdownGracefully();
                bossGroup.shutdownGracefully();
                System.out.println("Netty Chat Server关闭......");
            }
        }
    
        public static void main(String[] args) throws Exception {
            new ChatServer(9999).run();
        }
    }

    客户端:

    package cn.itcast.netty.chat;
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    //自定义一个客户端业务处理类
    public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
            System.out.println(s.trim());
        }
    }
    package cn.itcast.netty.chat;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.DelimiterBasedFrameDecoder;
    import io.netty.handler.codec.Delimiters;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.Scanner;
    
    //聊天程序客户端
    public class ChatClient {
        private final String host; //服务器端IP地址
        private final int port;  //服务器端端口号
    
        public ChatClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public void run(){
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap = new Bootstrap()
                        .group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch){
                                ChannelPipeline pipeline=ch.pipeline();
                                //往pipeline链中添加一个解码器
                                pipeline.addLast("decoder",new StringDecoder());
                                //往pipeline链中添加一个编码器
                                pipeline.addLast("encoder",new StringEncoder());
                                //往pipeline链中添加自定义的handler(业务处理类)
                                pipeline.addLast(new ChatClientHandler());
                            }
                        });
    
                ChannelFuture cf=bootstrap.connect(host,port).sync();
                Channel channel=cf.channel();
                System.out.println("------"+channel.localAddress().toString().substring(1)+"------");
                Scanner scanner=new Scanner(System.in);
                channel.writeAndFlush("我是一个客户端");
                while (scanner.hasNextLine()){
                    String msg=scanner.nextLine();
                    channel.writeAndFlush(msg+"
    ");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                group.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            new ChatClient("127.0.0.1",9999).run();
        }
    }
    package cn.itcast.netty.chat;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.DelimiterBasedFrameDecoder;
    import io.netty.handler.codec.Delimiters;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.Scanner;
    
    //聊天程序客户端
    public class ChatClient {
        private final String host; //服务器端IP地址
        private final int port;  //服务器端端口号
        public static Channel channel;
        public ChatClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public void run(){
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap = new Bootstrap()
                        .group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch){
                                ChannelPipeline pipeline=ch.pipeline();
                                //往pipeline链中添加一个解码器
                                pipeline.addLast("decoder",new StringDecoder());
                                //往pipeline链中添加一个编码器
                                pipeline.addLast("encoder",new StringEncoder());
                                //往pipeline链中添加自定义的handler(业务处理类)
                                pipeline.addLast(new ChatClientHandler());
                            }
                        });
    
                ChannelFuture cf=bootstrap.connect(host,port).sync();
                channel=cf.channel();
                System.out.println("------"+channel.localAddress().toString().substring(1)+"------");
                channel.writeAndFlush("run方法");
    //            Scanner scanner=new Scanner(System.in);
    //            while (scanner.hasNextLine()){
    //                String msg=scanner.nextLine();
    //                channel.writeAndFlush(msg+"
    ");
    //            }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //group.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            new ChatClient("127.0.0.1",9999).run();
            channel.writeAndFlush("clinet-main方法");
        }
    }

     解决沾包

    /**
     * @description: netty消息解码器
     * @author: 
     * @create: 2018-11-30 21:58
     **/
    public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder {
    
        /**
         * @param maxFrameLength 第一个参数代表最大的序列化长度
         * @param lengthFieldOffset 代表长度属性的偏移量 简单来说就是message中 总长度的起始位置(Header中的length属性的起始位置) 4
         * @param lengthFieldLength 代表长度属性的长度 整个属性占多长(length属性为int,占4个字节)  4
         */
        public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
            super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
        }
    
        /**
         * 将bytebuf解码成想要的对象
         * @param ctx 上下文环境
         * @param in 输入
         */
        @Override
        protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
            //1 调用父类(LengthFieldBasedFrameDecoder)方法:  返回整包或者空
            ByteBuf frame  = (ByteBuf)super.decode(ctx, in);
    
            if(frame == null){
                return null;
            }
    
            NettyMessage message = new NettyMessage();
            Header header = new Header();
            header.setCrcCode(frame.readInt());
            header.setLen(frame.readInt());
    //        header.setStation(frame.readShort());
    //        header.setSource(frame.readBytes(10).toString(Charset.forName("UTF-8")));
    //        header.setDestination(frame.readBytes(10).toString(Charset.forName("UTF-8")));
            header.setStation(frame.readBytes(20).toString(Charset.forName("UTF-8")));
            header.setSource(frame.readBytes(20).toString(Charset.forName("UTF-8")));
            header.setDestination(frame.readBytes(20).toString(Charset.forName("UTF-8")));
            header.setComponent(frame.readShort());
            header.setType(frame.readByte());
            Object body = null;
    
            if (frame.readableBytes() > 0){//说明body中有值
                if((header.getType() == NettyMessageTypeEnum.DEVICE_REAL_REQ.getCode()) || (header.getType() == NettyMessageTypeEnum.DEVICE_BASE_REQ.getCode()) ){
                    body = frame.toString(Charset.forName("GBK"));
                }else{
                    body = frame.toString(Charset.forName("utf-8"));
                }
                message.setBody(body);
            }
    
            message.setHeader(header);
            message.setBody(body);
            return message;
        }
    
    }
    /**
     * @description: netty消息编码器
     * @author: 
     * @create: 2018-11-30 21:59
     **/
    public class NettyMessageEncoder extends MessageToByteEncoder<NettyMessage> {
    
        /**
         *  将NettyMessage对象编码成ByteBuffer对象
         * @param ctx 连接的上下文环境
         * @param msg 需要编码的消息
         * @param sendBuffer 编码完成的结果
         */
        @Override
        protected void encode(ChannelHandlerContext ctx, NettyMessage msg, ByteBuf sendBuffer) throws Exception {
            //TODO: 如果在handler中抛出异常  会不会断掉通道?  需要做一个试验
            //TODO:将对象作为值传递 并后续继续使用  可读性差  bytebuffer
            if (msg == null || msg.getHeader() == null){
                throw new Exception("encode message is null!!");
            }
            Header header = msg.getHeader();
            sendBuffer.writeInt(header.getCrcCode());
            sendBuffer.writeInt(0);
    //        sendBuffer.writeShort(header.getStation());
    //        sendBuffer.writeBytes(header.getSource().getBytes(),0,10);
    //        sendBuffer.writeBytes(header.getDestination().getBytes(),0,10);
            sendBuffer.writeBytes(header.getStation().getBytes(),0,20);
            sendBuffer.writeBytes(header.getSource().getBytes(),0,20);
            sendBuffer.writeBytes(header.getDestination().getBytes(),0,20);
            sendBuffer.writeShort(header.getComponent());
            sendBuffer.writeByte(header.getType());
    
            if (msg.getBody() != null){
                sendBuffer.writeBytes(JsonHelper.toJson(msg.getBody()).getBytes());
    //            sendBuffer.writeBytes(JsonUtil.toJson(msg.getBody(), JsonSerialize.Inclusion.NON_NULL).getBytes(Charset.forName("UTF-8")));
            }
    
            sendBuffer.setInt(4,sendBuffer.readableBytes()-8);
        }
    }
  • 相关阅读:
    Python实用笔记 (24)面向对象高级编程——使用@property
    Python实用笔记 (23)面向对象高级编程——使用__slots__
    Python实用笔记 (22)面向对象编程——实例属性和类属性
    Python实用笔记 (21)面向对象编程——获取对象信息
    Python实用笔记 (20)面向对象编程——继承和多态
    Python实用笔记 (19)面向对象编程——访问限制
    Python实用笔记 (18)面向对象编程——类和实例
    Python实用笔记 (17)模块
    Python实用笔记 (16)函数式编程——偏函数
    Python实用笔记 (15)函数式编程——装饰器
  • 原文地址:https://www.cnblogs.com/honghong75042/p/13324104.html
Copyright © 2020-2023  润新知