• Netty入门之客户端与服务端通信(二)


    Netty入门之客户端与服务端通信(二)

    一.简介

      在上一篇博文中笔者写了关于Netty入门级的Hello World程序。书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码吧。

    二.客户端与服务端的通信

    2.1 服务端启动程序

    public class MyServer {
        public static void main(String[] args) throws InterruptedException {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            
            try{
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                
                serverBootstrap.group(bossGroup, workerGroup)
                               .channel(NioServerSocketChannel.class)
                               .childHandler(new MyInitializer());
                
                ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
                channelFuture.channel().closeFuture().sync();
            }finally{
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }

    2.2 服务端通道初始化程序

    public class MyInitializer extends ChannelInitializer<SocketChannel>{
    
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline =  ch.pipeline();
            /**
             * LengthFieldBasedFrameDecoder: 基于长度属性的帧解码器。
             * 客户端传递过来的数据格式为:
             *  BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)  
             *    +--------+----------------+      +--------+----------------+  
             *    | Length | Actual Content |----->| Length | Actual Content |  
             *    | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |  
             *    +--------+----------------+      +--------+----------------+  
             * 5个参数依次为:1.(maxFrameLength)每帧数据的最大长度.
             *              2.(lengthFieldOffset)length属性在帧中的偏移量。
             *           3.(lengthFieldLength)length属性的长度,需要与客户端 LengthFieldPrepender设置的长度一致,
             *               值的取值只能为1, 2, 3, 4, 8
             *           4.(lengthAdjustment)长度调节值, 当信息长度包含长度时候,用于修正信息的长度。
             *           5.(initialBytesToStrip)在获取真实的内容的时候,需要忽略的长度(通常就是length的长度)。
             *           
             * 参考: http://blog.csdn.net/educast/article/details/47706599
             */
            pipeline.addLast("lengthFieldBasedFrameDecoder", 
                                new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2));
            /**
             * LengthFieldPrepender: length属性在帧中的长度。只能为1,2,3,4,8。
             * 该值与对应的客户端(或者服务端)在解码时候使用LengthFieldBasedFrameDecoder中所指定的lengthFieldLength
             * 的值要保持一致。
             */
            pipeline.addLast("lengthFieldPrepender", new LengthFieldPrepender(3));
            //StringDecoder字符串的解码器, 主要用于处理编码格式
            pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
            //StringDecoder字符串的编码器,主要用于指定字符串的编码格式
            pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
            
            pipeline.addLast(new MyHandler()); //自定义的Handler
        }
    }

    2.3 自定义Handler

    public class MyHandler extends SimpleChannelInboundHandler<String>{
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(ctx.channel().remoteAddress() + ":" + msg);
            ctx.channel().writeAndFlush("from server: 草泥马");
        }
        
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            System.out.println(System.currentTimeMillis() + "********");
            System.out.println("server handler added**********");
        }
        
        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println(System.currentTimeMillis() + "********");
            System.out.println("server channel register****");
        }
        
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(System.currentTimeMillis() + "********");
            System.out.println("server channel actieve****");
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

    2.4客户端启动程序

    public class MyClient {
        public static void main(String[] args) throws InterruptedException {
            EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
            
            try{
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ClientInitializer());
                
                ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8899).sync();
                channelFuture.channel().closeFuture().sync();
            }finally{
                eventLoopGroup.shutdownGracefully();
            }
        }
    }

    2.5客户端通道初始化

    public class ClientInitializer extends ChannelInitializer<SocketChannel>{
    
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline =  ch.pipeline();
            pipeline.addLast("lengthFieldBasedFrameDecoder", 
                                new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2));
            pipeline.addLast("lengthFieldPrepender", new LengthFieldPrepender(3));
            pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
            pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
            
            pipeline.addLast(new MyClientHandler());
        }
    }

    2.5客户端自定义Handler

    public class MyClientHandler extends SimpleChannelInboundHandler<String>{
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(ctx.channel().remoteAddress());
            System.out.println(msg);
            ctx.channel().writeAndFlush("to Server: 草泥马");
        }
        
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(System.currentTimeMillis() + "...........");
            ctx.channel().writeAndFlush("来自于客户端的问候!");
            System.out.println("client channel Active...");
        }
        
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            System.out.println(System.currentTimeMillis() + "...........");
            System.out.println("client hanlder added...");
        }
        
        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println(System.currentTimeMillis() + "...........");
            System.out.println("client channel register...");
        }
        
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("client channel inactive...");
        }
        
        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println("client channel unregister...");
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

    三. 运行测试

    运行服务端启动代码,然后在运行客户端启动代码,就可以看见千万只"草泥马"在崩腾。

  • 相关阅读:
    JavaScript寄生组合式继承分析
    常用的css命名规则:
    jshint配置(js检查)
    当页面关闭或刷新时提示用户
    Ionic 开发环境搭建
    VS Code前端开发利器-常用快捷键
    Uploadify 上传插件引起Chrome崩溃解决方法
    “全栈工程师”的尴尬
    redis集群升级,数据迁移及校验
    K-means
  • 原文地址:https://www.cnblogs.com/miller-zou/p/6995440.html
Copyright © 2020-2023  润新知