一、TCP协议
TCP是一个面向字节流的协议,它的性质是流式的,所以它并没有分段。就像水流一样,你没法知道什么时候开始,什么时候结束。
所以他会根据当前的套接字缓冲区的情况进行拆包或是粘包。
发送端的字节流都会先传入缓冲区,再通过网络传入到接收端的缓冲区中,最终由接收端获取。
二、拆包、粘包
当我们发送两个完整包到接收端的时候:
正常情况会接收到两个完整的报文。
但也有以下的情况:
接收到的是一个报文,它是由发送的两个报文组成的,这样对于应用程序来说就很难处理了(这样称为粘包)。
还有可能出现上面这样的,虽然收到了两个包,但是里面的内容却是互相包含,对于应用来说依然无法解析(这样称为拆包)。
三、解决方式
对于这样的问题只能通过上层的应用来解决,常见的方式有:
- 在报文末尾增加换行符表明一条完整的消息,这样在接收端可以根据这个换行符来判断消息是否完整。
- 将消息分为消息头、消息体。可以在消息头中声明消息的长度,根据这个长度来获取报文(比如808协议)。
- 规定好报文长度,不足的空位补齐,取的时候按照长度截取即可。
以上的这些方式我们在Netty的pipline中加入对应的解码器都可以手动实现。
但其实Netty已经帮我们做好了,完全可以开箱即用。
比如:
- LineBasedFrameDecoder可以基于换行符解决。
- DelimiterBasedFrameDecoder可以基于分隔符解决。
- FixedLengthFrameDecoder可以指定长度解决。
上面提到的其实就是在解码中进行操作,我们也可以自定义自己的拆、粘包工具。
编解码的主要目的就是为了可以编码成字节流用于在网络中传输、持久化存储。
java中也可以实现serializable接口来实现序列化,但由于它性能等原因在一些RPC调用中用的很少。
而Google Protocol Buffer(protobuf)则是一个高效的序列化框架。
下载安装protobuf后,可以定义自己的协议格式,自动生成自定义协议格式java代码(编解码都封装好了)。
Netty已经自带了对protobuf的编解码器,只需要在pipline中添加即可。
具体原理及使用请前往https://crossoverjie.top/2018/08/03/netty/Netty(3)TCP-Sticky/,本文学习借鉴自此文章。