Netty(二)TCP粘包、拆包和UDP通信

来源:互联网 发布:安卓手机系统优化软件 编辑:程序博客网 时间:2024/06/02 05:16

Netty基本实现

Netty实现通信的步骤:
1. 创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写。
2. 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等。
3. 创建一个实际处理的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、以及实际处理数据的接口。
4. 绑定端口,执行同步阻塞方法等待服务器端启动即可。

public class ServerHandler extends ChannelHandlerAdapter{    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//      ((ByteBuf) msg).release();        //do something        ByteBuf buf = (ByteBuf) msg;        byte[] bytes = new byte[buf.readableBytes()];//readableBytes()可读数据        buf.readBytes(bytes);        String request = new String(bytes,"utf-8");        System.out.println("Server : " + request);        //写给客户端        String response = "服务器端响应数据:888";        ctx.write(Unpooled.copiedBuffer(response.getBytes())) //write方法会帮助释放消息,可以不用finally释放消息        .addListener(ChannelFutureListener.CLOSE); //监听,把数据发送后主动断开与客户端的连接        ctx.flush();    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }}public class Server {    public static void main(String[] args) throws InterruptedException {        //1.第一个线程组用于接收client端连接        EventLoopGroup bossGroup = new NioEventLoopGroup();        //2.第二个线程组用于实际业务处理        EventLoopGroup workGroup = new NioEventLoopGroup();        //3.创建一个辅助类BootStrap,就是对server进行一系列配置        ServerBootstrap b = new ServerBootstrap();        b.group(bossGroup, workGroup)        .channel(NioServerSocketChannel.class)        .childHandler(new ChannelInitializer<SocketChannel>() {            @Override            protected void initChannel(SocketChannel channel) throws Exception {                channel.pipeline().addLast(new ServerHandler());            }        })        .option(ChannelOption.SO_BACKLOG, 128)        .childOption(ChannelOption.SO_KEEPALIVE, true);        ChannelFuture future = b.bind(6666).sync();        //阻塞        future.channel().closeFuture().sync();        bossGroup.shutdownGracefully();        workGroup.shutdownGracefully();    }}public class ClientHandler extends ChannelHandlerAdapter{    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {    try {            ByteBuf buf = (ByteBuf) msg;            byte[] bytes = new byte[buf.readableBytes()];//readableBytes()可读数据            buf.readBytes(bytes);            String request = new String(bytes, "utf-8");            System.out.println("Client : " + request);        } finally {            ReferenceCountUtil.release(msg);//释放消息        }    }    @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();        b.group(group)        .channel(NioSocketChannel.class)        .handler(new ChannelInitializer<SocketChannel>() {            protected void initChannel(SocketChannel sc) throws Exception {                sc.pipeline().addLast(new ClientHandler());            }        });        ChannelFuture cf = b.connect("127.0.0.1",6666).sync();        //buf//      cf.channel().write(Unpooled.copiedBuffer("777".getBytes()));//      cf.channel().flush();        cf.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));        //阻塞        cf.channel().closeFuture().sync();        group.shutdownGracefully();    }}

TCP粘包、拆包问题

TCP是一个“流”协议,所谓流就是没有界限的遗传数据。TCP底层并不了解上层的业务数据具体的含义,它会跟进TCP缓冲区的实际情况进行包的划分,在业务上,我们一个完整的包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的TCP粘包、拆包问题。
TCP粘包、拆包问题的产生原因:
1. 应用程序write写入的字节大小大于套接口发送缓冲区的大小
2. 进行MSS大小的TCP分段
3. 以太网帧的payload大于MTU进行IP分片

粘包、拆包主流解决方案:
1. 消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格;
2. 在包尾部增加特殊字符进行分割,例如加回车等;
3. 将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理

Netty解决粘包、拆包问题:
1. 分隔符类 DElimiterBasedFrameDecoder(自定义分隔符)
2. FixedLengthFrameDecoder(定长)

public class ServerHandler1 extends ChannelHandlerAdapter{    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        System.out.println("server channel active...");    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        String request = (String) msg;        System.out.println("Server: " + request );        String response = "服务器端响应:" + msg + "$_";        ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }}public class Server1 {    public static void main(String[] args) throws InterruptedException {        //1.创建线程,接收客户端        EventLoopGroup pGroup = new NioEventLoopGroup();        //2.创建线程,用于处理数据传输        EventLoopGroup wGroup = new NioEventLoopGroup();        //3.创建服务器辅助类        ServerBootstrap b = new ServerBootstrap();        b.group(pGroup, wGroup)            .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 {                    //设置特殊分隔符                    ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());                    sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));                    //设置字符串形式的编码                    sc.pipeline().addLast(new StringDecoder());                    sc.pipeline().addLast(new ServerHandler1());                }            });        //4.绑定连接        ChannelFuture cf = b.bind(6666).sync();        //等待服务器监听端口关闭        cf.channel().closeFuture().sync();        pGroup.shutdownGracefully();        wGroup.shutdownGracefully();    }}public class ClientHandler1 extends ChannelHandlerAdapter{    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        System.out.println("client active..");    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        try {            String response = (String) msg;            System.out.println("client: " + msg);        } finally {            ReferenceCountUtil.release(msg);//释放消息        }    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        ctx.close();    }}public class Client1 {    public static void main(String[] args) throws InterruptedException {        //1.创建线程组        EventLoopGroup group = new NioEventLoopGroup();         //2.创建客户端辅助类        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 ClientHandler1());            }        });        //3.连接服务器        ChannelFuture cf = b.connect("127.0.0.1", 6666).sync();        cf.channel().writeAndFlush(Unpooled.wrappedBuffer("bbbb$_".getBytes()));        cf.channel().writeAndFlush(Unpooled.wrappedBuffer("ccc$_".getBytes()));        //等待服务器监听端口关闭        cf.channel().closeFuture().sync();        group.shutdownGracefully();    }}
public class ServerHandler1 extends ChannelHandlerAdapter{    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        System.out.println("server channel active..");    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        String request = (String) msg;        System.out.println("server : " + request);        String response = request;        ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        ctx.close();    }}public class Server1 {    public static void main(String[] args) throws InterruptedException {        //1.创建线程,管理连接        EventLoopGroup pGroup = new NioEventLoopGroup();        //2.创建线程,处理数据传输        EventLoopGroup wGroup = new NioEventLoopGroup();        //3.创建服务端辅助类        ServerBootstrap b = new ServerBootstrap();        b.group(pGroup, wGroup)            .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 ServerHandler1());                }            });        //4.绑定端口        ChannelFuture cf = b.bind(6666).sync();        //等待服务器监听端口关闭        cf.channel().closeFuture().sync();        pGroup.shutdownGracefully();        wGroup.shutdownGracefully();    }}public class ClientHandler1 extends ChannelHandlerAdapter{    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        System.out.println("client channel active..");    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        try {            String response = (String) msg;            System.out.println("client: " + msg);        } finally {            ReferenceCountUtil.release(msg);//释放消息        }    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        ctx.close();    }}public class Client1 {    public static void main(String[] args) throws InterruptedException {        //1.创建线程        EventLoopGroup group = new NioEventLoopGroup();        //2.创建辅助类        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 ClientHandler1());            }        });        //3.连接服务端        ChannelFuture cf = b.connect("127.0.0.1", 6666).sync();        cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aaaabbbb".getBytes()));        cf.channel().writeAndFlush(Unpooled.copiedBuffer("ccccccc".getBytes()));        //等待客户端关闭        cf.channel().closeFuture().sync();        group.shutdownGracefully();    }}

Netty编解码

可以使用java进行对象序列化,netty去传输,但是java序列化的缺点多,比如java序列化没法跨语言、序列化后码流太大、序列化性能太低等等。
主流的编解码框架:
JBoss的Marshalling包
google的Protobuf
基于Protobuf的Kyro
MessagePack框架

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

/** * 解压缩工具类 */public class GzipUtil {    private final static Logger logger = Logger.getLogger(GzipUtil.class);    public static byte[] gzip(byte[] data) throws IOException {        ByteArrayOutputStream baos = new ByteArrayOutputStream();        GZIPOutputStream gzipos = null;        byte[] result = null ;        try {            gzipos = new GZIPOutputStream(baos);            gzipos.write(data);            gzipos.finish();            result = baos.toByteArray();        } catch (IOException e) {            logger.error("压缩文件失败!",e);            throw new IOException();        } finally {            if (gzipos != null) {                try {                    gzipos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (baos != null) {                try {                    baos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            gzipos = null;            baos = null;        }        return result;    }    public static byte[] ungzip(byte[] data) throws IOException {        ByteArrayInputStream bais = new ByteArrayInputStream(data);        GZIPInputStream gzipis = null;        ByteArrayOutputStream baos = null;        byte[] result = null;        try {            gzipis = new GZIPInputStream(bais);            byte[] buf = new byte[1024];            int num = -1;            baos = new ByteArrayOutputStream();            while ((num = gzipis.read(buf, 0, buf.length)) != -1) {                baos.write(buf, 0, num);            }            result = baos.toByteArray();            baos.flush();        } catch (IOException e) {            logger.error("解压文件失败!",e);            throw new IOException();        } finally {            if (gzipis != null) {                try {                    gzipis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (bais != null) {                try {                    bais.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (baos != null) {                try {                    baos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            gzipis = null;            bais = null;            baos = null;        }        return result;    }    //测试    public static void main(String[] args) throws Exception {        //文件路径        String readPath = System.getProperty("user.dir") + File.separator + "sources" + File.separator + "001.jpg";        File file = new File(readPath);        FileInputStream fis = new FileInputStream(file);        byte[] data = new byte[fis.available()];        fis.read(data);        fis.close();        System.out.println("文件原始大小:" + data.length);        //文件压缩        byte[] result = GzipUtil.gzip(data);        System.out.println("文件压缩后大小:" + result.length);        //文件解压        byte[] ungzipFile = GzipUtil.ungzip(result);        System.out.println("文件解压后大小:" + ungzipFile.length);        //写出文件        String writePath = System.getProperty("user.dir") + File.separator + "receive" + File.separator + "001.jpg";        FileOutputStream fos = new FileOutputStream(writePath);        fos.write(ungzipFile);        fos.close();    }}/** * Marshalling工厂 */public final class MarshallingCodeCFactory {    /**     * 创建Jboss Marshalling解码器MarshallingDecoder     * @return MarshallingDecoder     */    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 Marshalling编码器MarshallingEncoder     * @return MarshallingEncoder     */    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;    }}

Netty

public class Request 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 Response 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 ServerHandlerExt extends ChannelHandlerAdapter{    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        Req req = (Req) msg;        System.out.println("Server:" + req.getId() + "," + req.getName() + "," + req.getRequestMessage());        byte[] attachment = GzipUtil.gzip(req.getAttachment());        String path = System.getProperty("user.dir") + File.separator + "receive" + File.separator + "001.jpg";        FileOutputStream fos = new FileOutputStream(path);        fos.write(attachment);        fos.close();        Resp resp = new Resp();        resp.setId(req.getId());        resp.setName("resp" + req.getId());        resp.setResponseMessage("响应内容:" + req.getId());        ctx.writeAndFlush(req);//.addListener(ChannelFutureListener.CLOSE)    }}public class ServerExt {    public static void main(String[] args) throws InterruptedException {        //创建两个现场        EventLoopGroup pGroup = new NioEventLoopGroup();        EventLoopGroup wGroup = new NioEventLoopGroup();        //创建辅助类        ServerBootstrap b = new ServerBootstrap();        b.group(pGroup, wGroup)            .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(MarshallingFactory.buildMarshallingEncoder());                    sc.pipeline().addLast(MarshallingFactory.buildMarshallingDecoder());                    sc.pipeline().addLast(new ServerHandlerExt());                }            });        b.bind(6666).channel().closeFuture().sync();        //关闭连接        pGroup.shutdownGracefully();        wGroup.shutdownGracefully();    }}public class ClientHandlerExt extends ChannelHandlerAdapter{    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        try {            Resp resp = (Resp) msg;            System.out.println("Client:" + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage());        } finally{            ReferenceCountUtil.release(msg);//释放消息        }    }}public class ClientExt {    public static void main(String[] args) throws Exception {        //创建线程        EventLoopGroup group = new NioEventLoopGroup();        //创建工具类        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(MarshallingFactory.buildMarshallingEncoder());                    sc.pipeline().addLast(MarshallingFactory.buildMarshallingDecoder());                    sc.pipeline().addLast(new ClientHandlerExt());                }            });        ChannelFuture cf = b.connect("127.0.0.1", 6666).sync();        for (int i = 0; i < 5; i++) {            Req req = new Req();            req.setId("" + i);            req.setName("pro" + i);            req.setRequestMessage("数据信息" + i);            String path = System.getProperty("user.dir") + File.separator + "sources" + File.separator + "001.jpg";            File file = new File(path);            FileInputStream fis = new FileInputStream(file);            byte[] data = new byte[fis.available()];            fis.read(data);            fis.close();            req.setAttachment(GzipUtil.gzip(data));            cf.channel().writeAndFlush(req);        }        cf.channel().closeFuture().sync();        group.shutdownGracefully();    }}

Netty的UDP协议

UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。

public class ServerHandlerExt extends SimpleChannelInboundHandler<DatagramPacket>{     // 谚语列表    private static final String[] DICTIONARY = {         "只要功夫深,铁棒磨成针。",        "旧时王谢堂前燕,飞入寻常百姓家。",         "洛阳亲友如相问,一片冰心在玉壶。",        "一寸光阴一寸金,寸金难买寸光阴。",        "老骥伏枥,志在千里。烈士暮年,壮心不已!"    };    private String nextQuote(){        int quoteId = ThreadLocalRandom.current().nextInt(DICTIONARY.length);        return DICTIONARY[quoteId];    }    protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {        String req = packet.content().toString(CharsetUtil.UTF_8);        System.out.println(req);        if ("谚语字典查询?".equals(req)) {            ctx.writeAndFlush(                    new DatagramPacket(Unpooled.copiedBuffer("谚语查询结果:"+ nextQuote(),                    CharsetUtil.UTF_8),packet.sender()));        }    }}public class ServerExt {    public void run(int port) throws InterruptedException{        EventLoopGroup group = new NioEventLoopGroup();        try {            Bootstrap b = new Bootstrap();            b.group(group).channel(NioDatagramChannel.class)            .option(ChannelOption.SO_BROADCAST, true)            .handler(new ServerHandlerExt());            b.bind(port).sync().channel().closeFuture().await();        } finally {            group.shutdownGracefully();        }    }    public static void main(String[] args) throws InterruptedException {        new ServerExt().run(6666);        new ServerExt().run(8888);    }}public class ClientHandlerExt extends SimpleChannelInboundHandler<DatagramPacket>{    @Override    protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {        String response = packet.content().toString(CharsetUtil.UTF_8);        if (response.startsWith("谚语查询结果:")) {            System.out.println(response);            ctx.close();        }    }}public class ClientExt {    public void run(int port) throws InterruptedException{        EventLoopGroup group = new NioEventLoopGroup();        try {            Bootstrap b = new Bootstrap();            b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BACKLOG, 1024)                    .handler(new ClientHandlerExt());            Channel channel = b.bind(0).sync().channel();            // 向网段内的所有机器广播UDP消息            channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("谚语字典查询?", CharsetUtil.UTF_8),                    new InetSocketAddress("255.255.255.255", port))).sync();            if (!channel.closeFuture().await(15000)) {                System.out.println("查询超时!");            }        } finally {            group.shutdownGracefully();        }    }    public static void main(String[] args) throws InterruptedException {        new ClientExt().run(6666);    }}

Netty的WebSocket

webSocket将网络套接字引入到客户端和服务端,WebSocke特点:
- 单一的tcp连接,双方可通信
- 对代理、防火墙和路由器透明
- 无头部信息、Cookie和身份验证
- 无安全开销
- 通过ping/pong帧保持链路激活
- 服务器可主动传递消息给客户端,不再需要客户端轮询

网络超时设置

public class Server {    public static void main(String[] args) throws Exception{        EventLoopGroup pGroup = new NioEventLoopGroup();        EventLoopGroup cGroup = new NioEventLoopGroup();        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>() {            protected void initChannel(SocketChannel sc) throws Exception {                sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());                sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());                sc.pipeline().addLast(new ReadTimeoutHandler(5)); //网络读写超时设置                sc.pipeline().addLast(new ServerHandler());            }        });        ChannelFuture cf = b.bind(8765).sync();        cf.channel().closeFuture().sync();        pGroup.shutdownGracefully();        cGroup.shutdownGracefully();    }}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(5));                         sc.pipeline().addLast(new ClientHandler());                    }            });    }    public void connect(){        try {            this.cf = b.connect("127.0.0.1", 8765).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++ ){            Request request = new Request();            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());                    //再次发送数据                    Request request = new Request();                    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("断开连接,主线程结束..");    }}
原创粉丝点击