目录
面向字节流
引入
介绍
比喻
处理数据
粘包问题
引入
介绍
解决的本质
面向字节流
引入
对于udp来说,它是面向数据报的
- 一旦要发送数据,因为没有发送缓冲区,且不需要维护连接,直接封装完报头就发出去了
- 依靠报头中的udp长度字段,可以拆分出报文然后交付给上层
- 一个报文里就是一次响应/请求的全部数据
但是对于tcp协议来说
- 它需要维护一个持续的连接,数据也就得是连续的
- 并且他有两个缓冲区,所以读写操作不需要一一匹配
- 比如,一次写了100字节,然后分10次读 / 分10次写完100字节,一次读出,这都是可以的
- 它可以根据需要,写入/读取任意长度的数据,tcp会负责将这些数据分割成合适大小的报文段进行传输
介绍
对于tcp来说,假设我们发送了4个请求给对方:
- 从应用层看来,是4个请求报文
- 但从传输层看来,这些只是若干个字节数据,它只保证如何安全地可靠地将这些数据发送给对方,并且发送的数量不定,由对方的接收能力决定
- 当对方的传输层接收到后,也同样只认识字节,它不管,它只管往上交付若干字节
所以,实际上只有用户层有报文的概念
在传输层看来:
- 就是有若干字节数据进缓冲区->出缓冲区->进缓冲区->出缓冲区
- 于是就有了字节流动的概念,这就叫面向字节流
比喻
其实面向字节流很像水流
- 传输层是一条水管,它不管水是哪来的,只管运输
- 另一方无论是用杯子接,用盆接都行,接多少都行,只要还有水
处理数据
但交付的这些字节并不能保证就是一份报文
- 所以需要在应用层定义一个缓冲区一直读,边读边对数据进行解析(要么不读,要么就读上来一份完整的数据)
- 解析成功后就拿走,剩下的继续重复上述步骤
我们在网络计算器里也正是这样做的
- 这些处理属于我们之前的encode/decode范畴,和序列化/反序列化有上下层关系
但是边读边解析效率比较低
- 我们已经介绍过tcp的传输控制机制,能知道 -- 如果自己的接收窗口更大,对方就会动态调整自己的滑动窗口大小,可以传过来更多的数据,可以提高传输效率
- 所以,我们最好是一有数据就全部读到用户层,再慢慢处理
粘包问题
引入
如果它没有制定协议,没有任何的处理,直接读
- 读上来的就是未知字节数的数据,可能是半个报文/一个半/多个
- 总之它处理不了,只能丢弃,但丢弃了就很大概率会影响之后的处理(因为数据不完整了)
- 而这样的情况,就叫做数据报粘包问题
就像:
- 蒸包子/馒头,蒸好后去拿,你本来只想拿一个,但拿起来了一个多/多个/半个
介绍
粘包只是针对用户层的概念
- 因为传输层面向字节流,对它来说没有报文的概念,自然也就不存在粘包问题
而解决粘包的方法就是制定协议
- 在之前的网络计算器里就有体现,我们封装了数据长度和特殊字符,使用encode/decode函数做封装和解包
其实不仅只有应用层要解决,下层也需要
- 因为他们处理数据也得先分出一份完整报文,才能进行封装报头/其他处理
- 而传递的数据还是以字节为单位
解决的本质
总结一下,解决粘包问题的本质是 -- 在应用层通过协议,明确报文之间的边界
- 定长报文
- 使用特殊字符
- 自描述字段+定长报文 (udp)
- 自描述字段+特殊字符 (网络计算器,http -- 报头里记录了有效载荷的长度,但报头长度不固定,所以添加特殊字符(空行)作为报头结束的标志)
分离报文后,才进入序列化/反序列化的逻辑