发送与接收
在TCP的Socket中有两个缓冲区分别是发送缓冲区(SO_SNDBUF)
和接收缓冲区(SO_RCVBUF)
。
SO_SNDBUF
每次程序调用send发送数据
时将要发送的数据先拷贝到发送缓冲区
中,然后send函数返回了。也就是说,send函数结束后数据可能并没有发送到对端,仅仅是把应用层的buffer中的数据放到了socket内核的缓冲区。
SO_RCVBUF
接收数据
后会将数据存放到socket内核接收缓冲区
,当程序调用recv
函数后将缓冲区的数据读取到应用程序的内存中进行处理。也就是说,不管有没有调用recv函数底层的接收缓冲区都会接收数据,
粘包和拆包
在调用send和recv后一个是不一定立刻发送到对端,另一个是不一定立刻读取到应用。当全部数据拷贝到发送缓冲区后一次性发送出去时,可能造成数据包粘包
。当缓冲区在没有将数据发送出去前就被拷贝满了,可能造成数据包拆包
,具体如下所示:
拆包
和粘包
是在socket编程中经常出现的情况,在socket通讯过程中,如果通讯的一端一次性连续发送多条数据包
,TCP协议会将多个数据包打包成一个TCP报文发送出去
,称为粘包。而如果通讯的一端发送的数据包超过一次TCP报文所能传输的最大值时
,TCP协议会将一个数据包拆成多个TCP报文分开传输
,称为拆包。
Nagle算法
MTU
泛指通讯协议中的最大传输单元。一般用来说明TCP/IP四层协议中数据链路层的最大传输单元,不同类型的网络MTU也会不同,我们普遍使用的以太网的MTU是1500,即最大只能传输1500字节的数据帧。可以通过ifconfig命令查看电脑各个网卡的MTU。
MTU
指TCP建立连接后双方约定的可传输的最大TCP报文长度,是TCP用来限制应用层可发送的最大字节数。因此,如果底层的MTU是1500字节,则:
MSS = 1500 - 20(IP Header) -20 (TCP Header) = 1460 byte。
传输优化
TCP/IP协议中,无论发送多少数据,总是要在数据(DATA)前面加上40字节的协议头,对方接收到数据,也需要发送ACK表示确认。因此,即使只发送了一个字节的数据发送时就变成了41个字节,这就造成了网络传输的浪费。
为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
算法规则
-
如果SO_SNDBUF中的数据长度达到MSS,则允许发送
-
如果该SO_SNDBUF中含有FIN,表示请求关闭连接,则先将SO_SNDBUF中的剩余数据发送,再关闭
-
设置了
TCP_NODELAY = true
选项,则允许发送。TCP_NODELAY是取消TCP的确认延迟机制,相当于禁用Negale算法
-
未设置TCP_CORK选项时,若所有发出去的小数据包,即包长度小于MSS,均被确认,则允许发送
-
上述条件都未满足,但发生了超时,则立即发送
处理方式
定长协议
每次发送的字节数是固定大小,而接收端在处理数据包的时候只按照固定大小取出数据包,如果不够长度可能发生了拆包,则等待下次数据到来后进行拼包。
变长协议
发送数据时在发送的实际内容之前添加一个整数表示二进制字节的长度,接收数据则先取出整数,根据整数的数值大小取出完整数据包。