Netty(一)核心概念

来源:互联网 发布:值机软件 编辑:程序博客网 时间:2024/05/20 02:29

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("断开连接,主线程结束..");            }        }
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 一年级学生记不住生字怎么办 一年级小孩记不住生字怎么办 配镜度数高了怎么办 宝宝两岁半不肯坐马桶拉臭臭怎么办 儿子字写得不好 怎么办 小孩不听话不爱读书和写字怎么办 两岁宝宝不愿意穿衣服怎么办 做题粗心不认真怎么办 5岁宝宝不会写字怎么办 四岁宝宝不会写字怎么办 4岁宝宝不写字怎么办 四岁宝宝不写字怎么办 孩子学习粗心计算能力差怎么办 一年级的小朋友不爱看书怎么办 马上要生了害怕怎么办 孩子做题不爱读题怎么办 孩子作业写的慢怎么办 孩子学习不好怎么办我们有绝招 英语不会做题怎么办呢? 小学二年级孩子厌学怎么办 狗狗拉肚子不吃东西怎么办 小孩做作业时容易发呆怎么办 一上高速就犯困怎么办 孩子初中数学学不好怎么办 高三注意力不集中怎么办 考砸了家长打我怎么办? 高三学生困疲劳怎么办 高三晚上很困怎么办 孩子上高三压力大不想上学怎么办 高三的孩子压力大怎么办 高三复读压力大怎么办 孩子一年级做数学粗心怎么办 一年级的孩子数学总粗心怎么办 天生手脚笨的人怎么办 高三的孩子厌学怎么办 二年级小孩学习笨怎么办 孩子高二不想上怎么办 高三孩子玩手机怎么办 孩子考试粗心丢题怎么办 工作中总出错是怎么办 工作上做错事了怎么办