Netty(一)核心概念

Netty介绍

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

Netty框架的组成

第一个Netty程序

服务器端

        /**
         * 服务端
         */
        public class Server {

            public static void main(String[] args) throws Exception {
                //1.创建两个线程组
                //用于处理服务器端接收客户端连接
                EventLoopGroup pGroup = new NioEventLoopGroup();
                //用于进行网络通信
                EventLoopGroup cGroup = new NioEventLoopGroup();

                try {
                    //2.创建辅助工具类,用于服务器通道的配置
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(pGroup, cGroup) //绑定两个线程组
                        .channel(NioServerSocketChannel.class) //指定NIO模式
                        .option(ChannelOption.SO_BACKLOG, 1024) //设置tcp缓冲区
                        .option(ChannelOption.SO_SNDBUF, 32*1024) //设置发送缓冲大小
                        .option(ChannelOption.SO_RCVBUF, 32*1024) //设置接收缓冲大小
                        .option(ChannelOption.SO_KEEPALIVE, true) //保持连接
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                //3.配置接收方法的处理
                                sc.pipeline().addLast(new ServerHandler());
                            }
                        });

                    //4.进行绑定,调用sync()方法会阻塞直到服务器完成绑定
                    ChannelFuture cf = b.bind(9000).sync();
                    //5.等待关闭
                    cf.channel().closeFuture().sync();
                } finally {
                    //6.释放资源
                    pGroup.close();
                    cGroup.close();
                }
            }
        }

服务器业务逻辑

        /**
         * 服务器处理方法
         */
        public class ServerHandler extends ChannelHandlerAdapter{

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                System.out.println("服务器通道激活...");
            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                ByteBuf buf = (ByteBuf) msg ;
                byte[] req = new byte[buf.readableBytes()];
                buf.readBytes(req);
                String body = new String(req,"utf-8");
                System.out.println("Server: " + body);
                String response = "返回给客户端的响应: " + body;
                ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                System.out.println("读取数据完毕");
                ctx.flush();
            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }


        }

Handler很简单,它的每个方法都可以被重写,它的所有的方法中只有channelRead方法是必须要重写的。
实现服务器步骤:
- 配置服务器功能,如线程、端口
- 实现服务器处理程序,它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么

客户端

        /**
         * 客户端
         */
        public class Client {

            public static void main(String[] args) throws Exception {
                EventLoopGroup group = new NioEventLoopGroup();
                try {
                    Bootstrap b = new Bootstrap();
                    b.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                sc.pipeline().addLast(new ClientHandler());
                            }
                        });

                    ChannelFuture cf = b.connect("127.0.0.1",9000).sync();
                    //发送信息
                    cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello server".getBytes()));

                    cf.channel().closeFuture().sync();
                } finally{
                    group.shutdownGracefully();
                }

            }
        }

客户端的业务逻辑

        /**
         * 客户端处理方法
         */
        public class ClientHandler extends ChannelHandlerAdapter{

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                try {
                    ByteBuf buf = (ByteBuf) msg;
                    byte[] req = new byte[buf.readableBytes()];
                    buf.readBytes(req);
                    String body = new String(req,"utf-8");
                    System.out.println("Client: " + body);
                    String response = "服务器端返回信息: " + body;
                } finally {
                    ReferenceCountUtil.release(msg);
                }

            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

            }


        }
  • channelActive():客户端连接服务器后被调用
  • channelRead():从服务器接收到数据后调用
  • exceptionCaught():发生异常时被调用

Netty核心概念

  • Bootstrap or ServerBootstrap
  • EventLoop
  • EventLoopGroup
  • ChannelPipeline
  • Channel
  • Future or ChannelFuture
  • ChannelInitializer
  • ChannelHandler

ChannelInitializer

ChannelInitializer类用来配置Handlers。ChannelInitializer是通过ChannelPipeline来添加ChannelHandler的,如发送和接收消息,这些Handlers将确定发的是什么消息。ChannelInitializer自身也是一个ChannelHandler,在添加完其他的handlers之后会自动从ChannelPipeline中删除自己。

Future or ChannelFuture

Future注册一个监听,当操作成功或失败时会通知。ChannelFuture封装的是一个操作的相关信息,操作被执行时会立刻返回ChannelFuture。

Channel

EventLoop就是一个Channel执行实际工作的线程。EventLoop总是绑定一个单一的线程,在其生命周期内不会改变。当注册一个Channel后,Netty将这个Channel绑定到一个EventLoop,在Channel的生命周期内总是被绑定到一个EventLoop。

Bootstrap or ServerBootstrap

“引导”是Netty中配置程序的过程,当你需要连接客户端或服务器绑定指定端口时需要使用bootstrap。如前面所述,“引导”有两种类型,一种是用于客户端的Bootstrap(也适用于DatagramChannel),一种是用于服务端的ServerBootstrap。
差异:
- Bootstrap用来连接远程主机,有1个EventLoopGroup
- ServerBootstrap用来绑定本地端口,有2个EventLoopGroup

第一个差异很明显,“ServerBootstrap”监听在服务器监听一个端口轮询客户端的“Bootstrap”或DatagramChannel是否连接服务器。通常需要调用“Bootstrap”类的connect()方法,但是也可以先调用bind()再调用connect()进行连接,之后使用的Channel包含在bind()返回的ChannelFuture中。
第二个差别也许是最重要的。客户端bootstraps/applications使用一个单例EventLoopGroup,而ServerBootstrap使用2个EventLoopGroup(实际上使用的是相同的实例),它可能不是显而易见的,但是它是个好的方案。一个ServerBootstrap可以认为有2个channels组,第一组包含一个单例ServerChannel,代表持有一个绑定了本地端口的socket;第二组包含所有的Channel,代表服务器已接受了的连接。

EventLoop与EventLoopGroup

EventLoopGroup可以包含很多个EventLoop,每个Channel绑定一个EventLoop不会被改变,因为EventLoopGroup包含少量的EventLoop的Channels,很多Channel会共享同一个EventLoop。这意味着在一个Channel保持EventLoop繁忙会禁止其他Channel绑定到相同的EventLoop。我们可以理解为EventLoop是一个事件循环线程,而EventLoopGroup是一个事件循环集合。

ChannelPipeline与 ChannelHandler

ChannelPipeline的作用我们可以理解为用来管理ChannelHandler的一个容器,每个ChannelHandler处理各自的数据(例如入站数据只能由ChannelInboundHandler处理),处理完成后将转换的数据放到ChannelPipeline中交给下一个ChannelHandler继续处理,直到最后一个ChannelHandler处理完成。
ChannelHandler是一段执行业务逻辑处理数据的代码,它们来来往往的通过ChannelPipeline。

当一个ChannelHandler添加到ChannelPipeline中时获得一个ChannelHandlerContext。通常是安全的获得这个对象的引用,但是当一个数据报协议如UDP时这是不正确的,这个对象可以在之后用来获取底层通道,因为要用它来read/write消息,因此通道会保留。
Netty中发送消息有两种方法:直接写入通道或写入ChannelHandlerContext对象。
这两种方法的主要区别如下:
- 直接写入通道导致处理消息从ChannelPipeline的尾部开始
- 写入ChannelHandlerContext对象导致处理消息从ChannelPipeline的下一个handler开始

拆包、黏包

粘包、拆包的解决方案:
1. 将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑处理。
2. 在包尾部添加特殊字符进行分隔,分隔符类DellmiterBasedFrameDecoder(自定义分隔符)。
3. 消息定长,如果不够,空位补空格,FixedLengthFrameDecoder(定长) 。

消息定长

服务端

        /**
         * 服务端
         */
        public class Server {

            public static void main(String[] args) throws Exception {
                //1.创建两个线程
                EventLoopGroup pGroup = new NioEventLoopGroup();
                EventLoopGroup cGroup = new NioEventLoopGroup();

                try {
                    //2.创建服务器辅助类
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(pGroup, cGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        .option(ChannelOption.SO_SNDBUF, 32*1024)
                        .option(ChannelOption.SO_RCVBUF, 32*1024)
                        .option(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                //设置定长字符串接收
                                sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
                                //设置字符串形式的解码
                                sc.pipeline().addLast(new StringDecoder());
                                sc.pipeline().addLast(new ServerHandler());
                            }
                        });

                    //绑定连接
                    ChannelFuture cf = b.bind(9002).sync();
                    //等待服务器监听端口关闭
                    cf.channel().closeFuture().sync();
                } finally {
                    pGroup.shutdownGracefully();
                    cGroup.shutdownGracefully();
                }
            }
        }

客户端

        /**
         * 客户端
         */
        public class Client {

            public static void main(String[] args) throws Exception {
                EventLoopGroup group = new NioEventLoopGroup();
                try {
                    Bootstrap b = new Bootstrap();
                    b.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
                                sc.pipeline().addLast(new StringDecoder());
                                sc.pipeline().addLast(new ClientHandler());
                            }
                        });
                    //绑定端口
                    ChannelFuture cf = b.connect("127.0.0.1",9002).sync();
                    cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aaaaaabbbbbbb  ".getBytes()));
                    cf.channel().writeAndFlush(Unpooled.copiedBuffer("cccccc    ".getBytes()));

                    //等待客户端端口关闭
                    cf.channel().closeFuture().sync();
                } finally {
                    group.shutdownGracefully();
                }
            }
        }

服务端、客户端业务逻辑不变

尾部添加特殊字符

服务端

        /**
         * 服务端
         */
        public class Server {

            public static void main(String[] args) throws Exception {
                //1.创建两个线程
                //负责接收客户端的连接
                EventLoopGroup pGroup = new NioEventLoopGroup();
                //负责进行数据传输
                EventLoopGroup cGroup = new NioEventLoopGroup();

                try {
                    //2创建服务辅助类
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(pGroup, cGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        .option(ChannelOption.SO_SNDBUF, 32*1024)
                        .option(ChannelOption.SO_RCVBUF, 32*1024)
                        .option(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                //3.设置分隔符,解决拆包粘包问题
                                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
                                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
                                //设置字符串形式的解码
                                sc.pipeline().addLast(new StringDecoder());
                                sc.pipeline().addLast(new ServerHandler());
                            }
                        });

                    //4.绑定连接
                    ChannelFuture cf = b.bind(9001).sync();
                    //5.等待服务器监听端口关闭
                    cf.channel().closeFuture().sync();
                } finally {
                    //6.释放资源
                    pGroup.close();
                    cGroup.close();
                }
            }
        }

        /**
         * 服务器端处理方法
         */
        public class ServerHandler extends ChannelHandlerAdapter{

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                System.out.println("服务器端通道激活。。。");
            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                String request = (String) msg;
                System.out.println("Server: " + msg);
                String response = "服务器响应: " + msg + "$_";
                ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }


        }

客户端

        /**
         * 客户端
         */
        public class Client {

            public static void main(String[] args) throws Exception {
                EventLoopGroup group = new NioEventLoopGroup();

                try {
                    Bootstrap b = new Bootstrap();
                    b.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                //处理分隔符
                                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
                                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
                                sc.pipeline().addLast(new StringDecoder());
                                sc.pipeline().addLast(new ClientHandler());
                            }
                        });
                    //连接服务端
                    ChannelFuture cf = b.connect("127.0.0.1",9001).sync();
                    cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aa$_".getBytes()));
                    cf.channel().writeAndFlush(Unpooled.wrappedBuffer("bb$_".getBytes()));
                    cf.channel().writeAndFlush(Unpooled.wrappedBuffer("ccc$_".getBytes()));

                    //等待客户端端口关闭
                    cf.channel().closeFuture().sync();
                } finally {
                    group.shutdownGracefully();
                }
            }
        }

        /**
         * 客户端处理方法
         */
        public class ClientHandler extends ChannelHandlerAdapter{

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                System.out.println("客户端通道激活。。。");
            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                try {
                    String response = (String) msg;
                    System.out.println("Client: " + response);
                } finally {
                    ReferenceCountUtil.release(msg);
                }
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }


        }

编解码器

解码器负责将消息从字节或其他序列形式转成指定的消息对象,编码器则相反;解码器负责处理“入站”数据,编码器负责处理“出站”数据。

压缩解压

        /**
         * 压缩解压工具类
         */
        public class GzipUtils {

            /**
             * 压缩
             * @param data
             * @return
             * @throws Exception
             */
            public static byte[] gzip(byte[] data) throws Exception{
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] ret;
                try {
                    GZIPOutputStream gzip = new GZIPOutputStream(bos);
                    gzip.write(data);
                    gzip.finish();
                    gzip.close();
                    ret = bos.toByteArray();
                } finally {
                    if (bos != null) {
                        bos.close();
                    }
                }
                return ret;
            }

            /**
             * 解压
             * @param data
             * @return
             * @throws Exception
             */
            public static byte[] unzip(byte[] data) throws Exception{
                ByteArrayInputStream bis = new ByteArrayInputStream(data);
                GZIPInputStream gzip = new GZIPInputStream(bis);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] ret;
                try {
                    byte[] buf = new byte[1024];
                    int num = -1;
                    while ((num = gzip.read(buf, 0, buf.length)) != -1) {
                        bos.write(buf,0,num);
                    }
                    ret = bos.toByteArray();
                    bos.flush();
                } finally {
                    if (gzip != null) {
                        gzip.close();
                    }
                    if (bis != null) {
                        bis.close();
                    }
                    if (bos != null) {
                        bos.close();
                    }
                }
                return ret;
            }
        }

Marshalling

Jboss Marshalling 是一个java对象序列化包,对jdk默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口兼容,同时增加了一些可调的参数和附加特性。

        /**
         * Marshalling工厂
         */
        public class MarshallingCodecFactory {

            /**
             * 创建Jboss Marshalling解码器MarshallingDecoder
             * @return
             */
            public static MarshallingDecoder buildMarshallingDecoder(){
                //通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
                final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
                //创建了MarshallingConfiguration对象,配置了版本号为5 
                final MarshallingConfiguration configuration = new MarshallingConfiguration();
                configuration.setVersion(5);
                //根据marshallerFactory和configuration创建provider
                UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
                //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
                MarshallingDecoder decoder = new MarshallingDecoder(provider,1024 * 1024 * 1);
                return decoder;
            }

            /**
             * 创建Jboss MarshallingEncoder编码器
             * @return
             */
            public static MarshallingEncoder buildMarshallingEncoder(){
                final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
                final MarshallingConfiguration configuration = new MarshallingConfiguration();
                configuration.setVersion(5);
                MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
                //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
                MarshallingEncoder encoder = new MarshallingEncoder(provider);
                return encoder;
            }
        }

JavaBean

        /**
         * 请求消息
         */
        public class ReqData implements Serializable {

            private static final long serialVersionUID = 1L;

            private String id;
            private String name;
            private String requestMessage;
            private byte[] attachment;

            public String getId() {
                return id;
            }
            public void setId(String id) {
                this.id = id;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public String getRequestMessage() {
                return requestMessage;
            }
            public void setRequestMessage(String requestMessage) {
                this.requestMessage = requestMessage;
            }
            public byte[] getAttachment() {
                return attachment;
            }
            public void setAttachment(byte[] attachment) {
                this.attachment = attachment;
            }

        }

        /**
         * 响应信息
         */
        public class RespData implements Serializable{

            private static final long serialVersionUID = 1L;

            private String id;
            private String name;
            private String responseMessage;
            public String getId() {
                return id;
            }
            public void setId(String id) {
                this.id = id;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public String getResponseMessage() {
                return responseMessage;
            }
            public void setResponseMessage(String responseMessage) {
                this.responseMessage = responseMessage;
            }

        }

服务端

        /**
         * 服务端
         */
        public class Server {

            public static void main(String[] args) throws Exception {
                EventLoopGroup pGroup = new NioEventLoopGroup();
                EventLoopGroup cGroup = new NioEventLoopGroup();

                try {
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(pGroup,cGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        .handler(new LoggingHandler(LogLevel.INFO)) //设置日志
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder());
                                sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder());
                                sc.pipeline().addLast(new ServerHandler());
                            }
                        });

                    //绑定端口
                    ChannelFuture cf = b.bind(9003).sync();
                    cf.channel().closeFuture().sync();
                } finally {
                    pGroup.close();
                    cGroup.close();
                }

            }
        }

        /**
         * 服务端处理方法
         */
        public class ServerHandler extends ChannelHandlerAdapter{

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                ReqData req= (ReqData) msg;
                System.out.println("Server: " + req.getId() + "," + req.getName() + "," + req.getRequestMessage());
                byte[] attachment = GzipUtils.unzip(req.getAttachment());

                String path = System.getProperty("user.dir") + File.separatorChar + "receive" + File.separatorChar + "001.jpg";
                FileOutputStream fos = new FileOutputStream(path);
                fos.write(attachment);
                fos.close();

                RespData resp = new RespData();
                resp.setId(req.getId());
                resp.setName("resp" + req.getId());
                resp.setResponseMessage("响应内容: " + req.getId());
                ctx.writeAndFlush(resp);
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }

        }

客户端

        /**
         * 客户端
         */
        public class Client {

            public static void main(String[] args) throws Exception {
                EventLoopGroup group = new NioEventLoopGroup();
                Bootstrap b = new Bootstrap();

                try {
                    b.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder());
                                sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder());
                                sc.pipeline().addLast(new ClientHandler());
                            }
                        });

                    ChannelFuture cf = b.connect("127.0.0.1",9003).sync();

                    for (int i = 0; i < 6; i++) {
                        ReqData req = new ReqData();
                        req.setId(""+i);
                        req.setName("pro"+i);
                        req.setRequestMessage("数据信息" + i);
                        String path = System.getProperty("user.dir") + File.separatorChar
                                + "sources" + File.separatorChar + "001.jpg";
                        File file = new File(path);
                        FileInputStream in = new FileInputStream(file);
                        byte[] data = new byte[in.available()];
                        in.read(data);
                        in.close();
                        req.setAttachment(GzipUtils.gzip(data));
                        cf.channel().writeAndFlush(req);
                    }

                    cf.channel().closeFuture().sync();
                } finally {
                    group.shutdownGracefully();
                }
            }
        }

        /**
         * 客户端处理方法
         */
        public class ClientHandler extends ChannelHandlerAdapter{

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                try {
                    RespData resp = (RespData) msg;
                    System.out.println("Client:" + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage());
                } finally {
                    ReferenceCountUtil.release(msg);
                }
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }

        }

应用场景

断开后重新连接

服务端,加上一行设置超时时间的代码,其它内容不变。

        b.group(pGroup,cGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .handler(new LoggingHandler(LogLevel.INFO)) //设置日志
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder());
                        sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder());
                        //设置连接超时时间
                        sc.pipeline().addLast(new ReadTimeoutHandler(3));
                        sc.pipeline().addLast(new ServerHandler());
                    }
                });

客户端

        /**
         * 客户端
         */
        public class Client {

            private static class SingletonHolder {
                static final Client instance = new Client();
            }

            public static Client getInstance(){
                return SingletonHolder.instance;
            }

            private EventLoopGroup group;
            private Bootstrap b;
            private ChannelFuture cf ;

            private Client(){
                    group = new NioEventLoopGroup();
                    b = new Bootstrap();
                    b.group(group)
                     .channel(NioSocketChannel.class)
                     .handler(new LoggingHandler(LogLevel.INFO))
                     .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder());
                                sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder());
                                //超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
                                sc.pipeline().addLast(new ReadTimeoutHandler(3)); 
                                sc.pipeline().addLast(new ClientHandler());
                            }
                    });
            }

            public void connect(){
                try {
                    this.cf = b.connect("127.0.0.1", 9003).sync();
                    System.out.println("远程服务器已经连接, 可以进行数据交换..");                
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            public ChannelFuture getChannelFuture(){

                if(this.cf == null){
                    this.connect();
                }
                if(!this.cf.channel().isActive()){
                    this.connect();
                }

                return this.cf;
            }

            public static void main(String[] args) throws Exception{
                final Client c = Client.getInstance();
                //c.connect();

                ChannelFuture cf = c.getChannelFuture();
                for(int i = 1; i <= 3; i++ ){
                    ReqData request = new ReqData();
                    request.setId("" + i);
                    request.setName("pro" + i);
                    request.setRequestMessage("数据信息" + i);
                    cf.channel().writeAndFlush(request);
                    TimeUnit.SECONDS.sleep(4);
                }

                cf.channel().closeFuture().sync();


                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("进入子线程...");
                            ChannelFuture cf = c.getChannelFuture();
                            System.out.println(cf.channel().isActive());
                            System.out.println(cf.channel().isOpen());

                            //再次发送数据
                            ReqData request = new ReqData();
                            request.setId("" + 4);
                            request.setName("pro" + 4);
                            request.setRequestMessage("数据信息" + 4);
                            cf.channel().writeAndFlush(request);                    
                            cf.channel().closeFuture().sync();
                            System.out.println("子线程结束.");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

                System.out.println("断开连接,主线程结束..");

            }

        }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值