现在的位置: 首页 > 综合 > 正文

深入剖析TCP协议的send与recv

2014年08月28日 ⁄ 综合 ⁄ 共 1841字 ⁄ 字号 评论关闭

一、 滑动窗口的概念

        TCP数据包的TCP头部有一个window字段,它主要是用来告诉对方自己能接收多大的数据(注意只有TCP包中的数据部分占用这个空间),这个字段在通信双方建立连接时协商确定,并且在通信过程中不断更新,故取名为滑动窗口。有了这个字段,数据发送方就知道自己该不该发送数据,以及该发多少数据了。TCP协议的流量控制正是通过滑动窗口实现,从而保证通信双方的接收缓冲区不会溢出,数据不会丢失。

由于窗口大小在TCP头部只有16位来表示,所以它的最大值是65536,但是对于一些情况来说需要使用更大的滑动窗口,这时候就要使用扩展的滑动窗口,如光纤高速通信网络,或者是卫星长连接网络,需要窗口尽可能的大。这时会使用扩展的32位的滑动窗口大小。

二、 滑动窗口移动规则

        1、窗口合拢:在收到对端数据后,自己确认了数据的正确性,这些数据会被存储到接收缓冲区,等待应用程序获取。但这时候因为已经确认了数据的正确性,需要向对方发送确认响应ACK,又因为这些数据还没有被应用进程取走,这时候便需要进行窗口合拢,缓冲区的窗口左边缘向右滑动。注意响应的ACK序号是对方发送数据包的序号,一个对方发送的序号,可能因为窗口张开会被响应(ACK)多次。

        2、窗口张开:窗口收缩后,应用进程一旦从缓冲区(滑动窗口区或接收缓冲区)中取出数据,TCP的滑动窗口需要进行扩张,这时候窗口的右边缘向右扩张,实际上窗口这是一个环形缓冲区,窗口的右边缘扩张会使用原来被应用进程取走内容的缓冲区。在窗口进行扩张后,需要使用ACK通知对端,这时候ACK的序号依然是上次确认收到包的序号。

        3、窗口收缩,窗口的右边缘向左滑动,称为窗口收缩,HostRequirement RFC强烈建议不要这样做,但TCP必须能够在某一端产生这种情况时进行处理。

三、send行为

        默认情况下,send的功能是拷贝指定长度的数据到发送缓冲区,只有当数据被全部拷贝完成后函数才会正确返回,否则进入阻塞状态或等待超时。如果你想修改这种默认行为,将数据直接发送到目标机器,可以将发送缓冲区大小设为0(或通过TCP_NODELAY禁用Nagle算法),这样当send返回时,就表示数据已经正确的、完整的到达了目标机器。注意,这里只表示数据到达目标机器网络缓冲区,并不表示数据已经被对方应用层接收了。

        协议层在数据发送过程中,根据对方的滑动窗口,再结合MSS值共同确定TCP报文中数据段的长度,以确保对方接收缓冲区不会溢出。当本方发送缓冲区尚有数据没有发送,而对方滑动窗口已经为0时,协议层将启动探测机制,即每隔一段时间向对方发送一个字节的数据,时间间隔会从刚开始的30s调整为1分钟,最后稳定在2分钟。这个探测机制不仅可以检测到对方滑动窗口是否变化,同时也可以发现对方是否有异常退出的情况。

        push标志指示接收端应尽快将数据提交给应用层。如果send函数提交的待发送数据量较小,例如小于1460B(参照MSS值确定),那么协议层会将该报文中的TCP头部的push字段置为1;如果待发送的数据量较大,需要拆成多个数据段发送时,协议层只会将最后一个分段报文的TCP头部的push字段置1。

四、recv行为

        默认情况下,recv的功能是从接收缓冲区读取(其实就是拷贝)指定长度的数据。如果将接收缓冲区大小设为0,recv将直接从协议缓冲区(滑动窗口区)读取数据,避免了数据从协议缓冲区到接收缓冲区的拷贝。recv返回的条件有两种:

      1. recv函数传入的应用层接收缓冲区已经读满

      2. 协议层接收到push字段为1的TCP报文,此时recv返回值为实际接收的数据长度

        协议层收到TCP数据包后(保存在滑动窗口区),本方的滑动窗口合拢(窗口值减小);当协议层将数据拷贝到接收缓冲区(滑动窗口区—>接收缓冲区),或者应用层调用recv接收数据(接收缓冲区—>应用层缓冲区,滑动窗口区—>应用层缓冲区)后,本方的滑动窗口张开(窗口值增大)。收到数据更新window后,协议层向对方发送ACK确认。

        协议层的数据接收动作完全由发送动作驱动,是一个被动行为。在应用层没有任何干涉行为的情况下(比如recv操作等),协议层能够接收并保存的最大数据大小是窗口大小与接收缓冲区大小之和。Windows系统的窗口大小默认是64K,接收缓冲区默认为8K,所以默认情况下协议层最多能够被动接收并保存72K的数据。

抱歉!评论已关闭.