Netty 系列 粘包.半包

Posted by lichao modified on September 17, 2019

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

概念

TCP/IP协议

是一个协议簇.

TCP

TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议
TCP 协议是面向流的协议(流模式),就是没有界限的一串数据.
可靠传输,保证数据正确性,保证数据顺序

当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,TCP则把数据流分割成适当长度的报文段,最大传输段大小(MSS)通常受该计算机连接的网络的数据链路层的最大传送单元(MTU)限制。之后TCP把数据包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。

UDP

UDP是面向消息/报文的协议(数据报模式),UDP是一个非连接的协议
UDP是面向报文的。发送方的UDP对应用程序交下来的报文, 在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界, 因此,应用程序需要选择合适的报文大小
可能丢包
不保证数据顺序

问题

什么是半包

指接受方没有接受到一个完整的包,只接受了部分,这种情况主要是由于TCP为提高传输效率,将一个包分配的足够大,导致接受方并不能一次接受完。( 在长连接和短连接中都会出现)。

什么是粘包

  1. 发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小、数据量小的数据包,合并成一个大的数据包发送(把发送端的缓冲区填满一次性发送)。
  2. 接收端底层会把tcp段整理排序交给缓冲区,这样接收端应用程序从缓冲区取数据就只能得到整体数据而不知道怎么拆分

一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据。TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。处理粘包的唯一方法就是制定应用层的数据通讯协议,通过协议来规范现有接收的数据是否满足消息数据的需要。 插入图片

粘包如何处理?

  1. 消息定长:报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。
  2. 包尾添加特殊分隔符:例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。
  3. 将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段

Netty如何实现拆包?

  1. Netty中提供了 DelimiterBasedFrameDecoder 解码器可以帮助我们轻松实现第二种解决方案:包尾添加特殊分隔符。
  2. LineBaseFrameDecoder的工作原理是是一次遍历ByteBuf中的可读字节判断是否有换行符”\n”或者”\r\n”,并以此位置为结束位置

什么是TCP粘包、为什么UDP不会粘包

  1. UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据

  2. UDP具有保护消息边界,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样对于接收端来说就容易进行区分处理了。传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。接收端一次只能接收发送端发出的一个数据包,如果一次接受数据的大小小于发送端一次发送的数据大小,就会丢失一部分数据,即使丢失,接受端也不会分两次去接收

    为什么Http 不存在粘包?

    每建立一次tcp链接通信一次。

将TCP连接改成短连接,一个请求一个短连接。这样的话,建立连接到释放连接之间的消息即为传输的信息,消息也就产生了边界。 这样的方法就是十分简单,不需要在我们的应用中做过多修改。但缺点也就很明显了,效率低下,TCP 连接和断开都会涉及三次握手以及四次握手,每个消息都会涉及这些过程,十分浪费性能。

Rocket 如何处理粘包问题??

RocketMQ 主要是使用了 LengthFieldBasedFrameDecoder 解码器

####