《TCP/IP详解卷2:实现》笔记--TCP输出

来源:互联网 发布:玩游戏防闪退软件 编辑:程序博客网 时间:2024/04/30 06:58

函数tcp_output负责发送报文段,代码中很多地方都调用了它。

tcp_usrreq在多种请求处理中调用了这一函数:处理PRU_CONNECT,发送初始SYN;处理PRU_SHUTDOWN,发送FIN;

处理PRU_RCVD,应用进程从插口接收缓存中读取若干数据后可能需要发送新的窗口大小通告;处理PRU_SEND,发送

数据;处理PRU_SENDOOB,发送带外数据。

tcp_fasttimo调用它发送延迟的ACK。

tcp_timers在重传定时器超时时,调用发送窗口探测报文段。

tcp_timers在持续定时器超时时,调用它发送窗口探测报文段。

tcp_drop调用它发送RST。

tcp_disconect调用它发送FIN。

tcp_input在需要输出或需要立即发送ACK时调用它。

tcp_input在收到一个纯ACK报文段且本地有数据发送时调用它。

tcp_input在连续收到3个重复的ACK时,调用它发送一个单一报文段。(快速重传算法)

tcp_output首先确定是否有报文段等待发送,除了存在需要发往连接对端的数据外,tcp输出还受到其他许多因素的控制。

例如,对端可能通告接收窗口为0,阻止TCP发送任何数据;Nagle算法阻止TCP发送大量小报文段;慢启动和避免拥塞

算法限制TCP发送的数据量。相反,有些函数置位一些特殊标志,强迫tcp_output发送报文段,如TF_ACKNOW标志置位

意味着必须发送一个ACK。如果tcp_output确定不发送某个报文段,数据将保留在插口的发送缓存中,等待下一次调用

该函数。


1.tcp_output概述

函数的大概处理流程如下:

1.是否等待对端的ACK?如果发送的最大序号等于最早未确认过的序号,即不等待对端发送ACK。

2.返回慢启动。如果TCP不等待对端发送ACK,而且在一个往返时间内没有收到对端发送的其他报文段,设置拥塞窗口

为仅能容纳一个报文段,从而在发送下一个报文段时,强迫执行慢启动算法。如果数据传输中出现了显著的停顿,说明

与先前测量RTT时相比,网络条件已经发生了变化,Net/3假定出现了最坏情况,因而返回慢启动状态。

3.发送多个报文段,调用ip_output发送一个报文段。但如果ip_output确实有多个报文段需要发送,函数将试图发送另

一个报文段。因此,ip_output的一次调用能够发送多个报文段。


2.决定是否发送一个报文段

某些情况下,在报文段准备好之前已经调用了tcp_output,例如,当插口层从插口的接收缓存中移走数据,传递给用户进程

时,会生成PRU_RCVD请求。尽管不一定,但完全有可能因为进程取走了大量数据,而使得TCP有必要发送新的窗口通告。

tcp_output的前半部分确定是否存在需要发往对端的报文段。如果没有,则函数返回,不执行发送操作。


3.TCP选项

TCP首部可以有任选项。下图列出了Net/3支持的选项格式。


所有选项以1字节的kind字段开头,确定选项类型。头两个选项(kind=0或者kind=1)只有1个字节。其余3个选项都是多字节

的,带有len字段,位于kind字段之后,存储选项的长度。长度中包含kind字段和len字段。


4.窗口大小选项

窗口大小选项,避免了TCP首部窗口大小字段只有16bit的限制。如果网络带宽较高或延时较长,在需要较大的窗口,称为

长肥管道。现代网络需要较大的窗口,以获取最大的TCP吞吐量。

上图中的偏移量最小值为0,最大值为14,即窗口最大可设定为65536*2^14字节。

窗口大小选项智能出现在SYN中,因此,连接建立后,每个传输方向上的缩放因子时固定不变的。

TCP控制块中的两个变量snd_scale和rcv_scale,分别规定了发送窗口和接收窗口的偏移量。

TCP发送SYN时,无论是主动打开或被动打开,都是根据本地插口接收缓存代销选取rcv_scale值,填充窗口大小选项的偏移

量字段。


5.时间戳选项

发送方在每个报文中放入时间戳,接收方在ACK中将时间戳发回。对于每个收到的ACK,发送方根据返回的时间戳计算相应

的RTT样本值。


6.发送一个报文段

tcp_output接下来的代码负责发送报文段----填充TCP报文首部的所有字段,并传递给IP层准备发送。大概的处理流程如下:

1.构造MSS选项。

2.是否发送窗口大小选项。

3.如果需要发送窗口大小选项,则构造窗口大小选项。

4.是否需要发送时间戳。

5.如果需要发送时间戳,则构造时间戳选项。

6.选项加入后是否会造成报文长度越界。

7.更新统计值。

8.为IP和TCP首部分配mbuf。

9.向mbuf中复制数据。

10.置位PSH标志。

11.更新统计值。

12.得到存储IP和TCP首部的mbuf。

13.向mbuf中复制IP和TCP首部模板。

14.如果FIN将重传,递减snd_nxt。

15.设置报文的序号字段。

16.设置报文的确认字段。

17.如果存在首部选项,设置首部长度字段。

18.通告的窗口大小应大于最大报文段长度。

19.遵循连接的通告窗口大小的上限。

20.不要缩小窗口。

21.设置紧急数据偏移量。

22.保存起始序号。

23.增加snd_nxt。

24.更新snd_max.

25.设定重传定时器。

26.持续状态。

27.为插口调试添加路由记录。

28.设置IP长度、TTL和TOS。

29.向IP传递数据报。

30.更新rcv_adv和last_ack_sent。

31.是否还有数据需要发送。

0 0