传输层的观点

传输层有TCP与UDP两大协议,TCP是可靠的流传输协议,UDP是不可靠的数据报协议,但这里的可靠性仅局限在端到端的通信上。在使用TCP协议时,发送端通过应用程序调用写函数时发送的数据都能被接收端有序完整的接收,接收端调用读函数时也能有序完整的接收到发送端传输的数据。

这里有一个问题,TCP是基于流的,那么TCP分包粘包问题中“包”的概念是什么呢?这里的包指的是字节块,当TCP协议把50字节的数据一次性发送给对端时,就产生了一个大小为50的包,分为20字节和30字节发送给对端,就产生了两个包。

当发送端分别发送hello和world两条消息时,由于写函数的行为,这两条消息可能会被看作没有边界的一条消息。从宏观的角度看,发送端发送了helloworld,接收端也有序完整的收到了helloworld,但是接收端并无法区分hello和world,传输层上的接收端并没有能力划分消息边界。

image

写函数的行为

当发送端调用write函数时,我们看到有数据被发送了,但是这个数据被发送到哪里了呢?答案是发送缓冲区。

TCP提供了发送缓冲区和接收缓冲区,write函数会把数据暂存在发送缓冲区中,由网络协议栈决定何时发出。同理,read函数会把收到的数据暂存在接收缓冲区中。

当发送端连续调用两次write函数分别发送hello和world时,hello和world会被放在发送缓冲区中且无法区分边界,操作系统会把helloworld作为一条消息发送出去或者分割成多条消息发送,对端也做同样的处理,因为无法区分边界。

边界怎么划分

上面反复的提到消息之间必须要存在边界,但是TCP把消息看成流的特性决定了无法在TCP解决这个问题,这时就轮到应用层用爱发电了。

我先说结论:
1.应用层每次发送固定大小的字节块
2.应用层提供明确的边界符
3.应用层增加一个长度指针变量供对端读取数据

不妨我们先转换思维分析一下http协议的设计?

http协议

image.png
http协议请求报文分为请求行,首部字段和内容三个部分。
我们注意到在请求行和首部字段中每一行的末尾都有一个换行符\r\n,这就是http协议中明确的边界符。
http首部字段中有一个Content-length字段,这个字段标注了内容的字节数,这就是长度指针变量。类似的设计还有常用的iovec向量。
至于固定大小的字节块,TCP协议首部规定的20字节就是这种设计。

应用层的观点

应用层的作用是帮助TCP确定消息边界,确定消息边界的手段是协议。
在这一点明确之后,我们就可以在应用层设计我们自己需要的协议并辅以代码实现,从而在应用层解决TCP分包粘包问题。
image

TCP到底有多少种发包的可能?

面试中有一个经典问题:当TCP发送n字节的数据包时,有多少种发送方式。
这里不妨暴力枚举一下:
发送端先发送1字节数据,再发送n-1字节数据
发送端先发送2字节数据,再发送n-2字节数据
......
发送端先发送n-1字节数据,再发送1字节数据
发送端直接发送n字节数据
这样我们先让发送端发送x字节,在剩余n-x字节的发送方式就构成了一道简单的动态规划,答案是2^(n-1)。


四有青年
1 声望0 粉丝