1. 运输层概述
1.1. 进程之间的通信
运输层向应用层提供通信服务,面向通信服务的最高层,同时也是用户功能的最底层;当网络边缘部分的两台主机使用网络核心部分的功能进行端到端的通信时,需要使用协议栈的运输层,网络核心部分中路由器在转发分组时使用下三层;
通信的真正实体是两台主机之中的两个应用进程在交换数据;
对于运输层来说,也是可以复用和分用的;复用:发送方不同进程可以使用同一个运输层协议传送数据;分用:接收方运输层使用同一个运输层协议在剥去报文首部,可以将数据正确交付目的应用进程;网络层为主机之间的通信提供服务,运输层在网络层的基础上,为应用进程之间提供服务;
面对不同需求,运输层需要又两种不同运输协议,一种面向连接的 TCP,一种无连接的 UDP;当运输层采用面向连接的 TCP 协议时,尽管下面网络不可靠,但是逻辑通信信道时一条全双工可靠信道;采用无连接 UDP,那么逻辑信道仍然时不可靠信道;
1.2. 运输层两个主要协议
- 用户数据包协议 UDP;
- 传输控制协议 TCP;
按照 OSI 的术语,两个对等运输实体在通信时传送的数据单位叫做运输协议数据单元 TPDU(Transport Protocol Data Unit);但在 TCP/IP 体系之中,则根据所使用协议,分别称为:TCP报文段,UDP用户数据报;
1.2.1. UDP
UDP 在传输数据前不需要建立连接,收到 UDP 后,不需要确认;虽然 UDP 不提供可靠交付,但是因为非常简单,某种情况下,UDP 是一种最有效的工作方式;
1.2.2. TCP
TCP 则提供面向连接的服务,传送之前必须要先建立连接,数据传送之后释放连接;不提供广播或者多播服务;因为提供可靠、面向连接的传输服务,因此不可避免的增加许多开销;
1.2.3. 示例协议
应用 | 应用层协议 | 运输层协议 |
---|---|---|
名字转换 | DNS | UDP |
文件传送 | TFTP | UDP |
路由选择协议 | RIP | UDP |
IP 地址配置 | DHCP | UDP |
网络管理 | SNMP | UDP |
远程文件服务器 | NFS | UDP |
IP 电话 | 专用协议 | UDP |
流式多媒体通信 | 专用协议 | UDP |
多播 | IGMP | UDP |
电子邮件 | SMTP | TCP |
远程终端接入 | TELNET | TCP |
万维网 | HTTP | TCP |
文件传送 | FTP | TCP |
1.3. 运输层的端口
应用层发送数据,将数据发送到适当的端口,运输层从该端口中读取数据,进行后续处理;接收到对方主机发来的数据,就把数据发送给适当的端口,应用进程再从该端口进行读取数据;
TCP/IP 运输层用一个 16 位端口号来标志一个端口;端口号仅具有本地意义,为了标志本计算机应用层中各个进程在和运输层交互的层间端口;运输层端口号分为两大类:
- 服务器端使用的端口号:最重要就是熟知端口号(全球通用端口号);如常见的 FTP(21),TELNET(23),SMTP(25) 等等;还有一类叫做登记端口号,数值为:1024~49151;这类端口号是为没有熟知端口号程序使用的,如果要使用这类端口号必须要在 IANA 进行登记,防止重复;
- 客户端使用端口号:49152~65535;这类端口号尽在客户进程运行时才动态选择,因此也被叫做短暂端口号,即临时端口号;
2. 用户数据报协议 UDP
2.1. UDP 概述
只是在 IP 的数据报服务之上增加了很少一点的共嗯那个;就是复用和分用及差错检测功能;
2.1.1. UDP 主要特点
-
无连接:即发送数据钱不需要建立连接;
-
尽最大努力交付:即不保证可靠交付,因此主机不需要维持复杂连接状态;
-
面向报文:即发送方的 UDP 对应用程序交下来的报文,在添加首部之后就向下交付 IP 层;对其报文即不合并也不拆分,保留这些报文的边界;应用层交给 UDP 多长报文,UDP 一次发送一个报文;故应用程序必须要选择合适大小的报文;报文太长,IP 层要分片,降低效率;报文太短,IP 数据包首部相对较长,也降低效率;
- 无拥塞控制:网络出现了拥塞之后,不会使源主机发送速率降低;
- 支持一对一、一对多、多对一、多对多交互通信;
- 首部开销小;仅八个字节;
2.2. UDP 首部格式
UDP 只有两个字段:数据字段和首部字段;首部字段由四个字段构成,每个字段长度 2 字节;各个字段如下:
1. 源端口;不需要可全 0;
2. 目的端口:接受方发现目的端口不正确,则丢弃报文,由 ICMP 发送 “端口不可达” 差错报文给发送方;
3. 长度:UDP 用户数据包的长度,最短为 8 (即仅首部);
4. 校验和:检测 UDP 用户数据报是否有错,错则丢;计算校验和的时候,需在 UDP 报之前增加 12 个字节的伪首部;并非是 UDP 真正首部,只是计算校验和临时添加的;不向下传送也不向上递交,仅计算校验和;
2.2.1. UDP 首部计算校验和
计算校验和方法与 IP 数据报首部校验和计算方法类似;只不过 IP 数据包校验和只校验 IP 数据包的首部;但是 UDP 是首部和数据部分均校验;
在发送方,先将全 0 放入校验和字段;再将伪首部以及 UDP 用户数据报堪称是很多 16 位字串连接起来;如果 UDP 用户数据报数据部分非偶数字节,则需补 全 0 字节(该字节不发送);然后按照二进制反码进行计算出这些 16 位字的和;此和即是校验和;接收方街道后,按照二进制反码求这些 16 位字串和,无差错则全 1 ;否则,即丢弃或上交附带警告;
这种差错校验的方法,检错能力并不强,但是好处是简单,处理快;
第三个字段全 0 ,第四个字段是 IP 首部中协议字段的值,UDP 对应 17,第五个字段则是 UDP 数据报长度;
3. 传输控制协议 TCP
3.1. TCP 主要特点
- 面向连接;使用 TCP 协议之前,必须建立好 TCP 连接;传输数据之后,需要释放已经建立的 TCP 连接;
- TCP 只能点对点,即一对一;
- 可靠交付;通过 TCP 连接传送数据,无差错、不丢失、不重复且按序到达;
- 全双工通信;TCP 允许通信双方在任何时候发送数据,两端都设有发送缓存和接收缓存;发送的时候,放入缓存后就可以做自己的事情;接收收到数据放入缓存,上层的应用进程在合适时候读取;
- 面向字节流;TCP 中的流指的是:流入到进程或从进程流出的字节序列;
面向字节流:虽然说程序和 TCP 交互是一次一个大小不等的数据块,但是 TCP 把应用程序交下来的数据仅仅看成一连串的无结构的字节流;TCP 不保证接收方应用程序所收到的数据块和发送方应用程序发出的 具有对应大小关系,但是接收方应用程序的字节流和发出的字节流完全一样;
怎么说呢,其实就是说,TCP将数据传输的时候,不会保留应用层数据的边界,将数据视作无结构的、连续的字节流来传输;意味可能说,你一串数据,在 TCP 中会被拆分成多个数据包来进行发送,而接收方,则需要按照字节流的顺序来进行重新组装数据;
3.2. TCP 的连接
TCP 将 连接 作为 最基本的抽象;每条 TCP 连接都会有两个端点;TCP 连接的端点叫做 套接字(socket) 或 插口;即:socket = (IP host:port)
;TCP 连接 ::= {socket1, socket2} = {(IP1:port1), (IP2:port2)}
;
同一个 IP 地址可以有多个不同的 TCP 连接,而同一个端口号也可以出现在多个不同的 TCP 连接中;
4. 可靠传输
理想的传输条件:
- 传输信道不产生差错;
- 不管发送方以多块的速度发送数据,接收方总是可以来得及处理收到的数据;
4.1. 停止等待协议
这里讨论,我们仅考虑 A 作为发送方,B 作为接收方,且将传送的数据单元称为分组,不考虑数据在哪一层次传送的;
停止等待协议:每发送完一个分组就停止发送,等待对方确认,收到确认后再发送下一个分组;
4.1.1. 无差错情况
4.1.2. 出现差错
B 接收 M1 时检测检测出了差错,就丢弃,其余的什么也不做;也可能 M1 再传输过程中就丢失了,这时 B 什么也不知道;这两种情况,B 没有任何操作;可靠传输协议这样设计:A 只要超过一段时间仍然没有收到确认,就认为刚才的分组丢失,因而重传,这叫做 超时重传;
如果要实现超时重传,那么发出一个分组,要设置一个超时计时器;如果在计时器到期前收到对方的确认,那么撤销该超时计时器;那么此处需要注意三点:
1. A 发送完一个分组后,需要暂时保留已发送的分组的副本;只有收到相应确认后,才可以清除;
2. 分组和确认分组都必须要进行编号,否则无法确认确认针对哪个分组的;
3. 超时计时器设置重传时间应该大于 数据在分组传输的平均往返时间;
4.1.3. 确认丢失和确认迟到
如果 B 对 M1 发送的确认丢失了,A 在规定的超时重传时间内没有收到确认,就无法知道自己发送的分组时出错、丢失或者是确认丢失;则 A 在超时计时器到期后,就会重传;那么这个时候 B 收到了分组后,则会采用两个行动:
1. 丢弃这个重复分组,不向上层重复交付;
2. 向 A 发送确认;
对于以上的确认和重传机制,则可以在不可靠传输网络上实现可靠的通信,常被称为:自动重传请求 ARQ(Automatic Repeat reQuest);
4.1.3. 信道利用率
对于停止等待协议来说,优点就是简单,但缺点是信道利用率太低了;在绝大多数时间内,信道都是空闲的;
假定:A 发送分组时间是:TD;分组到达 B 之后处理时间不计,立即确认,发送确认时间需要 TA;同理 A 处理确认分组时间也不计;那么A 在经过 (TD + RTT + TA) 时间之后才可以发送下个分组,信道利用率 U 可计算为:U = \frac{T_D}{T_D+RTT+T_A};此时发现绝大多数情况,信道均为空闲;那么为了提高效率,一般流水线传输,例如:连续 ARQ 协议和滑动窗口协议;
4.2. 连续 ARQ 协议(简介,详细见后续)
它会维持一个发送窗口,位于窗口内的分组都可以连续发送出去,而不需要等待对方确认;接收方一般按照累计确认即可,即不需要逐个确认,收到几个分组之后,对按序到达的最后一个分组进行确认;
优点:容易失败,即使确认丢失也不必重传;缺点:不能向发送方及时反映接收方已正确收到的所有分组信息;例如:发送方发了前五个,结果,中间第三个丢失,则接收方只能对前两个确认,得重新发送后三个;这就是 Go-back-N(回退N)
在通信质量不好的时候,会带来负面影响;
5. TCP 报文段的首部格式
-
源端口和目的端口:各占 2 个字节;
-
序号:占 4 个字节;序号范围 [0,232 - 1],增加到最大则回归 0 ,即采用 mod 232;TCP 是面向字节流的,在一个 TCP 连接中传送的字节流中的每一个字节都按顺序编号;整个要传送的字节流的起始序号必须在连接建立时设置的;首部的序号字段值指的时本报文段所发送的数据的第一个字节的序号;如果一个报文段的序号字段值时 301,携带 100 字节数据,那么最后一个字节的序号是 400;下一个报文段从 401 开始;该字段又称:报文段序号;
-
确认号:占 4 个字节;期望收到对方下一个报文段的第一个数据字节的序号;注:如果确认好 = N,则表明:到序号 N - 1 为止的所有数据都已正确收到;
-
数据偏移:占 4 位:TCP 报文段的首部长度;因为 首部又长度不确定的选项字段,因此数据偏移字段是必要的;单位是 32 位(即 4 字节为单位);数据偏移最大值为 60 字节,即 TCP 首部最大长度(选项长度不可超过 40 字节);
-
保留:占 6 位;保留给未来使用,目前为 0 ;
-
紧急 URG:当 URG = 1,标明紧急指针字段有效,告诉系统此报文段有紧急数据,应尽快传送;例如:一句发送了很长的程序要在远处主机运行,但是现在键盘发出中断命令;此时应 URG = 1;与首部紧急指针配合使用;
-
确认 ACK:当 ACK = 1,确认号才有效,ACK = 0,确认号无效;
-
推送 PSH(PuSH):当两个应用进程在进行交互式通信时,有时一端应用进程希望键入命令后立即收到响应,此时就可以 PSH = 1,立即创建一个报文段发送出去,接收方 TCP 收到 PSH = 1的,尽快交付接收应用程序,而不等待缓存填满再向上交付;(推送操作很少被使用)
-
复位 RST(ReSeT):RST = 1,标明 TCP 连接出现严重插座,需要释放重新连接;RST = 1,害用来拒绝一个非法的报文段或拒绝打开一个连接;也称为:重建位或重置位;
-
同步 SYN(SYNchronization):连接建立时用来同步序号的;SYN = 1 & ACK = 0,标明是一个连接请求报文段;对方如果统一,则:SYN = 1 & ACK = 1;
-
终止 FIN(FINish):用来释放一个连接;
-
窗口:占 2 字节;指的是发送本段报文段的一方接收窗口;因为接收方数据缓存空间是有限的;窗口值作为接收方让发送方设置其发送窗口的依据; 窗口字段明确指出了现在允许对方发送的数据量,且窗口值经常在动态变化着;
-
校验和:占 2 字节;校验范围:首部+数据部分;计算校验和,在 TCP 报文段前面加上 12 字节伪首部,与 UDP 的伪首部一致,只不过将第四个协议号字段由 17 改为 6,第 5 个字段改为 TCP 长度;
-
紧急指针:占 2 字节;URG = 1 时才有意义;指出本报文段的紧急数据的字节数,紧急数据结束之后就是普通数据;紧急指针指出紧急数据的某位在报文段的位置;当所有紧急数据都处理完,TCP 告诉应用程序回复到正常操作;(即使窗口为 0 ,也可以发送紧急数据);
-
选项:长度可变,最长 40 字节,未使用选项时:TCP 首部长度为 20 字节;最后填充字段仅为使得整个 TCP 首部长度是 4 字节的整数倍;
5.1. TCP 选项字段
-
MSS:TCP 最初只规定 最大报文段长度MSS;MSS 是一个 TCP 报文段中数据字段的最大长度;不加上首部;MSS 初衷时为了提高网络传输效率;在连接建立之初,双方将自己支持的 MSS 写入这个字段;如果未填写,默认 536 字节;
-
随着互联网发展,又增加了窗口扩大/时间戳/选择确认SACK选项等;
-
窗口扩大占 3 字节,其中有一个字节表示移位值 S;所以窗口位数从 16 增加到 (16 + S);移位值允许使用最大值为 14 ;则窗口最大值增加到 2(16+14) - 1;
-
TCP 窗口缩放选项格式(3 字节)
字节偏移 说明 1 选项类型( Kind = 3
,表示 Window Scale)2 选项长度( Length = 3
,即总共 3 字节)3 移位值(Shift Count,取值 0~14) -
移位值不超过 14 是因为没有必要,如果设置为 15 那么就是 216+15;接近于 32 位整数极限,容易导致溢出或者兼容性;其次 14 的话,已经约等于 1GB,覆盖当前网络应用场景,超过没意义了;再其次 RFC 1323 规定了;
-
-
时间戳:占 10 字节,主要时:时间戳(4字节)、时间戳回送回答字段(4字节);一方面时为了计算 RTT,一方面时为了用来处理 TCP 序号超过的时候,可以区分;(TCP 32位,如果在告诉网络中,很容易重复;例如如果用 2.5Gbit/s 的速率发送,不到 14 秒就会重复;)
6. TCP 可靠传输的实现
6.1. 以字节为单位的滑动窗口
假定 A 收到 B 的确认报文段,其确认号为 31,窗口为 20 字节;则构建上图的发送窗口;未收到 B 的确认的时候,A 可以连续把窗口内的数据都发送出去;凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便超时重传的时候运用;
发送窗口前沿也有可能向后收缩;即发生在对方通知的窗口缩小,但是 TCP 标准强烈不建议这么做;因为可能发送方在收到通知前,就已经发送许多数据,又要缩小窗口,不让发送,这样会产生一些错误;
- 对于缓存空间和序号空间都是有限且循环使用的;
- 发送窗口只是发送缓存的一部分;已经被确认数据,应该从缓存中删除;发送应用程序必须要控制写入缓存的速率,不能太快,否则发送缓存没有存放数据的空间;
- 收到的分组,检测有错,则丢弃;如果接收应用程序来不及接收到数据,接收缓存最终会被填满,接收窗口减小到 0 ,反之亦然;
- A 的发送窗口时根据 B 的接收窗口设置,但是同一时刻,因为网络传送窗口需要一定时间滞后,所以不总是一样大;
- 对于不按序到达的数据,一般临时存储在接收窗口,等到缺少的字节收到,再按序交付;
- 接收方应累积确认,但不应该过分推迟发送确认;否则导致发送方不必要的重传,反而浪费资源;TCP 规定,确认推迟的时间不应超过 0.5 秒;若是收到一连串具有最大长度报文段,应该每隔一个发送一个确认;同时接收方可以在自己数据要发送的时候把确认信息顺便捎带上,但是很少发生,因为大多数应用程序很少同时在两个方向上发送数据;
6.2. 超时重传时间的选择
TCP 采用一种自适应算法,记录一个报文发出时间以及收到的时间,两个时间差就是报文段往返时间 RTT;TCP 保留 RTT 的一个加权平均往返时间 RTTs (又称为:平滑的往返时间);每新一个 RTT 样本,会按照:新RTT_s = (1 - \alpha) \times(旧的 RTT_s) + \alpha \times (新的 RTT 样本);其中在上述表达式:0 \leq \alpha < 1; 按照 RFC 6298 标准,推荐 \alpha = 0.125;
超时计时器设置的 超时重传时间 RTO 应略大于上述的加权平均往返时间 RTT_S; RFC 6298 建议:RTO = RTT_S + 4 \times RTT_D;其中 RTT_D 在 RFC 6298 中规定:第一次策略,RTT_D 取测量到的 RTT 的一半;以后测量中采用加权平均的 RTT_D ;新RTT_D = (1 - \beta) \times (旧RTT_D) + \beta \times |RTT_S - 新RTT| ;这个 \beta 推荐为 0.25;
这里有个问题:发送一个报文后,发现超时未确认,重传,然后收到确认;**如何确定此确认报文时哪次的?**故 Karn 提出:计算加权平均 RTT_S 的时候,报文重传,则不考虑往返时间的样本;
6.3. 选择确认 SACK
如果收到报文无差错,但是未按序号,中间少一些序号;如何只传送缺少数据,而不传送收到的正确数据;选择确认即为可行方法;
7. TCP 流量控制
7.1. 利用滑动窗口实现流量控制
流量控制:让发送方发送速率不要太快,接收方来得及接收;
其中连接建立的时候,B 会告诉 A 我的接收窗口 rwnd = 400(即:receiver window);发送方的发送窗口不可超过接收方给出的接收窗口数值;注意:TCP 的窗口单位时字节,而不是报文段;
这里有一个问题,如果 B 向 A 发送 零窗口报文段后,B 又可以接收了,向 A 发送 rwnd = 400 的报文段,但传送丢失;A 一直等待 B 的非零窗口通知,B 一直等待 A 的发送数据;如果没措施,则会一直死锁下去;故:TCP 为每一个连接设有一个持续计时器;只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器;如果持续计时器到期,就发送一个零窗口的探测报文段(只携带 1 字节数据);对方就在确认这个报文段给出现在窗口值,如果仍是 0 ,则继续等待;不是则打破死局;
这里:TCP 规定,即便设置零窗口,也必须要接收:零窗口探测报文段,确认报文段和携带紧急数据的报文段;
7.2. TCP 的传输效率
应用程序将数据传送到 TCP 的发送缓存后,剩下的发送任务就由 TCP 来控制了;可以用不同机制来控制;例如:
1. TCP 维持一个变量,等于 最大报文段长度 MSS;只要缓存中存放数据达到 MSS 字节,组装一个 TCP 报文段发送出去;
2. 发送方的应用进程指明要求发送报文段,即 TCP 支持的 推送 (push) 操作;
3. 发送方的一个计时器期限到了,这时发送;
如何控制 TCP 发送报文段的时机时一个比较复杂的问题;在 TCP 中广泛使用 Nagle 算法;即:若发送应用程序将要发送的数据逐个字节送到 TCP 发送缓存,则发送方将第一个数据字节先发送出去,后面到达的缓存起来,发送方收到对第一个字节确认后,再将其缓存数据组装成一个报文段发送,同时对后面进行缓存;同时规定:当到达的数据已达到发送窗口大小的一半或已达到报文段最大长度时,就立即发送一个报文段;
产生一个叫 糊涂窗口综合征,即 TCP 接收方缓存已满,应用程序一次读取一个字节,然后就向发送方发送确认,并且设置窗口为 1;这导致发送方发送一个字节,然后又这样反复;网络效率极低;解决这个问题,则时让接收方等待一段时间,让接收缓存空出足够空间容纳,或者等接收缓存已经有一半空间;同时发送发出累计大的报文段;
上述两个方法配合使用,使得发送方不发送很小的报文段的同时,接收方也不要缓存有一点小空间,就急忙发确认通知很小的窗口信息;
8. TCP 拥塞控制
8.1. 拥塞控制的一般原理
拥塞就是:计算网络中的链路容量、交换节点中的缓存和处理等,某段时间,对网络某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变化;
拥塞控制就是:防止过多数据注入到网络中,可以使网络中的路由器或链路不至于过载;当然,前提是:网络能够承受现有的网络负荷;流量控制往往是指:点对点的通信量控制,是个端到端的问题;
当负载过大,会导致,网络吞吐量下降到 0 ,然后死锁,整个网络无法工作;实践证明,拥塞控制是很难设计,因为是个动态问题;很多时候,丢分组,是征兆而不是原因,甚至在许多情况下,拥塞控制机制本身成为:引起网络性能恶化甚至发生死锁的原因;
从控制理论看拥塞控制,大的方面看,可以分为开环控制和闭环控制两种;开环控制就是设计网络时考虑到,力求不产生拥塞,一旦系统运行,就不中途进行改正;闭环控制基于反馈环路来的,主要有:1. 监测网络系统以便检测拥塞在何时何处发生;2. 拥塞发生信息传送到可采取行动的地方;3. 调整网络系统的运行解决出现的问题;
8.2. TCP 的拥塞控制方法
8.2.1. 慢开始和拥塞避免
发送方维持一个拥塞窗口 cwnd (congestion windows) 的状态变量,其大小取决于网络的拥塞程度,且动态变化;发送方让自己的发送窗口等于拥塞窗口,如果对方接收窗口足够大,则,发送数据只需要考虑发送方的拥塞窗口;
其原则:只要网络没有出现拥塞,拥塞窗口就可以增大一些,以便更多的分组发送出去,提高网络利用率;当网络可能或已出现拥塞,则减小拥塞窗口;发送方如何知晓?目前通信线路的传输质量一半很好,因传输出差错丢弃分组概率很小;因此,发送方再超时重传计时器启动时,就判断网络出现了拥塞;
8.2.1.1. 慢开始算法
慢开始算法思路:主机在已建立的 TCP 连接上开始发送数据时,并不清楚网络当前的负荷情况;如果立即把大量数据字节注入到网络,那么可能就引起网络发生拥塞;经验证明:较好的方法是:由小到大逐渐增大注入到网络中的数据字节;即:由小到大逐渐增大拥塞窗口数值;
旧规定:在刚开始发送报文段,先把初始拥塞窗口 cwnd 设置为 1 到 2 个发送方的最大报文段 SMSS(Sender Maximum Segment Size);但是新 RFC 5681 规定:初始拥塞窗口 cwnd 设置为不超过 2 到 4 个 SMSS 的数值;具体如下:
SMSS > 2190 字节:设置 cwnd = 2 x SMSS 字节
; 且不得超过 2 个报文段;
SMSS > 1095 字节 且 SMSS \leq 3 \times 2190字节:cwnd = 3 x SMSS 字节
;且不得超过 3 个报文段;
SMSS \leq 1095 字节:cwnd = 4 x SMSS 字节
;且不得超过 4 个报文段;
慢开始规定:每收到一个对新的报文段的确认之后,可以将拥塞窗口增加最多一个 SMSS 数值,即:拥塞窗口 cwnd 每次增加量:min(N, SMSS)
;其中 N 是原先未被确认但现在刚收到确认报文段锁确认字节数,可见 N < SMSS 的时候,拥塞窗口每次增加量小于 SMSS;
8.2.1.2. 慢开始算法示例
实际 TCP 用字节数来作为窗口大小单位,但因为叙述方便等,教材采用 报文段个数作为窗口大小的单位;
在最开始,发送方首先设置 cwnd = 1,发送第一个报文段,接收方收到后确认,发送方每收到一个新报文段确认(重传不算),cwnd + 1 ;发送方收到确认后就立即调整,抓紧发送报文段;(非等待收齐确认后调整,这里只关注增长趋势)
故可知:慢不是增长速率慢,而是开始发送的值很小;相比一开始设置大的 cwnd 值,慢得多;当然,为了防止增大过快引起网络拥塞,设置了一个慢开始门限 ssthresh 状态变量;
当 cwnd < ssthresh 时,使用上述的慢开始算法;
当 cwnd > ssthresh 时,停止慢开始算法改用拥塞避免算法;
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法;
拥塞避免算法的目的时让拥塞窗口 cwnd 缓慢地增大;没经过一个 RTT,发送方 cwnd 大小加 1,不是像开始加倍增长,而是线性缓慢增长;
图 2 发生超时,即网络发生拥塞的标志;调整门限值:ssthresh = cwnd / 2;且 拥塞窗口 cwnd = 1;执行慢开始;
图点 4 位置,出现新情况,发送方连续收到 3 个重复对同一个报文段的确认;即 3-ACK;如下解释:个别报文会意外丢失,但是网络未拥塞,此时执行慢开始,将 cwnd = 1 的话,会降低传输效率,因此采用快重传算法;
快重传算法:可以让发送方尽早知道发生了个别报文段的丢失;首先要求接收方不要等待自己发送数据的时候捎带确认,而是立即确认,即便时失序的数据;发送方收到三个连续的确认,就知道,网络并非拥塞,而是少报文段,立即重传,快重传可以提高网络吞吐量 20%;
因此,在点 4 的时候,发送方知道只是丢失个别的报文段,不启动慢开始,而是执行快恢复算法;发送方调整门限值:ssthresh = cwnd / 2 ,同时设置拥塞窗口 cwnd = ssthresh = 8 ,并开始执行拥塞避免算法;
这里没考虑接收方 rwnd ,按理说,发送方窗口上限值 = Min[rwnd, cwnd];
8.3. 主动队列管理 AQM
路由器的队列通常按照先进先出 FIFO 的策略处理分组;当队列已经满了,后到达的分组将被丢弃;这叫作尾部丢弃策略;
路由器的尾部丢弃往往会导致一连串分组的丢失,使发送方出现超时重传,使 TCP 进入拥塞控制的慢开始状态;更严重的是,很多 TCP 链接都是复用在网络层的 IP 数据报中传送,如果发生路由器尾部丢弃,会同时影响到多条 TCP 链接,使虚度哦 TCP 链接在同一时间进入慢开始,TCP 术语称为:全局同步;这样会导致全网通信量下降很多,而在恢复后,又会突然增大很多;
为了避免上述现象,提出主动队列管理 AQM;所谓“主动”即不要等待已经达到最大值才开始不得不丢弃后面分组;太过于而壁咚,到预警值,就要主动丢弃到达分组,这样便提醒了发送方放慢发送的速率,因而可能使网络拥塞程度减轻,甚至不出现网络拥塞;AQM 有很多实现方法,例如随机早期检测 RED(Random Early Detection);
RED 需要维持两个参数:即队列最小门限,最大门限;处在之间的,随机概率 p 丢弃;超过最大门限,直接丢弃;小于最小门限,直接入队列;
9. TCP 运输连接管理
TCP 是面向连接的协议,TCP 运输连接的建立和释放是每一次面向连接的通信中不可少的过程;因此,运输连接有三个阶段:连接建立、数据传送、连接释放;在 TCP 连接建立过程中需要解决:1. 使每一方能够感知对方的存在;2. 允许双方协商一些参数;3. 能够对运输实体资源进行分配;TCP 采用 C/S 方式;
9.1. TCP 连接建立 (三次握手)
TCP 建立连接过程叫做握手;握手需要在客户和服务器之间交换三个 TCP 报文段;