zoukankan      html  css  js  c++  java
  • Netty处理TCP拆包、粘包

    Netty实践(二):TCP拆包、粘包问题-学海无涯 心境无限-51CTO博客 http://blog.51cto.com/zhangfengzhe/1890577

     2017-01-09 21:56:06

    什么是TCP拆包、粘包?

    在网络通信中,数据在底层都是以字节流形式在流动,那么发送方和接受方理应有一个约定(协议),只有这样接受方才知道需要接受多少数据,哪些数据需要在一起处理;如果没有这个约定,就会出现本应该一起处理的数据,被TCP划分为多个包发给接收方进行处理,如下图:

    看一个TCP拆包、粘包的实例

    客户端Handler:

    服务端Handler:

    运行结果:

    上面的程序本意是CLIENT发送3次消息给SERVER,SERVER端理应处理3次,可是结果SERVER却将3条消息一次处理了。

    那么如何解决TCP拆包、粘包问题呢?其实思路不外乎有3种:

    第一种:发定长数据

    接收方拿固定长度的数据,发送方发送固定长度的数据即可。但是这样的缺点也是显而易见的:如果发送方的数据长度不足,需要补位,浪费空间。

    第二种:在包尾部增加特殊字符进行分割

    发送方发送数据时,增加特殊字符;在接收方以特殊字符为准进行分割

    第三种:自定义协议

    类似于HTTP协议中的HEAD信息,比如我们也可以在HEAD中,告诉接收方数据的元信息(数据类型、数据长度等)

    Netty如何解决TCP拆包、粘包问题?

    《Java通信实战:编写自定义通信协议实现FTP服务》中,涉及到了JAVA SOCKET这方面的处理,大家可以参考。接下来,我们来看Netty这个框架是如何帮助我们解决这个问题的。本篇博客的代码在《Netty实践(一):轻松入门》基础上进行。

    方式一:定长消息

    Server启动类:

    Client Handler:

    运行结果:

     

    利用FixedLengthFrameDecoder,加入到管道流处理中,长度够了接收方才能收到。

    方式二:自定义分隔符

    Server启动类:

    Client Handler:

    运行结果:

    方式三:自定义协议

    下面我们将简单实现一个自定义协议:

    HEAD信息中包含:数据长度、数据版本

    数据内容

    MyHead
    
    public class MyHead {
    
        //数据长度
        private int length;
    
        //数据版本
        private int version;
    
    
        public MyHead(int length, int version) {
            this.length = length;
            this.version = version;
        }
    
        public int getLength() {
            return length;
        }
    
        public void setLength(int length) {
            this.length = length;
        }
    
        public int getVersion() {
            return version;
        }
    
        public void setVersion(int version) {
            this.version = version;
        }
    
    
    }
    
    
    MyMessage
    
    public class MyMessage {
        //消息head
        private MyHead head;
        //消息body
        private String content;
    
        public MyMessage(MyHead head, String content) {
            this.head = head;
            this.content = content;
        }
    
        public MyHead getHead() {
            return head;
        }
    
        public void setHead(MyHead head) {
            this.head = head;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        @Override
        public String toString() {
            return String.format("[length=%d,version=%d,content=%s]",head.getLength(),head.getVersion(),content);
        }
    }
    
    
    编码器
    
    /**
     * Created by Administrator on 17-1-9.
     * 编码器 将自定义消息转化成ByteBuff
     */
    public class MyEncoder extends MessageToByteEncoder {
    
        @Override
        protected void encode(ChannelHandlerContext channelHandlerContext, MyMessage myMessage, ByteBuf byteBuf) throws Exception {
    
            int length = myMessage.getHead().getLength();
            int version = myMessage.getHead().getVersion();
            String content = myMessage.getContent();
    
            byteBuf.writeInt(length);
            byteBuf.writeInt(version);
            byteBuf.writeBytes(content.getBytes(Charset.forName("UTF-8")));
    
        }
    }
    
    
    解码器
    
    /**
     * Created by Administrator on 17-1-9.
     * 解码器  将ByteBuf数据转化成自定义消息
     */
    public class MyDecoder extends ByteToMessageDecoder {
    
        @Override
        protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List

     

    运行结果

    到这里,你会发现Netty处理TCP拆包、粘包问题很简单,通过编解码技术支持,让我们编写自定义协议也很方便,在后续的Netty博客中,我将继续为大家介绍Netty在实际中的一些应用(比如实现心跳检测),See You~

  • 相关阅读:
    C++的精度控制
    N*N矩阵的旋转 不开辟新空间
    关于内存对齐的探索
    最大公约数,最小公倍数
    冒泡排序,直接选择排序,插入排序实现
    vector function trmplate
    function template
    dijit/_WidgetBase
    DOJO之gridx
    [b0008] Windows 7 下 hadoop 2.6.4 eclipse 本地开发调试配置
  • 原文地址:https://www.cnblogs.com/rsapaper/p/9952841.html
Copyright © 2011-2022 走看看