zoukankan      html  css  js  c++  java
  • Netty实现客户端和服务端通信简单例子

    Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象。

    在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。

    Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的并发模型。

    Netty提供了内置的常用编解码器,包括行编解码器[一行一个请求],前缀长度编解码器[前N个字节定义请求的字节长度],可重放解码器[记录半包消息的状态],HTTP编解码器,WebSocket消息编解码器等等

    Netty提供了一些列生命周期回调接口,当一个完整的请求到达时,当一个连接关闭时,当一个连接建立时,用户都会收到回调事件,然后进行逻辑处理。

    Netty可以同时管理多个端口,可以使用NIO客户端模型,这些对于RPC服务是很有必要的。

    Netty除了可以处理TCP Socket之外,还可以处理UDP Socket。

    在消息读写过程中,需要大量使用ByteBuffer,Netty对ByteBuffer在性能和使用的便捷性上都进行了优化和抽象。

    代码:

    服务端:

    package com.kinson.netty.server;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    
    /**
     * descripiton:服务端
     *
     * @author: www.iknowba.cn
     * @date: 2018/3/23
     * @time: 15:37
     * @modifier:
     * @since:
     */
    public class NettyServer {
        /**
         * 端口
         */
        private int port;
    
        public NettyServer(int port) {
            this.port = port;
        }
    
        public void run() {
            //EventLoopGroup是用来处理IO操作的多线程事件循环器
            //负责接收客户端连接线程
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            //负责处理客户端i/o事件、task任务、监听任务组
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            //启动 NIO 服务的辅助启动类
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup);
            //配置 Channel
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new ServerIniterHandler());
            //BACKLOG用于构造服务端套接字ServerSocket对象,
            // 标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            //是否启用心跳保活机制
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            try {
                //绑定服务端口监听
                Channel channel = bootstrap.bind(port).sync().channel();
                System.out.println("server run in port " + port);
                //服务器关闭监听
                /*channel.closeFuture().sync()实际是如何工作:
                channel.closeFuture()不做任何操作,只是简单的返回channel对象中的closeFuture对象,对于每个Channel对象,都会有唯一的一个CloseFuture,用来表示关闭的Future,
                所有执行channel.closeFuture().sync()就是执行的CloseFuturn的sync方法,从上面的解释可以知道,这步是会将当前线程阻塞在CloseFuture上*/
                channel.closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //关闭事件流组
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) {
            new NettyServer(8899).run();
        }
    }

    服务端业务逻辑处理:

    package com.kinson.netty.server;
    
    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;
    
    /**
     * descripiton: 服务器的处理逻辑
     *
     * @author: www.iknowba.cn
     * @date: 2018/3/23
     * @time: 15:50
     * @modifier:
     * @since:
     */
    public class ServerHandler extends SimpleChannelInboundHandler<String> {
    
        /**
         * 所有的活动用户
         */
        public static final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
        /**
         * 读取消息通道
         *
         * @param context
         * @param s
         * @throws Exception
         */
        @Override
        protected void channelRead0(ChannelHandlerContext context, String s)
                throws Exception {
            Channel channel = context.channel();
            //当有用户发送消息的时候,对其他的用户发送消息
            for (Channel ch : group) {
                if (ch == channel) {
                    ch.writeAndFlush("[you]: " + s + "
    ");
                } else {
                    ch.writeAndFlush("[" + channel.remoteAddress() + "]: " + s + "
    ");
                }
            }
            System.out.println("[" + channel.remoteAddress() + "]: " + s + "
    ");
        }
    
        /**
         * 处理新加的消息通道
         *
         * @param ctx
         * @throws Exception
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            for (Channel ch : group) {
                if (ch == channel) {
                    ch.writeAndFlush("[" + channel.remoteAddress() + "] coming");
                }
            }
            group.add(channel);
        }
    
        /**
         * 处理退出消息通道
         *
         * @param ctx
         * @throws Exception
         */
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            for (Channel ch : group) {
                if (ch == channel) {
                    ch.writeAndFlush("[" + channel.remoteAddress() + "] leaving");
                }
            }
            group.remove(channel);
        }
    
        /**
         * 在建立连接时发送消息
         *
         * @param ctx
         * @throws Exception
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            boolean active = channel.isActive();
            if (active) {
                System.out.println("[" + channel.remoteAddress() + "] is online");
            } else {
                System.out.println("[" + channel.remoteAddress() + "] is offline");
            }
            ctx.writeAndFlush("[server]: welcome");
        }
    
        /**
         * 退出时发送消息
         *
         * @param ctx
         * @throws Exception
         */
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            if (!channel.isActive()) {
                System.out.println("[" + channel.remoteAddress() + "] is offline");
            } else {
                System.out.println("[" + channel.remoteAddress() + "] is online");
            }
        }
    
        /**
         * 异常捕获
         *
         * @param ctx
         * @param e
         * @throws Exception
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
            Channel channel = ctx.channel();
            System.out.println("[" + channel.remoteAddress() + "] leave the room");
            ctx.close().sync();
        }
    
    }

    服务端处理器注册:

    package com.kinson.netty.server;
    
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    
    /**
     * descripiton: 服务器初始化
     *
     * @author: www.iknowba.cn
     * @date: 2018/3/23
     * @time: 15:46
     * @modifier:
     * @since:
     */
    public class ServerIniterHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            //管道注册handler
            ChannelPipeline pipeline = socketChannel.pipeline();
            //编码通道处理
            pipeline.addLast("decode", new StringDecoder());
            //转码通道处理
            pipeline.addLast("encode", new StringEncoder());
            //聊天服务通道处理
            pipeline.addLast("chat", new ServerHandler());
        }
    }

    客户端:

    package com.kinson.netty.client;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import org.apache.commons.lang3.StringUtils;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    /**
     * descripiton: 客户端
     *
     * @author: www.iknowba.cn
     * @date: 2018/3/23
     * @time: 16:40
     * @modifier:
     * @since:
     */
    public class NettyClient {
    
        private String ip;
    
        private int port;
    
        private boolean stop = false;
    
        public NettyClient(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
    
        public void run() throws IOException {
            //设置一个多线程循环器
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            //启动附注类
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(workerGroup);
            //指定所使用的NIO传输channel
            bootstrap.channel(NioSocketChannel.class);
            //指定客户端初始化处理
            bootstrap.handler(new ClientIniterHandler());
            try {
                //连接服务
                Channel channel = bootstrap.connect(ip, port).sync().channel();
                while (true) {
                    //向服务端发送内容
                    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                    String content = reader.readLine();
                    if (StringUtils.isNotEmpty(content)) {
                        if (StringUtils.equalsIgnoreCase(content, "q")) {
                            System.exit(1);
                        }
                        channel.writeAndFlush(content);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.exit(1);
            } finally {
                workerGroup.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            new NettyClient("127.0.0.1", 8899).run();
        }
    }

    客户端逻辑处理:

    package com.kinson.netty.client;
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    /**
     * descripiton: 客户端逻辑处理
     *
     * @author: www.iknowba.cn
     * @date: 2018/3/23
     * @time: 16:50
     * @modifier:
     * @since:
     */
    public class ClientHandler extends SimpleChannelInboundHandler<String> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
            //打印服务端的发送数据
            System.out.println(s);
        }
    }

    客户端处理器注册:

    package com.kinson.netty.client;
    
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.handler.codec.http.HttpClientCodec;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    /**
     * descripiton: 客户端处理初始化
     *
     * @author: www.iknowba.cn
     * @date: 2018/3/23
     * @time: 16:55
     * @modifier:
     * @since:
     */
    public class ClientIniterHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            //注册管道
            ChannelPipeline pipeline = socketChannel.pipeline();
            pipeline.addLast("decoder", new StringDecoder());
            pipeline.addLast("encoder", new StringEncoder());
            pipeline.addLast("http", new HttpClientCodec());
            pipeline.addLast("chat", new ClientHandler());
        }
    }

    测试时先启动服务端,再启动客户端。。。

    完整代码下载:netty-demo

  • 相关阅读:
    从零开始学android开发-四大组件之一 Activity
    从零开始学android开发-详细谈谈intent的startActivityForResult()方法
    从零开始学android-一行两个按钮居中 布局
    nginx上用fastcgi配置python环境
    服务器程序源代码分析之三:gunicorn
    全面解读python web 程序的9种部署方式
    python检测文件是否更新
    nginx安装
    solr教程,值得刚接触搜索开发人员一看
    Shell标准输出、标准错误 >/dev/null 2>&1
  • 原文地址:https://www.cnblogs.com/kingsonfu/p/Java.html
Copyright © 2011-2022 走看看