传输服务

网络层完成了“主机到主机”的通信,但主机间的通信并不是最后的结果,产生和消耗数据的并不是主机,而是某项网络应用,真正通信的是两个应用“进程”。“进程”就是“正在进行的程序”。而“进程到进程的通信”正是传输层的功能。但这不是全部,更重要的,传输层的任务是为从源主机到目的主机提供可靠的,低价格的数据传输。可靠性,低价格是两个关键词,或者为了更明显一些,可以有第三个关键词,拥塞控制。其实可靠性与拥塞控制本质上是一个词。可靠性、低价格、拥塞控制使传输层成为整个协议体系的核心与灵魂。如果没有传输层,就没有可靠的数据传输,网络层也将失去意义。

传输层的服务与网络层服务很相似,为何要分为两个层呢?答案就是“可靠性”。网络层并不提供可靠性,路由器可以丢失分组,用户无法控制中间的网络设备,用户不能选择性能更好的路由器或质量更好的数据链路,那么如何保证数据可以正常传输呢?添加一个传输层,传输层应该检测到各种问题,并采取补救措施,从而提供可靠的数据传输。传输层就是要弥补网络层技术、设计的各种缺陷。用个不恰当的比方,传输层就是“填坑的”,将网络层与应用层之间的坑、沟填平。传输层服务前,是遍布坑、沟的公路,传输层服务后,是平坦的公路。

再谈谈“低价格”这个关键词。如果在设计网络时,由网络层提供可靠性,会如何呢?如果由网络层提供可靠性,就要在中间网络的千万个路由器上添加可靠性的功能,系统的复杂性会提高数据传输的成本,那就与电话通信网的成本差别不大。可靠性由通信网提供还是由端计算机提供,二者的价格差别可太大了。可靠性由端计算机提供,才有了低成本的数据传输,低价格才是计算机网络将其他通信技术淘汰的本质。要低价格,可靠性就要放置在端计算机内部。显然放置在操作系统内部更加合理,直接由操作系统对应用程序提供可靠的数据传输服务,是非常自然的选择。传输层封装在端计算机的操作系统内,用个不恰当的比方,如同封装在房间内的电线,在装修时已经埋好了,只是提供了许多插座,这个插座接洗衣机,那个插座接冰箱,那个插座接电视,等等。

对计算机网络来说,“可靠性”的关键是什么?或者反过来,造成数据传输不可靠的最主要的原因是什么?是网络拥塞,当网络拥塞时,路由器就会丢弃数据包。传输层需要具有“调控网络”的功能。我们说,传输层在端主机内,而端主机是无法控制中间的网络设备的,“调控网络”从何谈起呢?后面会说到网络拥塞如同现实生活的堵车,根本的解决办法是不让车上路,所有的车都不上路,路就不堵了。“调控网络”是说所有端计算机内的传输层要能感知到网络的状态,能感知到当前通信网的态势,网络拥塞,就少发数据,网络通畅,就多发数据。尤其是网络拥塞时,要少发数据,让中间网络尽快恢复传输能力。

端口

最常用的进程到进程的通信方式是客户机与服务器模式。我们这里说的客户机与服务器都是指一个应用进程,而不是机器。客户机,请求服务,主动发起呼叫的进程。服务器,提供服务,被动等待的进程。总是客户机呼叫服务器,绝不可能是服务器呼叫客户机。在生活中,总是你给消防队打电话,绝不可能是消防队给你打电话。某一项服务,就是一项网络应用。端主机完全可以同时有多项网络应用,如同时打开浏览器浏览网页,打开 QQ 聊天。标识不同的网络应用进程的标识符称为协议端口号 (protocol port number),简称为端口 (port)。端口是一个 16 位的标识符。客户机用一个临时端口号定义自己。客户机可以随机选择一个端口号使用。服务器也需要用一个端口号来定义自己,但是服务器不能随机选用一个端口号。为什么呢?假设消防队随机使用一个电话号码,当发生火灾时,人们向哪打电话呢?服务器必须使用一个预先定义的,众所周知的端口号,就如同消防队使用119,急救中心使用 120 一样。

端口范围划分

  • 熟知端口,端口号范围是 0~1023。由 ICANN 分配和控制。
  • 注册端口,端口号范围是 1024~49151,ICANN 不分配也不控制,但必须在 ICANN 登记以防止重复。通常为没有熟知端口号的应用程序使用的。
  • 动态端口,端口号范围是 49152~65535,这范围的端口号即不用指派,也不需注册,可以由任何进程使用。最初的建议是客户机使用的临时端口号应该在这个范围,但许多程序员可没有遵守这个建议。

注意:端口号只具有本地意义,只是为了标志本计算机应用层中的各进程。

02.04-网络-运输层 - 图1

复用与分用

某台主机中可能有多个应用进程同时分别和网络上的许多其他主机中的某个或多个应用进程通信。这表明运输层有一个很重要的功能:复用(multiplexing)和分用(demultiplexing)。

当一个实体接受来自多个源的输入时,就称为复用(multiplexing) (多到一)。

而当一个实体将数据交到多个源时,就称为分用(demultiplexing)(一到多)。

02.04-网络-运输层 - 图2

UDP

用户数据报协议 UDP(User Datagram Protocol)是无连接不可靠的传输层协议。它只在 IP 的数据报服务之上增加了很少一点的功能,即端口的功能和校验和的功能。校验和功能是可选的,如果不选择校验功能,就全填入 0。UDP 缺点是不可靠,优点是开销小。发送数据之前不需要建立连接。这对某些实时应用是很重要的。网络出现拥塞时,不调整,不降低发送速率。UDP 用户数据报首部如下图:

02.04-网络-运输层 - 图3

校验和

UDP 的校验和功能是可选的,如果不选择校验和功能,就全填入 0,否则,计算校验和。计算时包含三个部分,伪首部,首部,数据部分,注意计算校验和是包含了数据部分的。如下图:

02.04-网络-运输层 - 图4
二进制反码计算规则:0 + 0 = 10;0 + 1 = 1; 1 + 1 = 0

02.04-网络-运输层 - 图5

TCP

TCP格式

一个 TCP 报文段分为首部和数据两部分,首部的前 20 个字节是固定的,后面有 4n 字节是根据需要而增加的选项 (n 是整数)。因此 TCP 首部的最小长度是 20 字节。而 TCP 的全部功能都体现在它首部中各字段的作用:

02.04-网络-运输层 - 图602.04-网络-运输层 - 图7

  • 紧急指针字段:占 16 位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。
  • 选项字段:长度可变。TCP 最初只规定了一种选项,即最大报文段长度 MSS。MSS 告诉对方 TCP:“我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节。”

差错控制

TCP 是可靠的传输层协议。就是说 TCP 向应用层交付的是按顺序的,没有差错的,没有丢失的数据。TCP 通过 2 种机制进行差错控制:检验和确认和超时

检验和

TCP 规定每个报文段都必须使用 16 位的检验和。TCP 检验计算时包含三个部分,伪首部,首部,数据部分,注意计算校验和是包含了数据部分的。计算方 法同 UDP 一样。

确认和超时

TCP 采用确认的方式来证实收到报文。接收方可以在合适的时候单独发送确 认报文,也可以在自己有数据要发送时把确认信息捎带上。TCP 使用肯定的累积确认。先解释“肯定”,ACK 就是“肯定”的意思。就是只在正确的情况下才发送确认。当发生丢弃,丢失,重复这些错误时,就什么也不做。“报喜不报忧”,注意,当发生错误时,不发送确认。这样,对方收不到确认,重传定时器就会超时,触发重传。 再解释“累积”,就是表示的累积效果,确认号字段值表示的是希望接收的下一个字节的序号。例如确认号为 301,是表示 301 号字节之前的数据都正确接收了,希望接收的下一个字节是 301 号字节。

重传定时器

差错控制的核心就是重传机制。TCP 使用确认-超时重传机制。具体说,TCP 每发送一个报文段,就设置一个重传定时器,当重传时间到,但还没有收到确认,就要重传这一报文段。重传定时器的值怎么设是 TCP 最复杂的事情之一。后面我们会解释原因,现在我们只要知道,重传定时器的值的估算要尽可能的准确,定时器的值不像加班费,越大越好,也不是越小越好,是越准确越好。

首先,很自然的想法,重传定时器的值应该是“一个往返时延再多一点”。“一个往返时延”如何确定?举例,8 点测了一次往返时延,8:05 又测一次,间隔 5 分钟,测了 10 次,往返时延应该用哪次测量的值呢?显然,用哪一次的
也不合适,应该是某种“平均值”。下面介绍的这个算法的目标是使估计值更加“平滑”,我们将往返时延估计值记作 RTTs。这个算法中,历史的累积效应权重更大一些,占比 7/8,新测量值的权重小,占比1/8。

02.04-网络-运输层 - 图802.04-网络-运输层 - 图902.04-网络-运输层 - 图10

流量控制

TCP 是全双工通信,TCP 为每个方向的数据传输使用两个窗口,发送窗口和接收窗口。双向通信就有四个窗口,为简化讨论,只讨论单向数据传输。

发送窗口

下图是一发送窗口例子,TCP 中的窗口以字节为单位。TCP 的传输实际是一个一个的报文段,但控制窗口的变量是以字节为单位。TCP 中只使用一个重传计时器。为方便说明,字节编号取得很小。

02.04-网络-运输层 - 图1102.04-网络-运输层 - 图12
下图是一接收窗口例子。实际上,接收窗口永远不会收缩。通常,接收方 TCP 等待应用进程来取数据。就是说,分配给接收方的缓存可能包含已接收且确认的数据字节,它们在正等待应用进程将它们拉走。接收窗口总是小于缓冲区的大小。接收窗口通常称为 rwnd,rwnd = 缓冲区大小 – 正在等待被拉走的字节数,如下图:rwnd = 40 。

02.04-网络-运输层 - 图13
TCP 通过滑动窗口机制实现流量控制。我们先忽略差错、拥塞等其他因素,且只简化讨论一个方向的数据传输。下图描述了一个例子,总是客户端发送数据,服务器确认。客户端是发送方,发送窗口,使用序号字段,服务器是接收方,接收窗口,使用确认号和窗口两个字段,窗口字段值是 rwnd 的值。

02.04-网络-运输层 - 图14

  1. 第 1 个报文段,客户端发给服务器,SYN 报文段,seq=100。三次握手建立连接的第一个报文,请求连接,并通告初始序号是 seq=100。
  2. 第 2 个报文段,服务器发给客户端,SYN+ACK 报文段,ack=101,rwnd=800。三次握手建立连接的第二个报文,窗口值通告 rwnd=800。
  3. 第3个报文段,客户端发给服务器,ACK报文段。客户端通告rwnd=2000,表示客户端的接收缓冲区的大小,我们忽略这个值,只讨论单向传输。
  4. 第 4 个报文段,客户端发给服务器,数据报文段,seq=101。客户端发送一数据报文段,携带 200 字节数据,数据字节编号 101~300,序号字段填写第 1个数据字节的编号 seq=101。发送窗口前沿在 901,后沿在 101,显示已发送 200字节数据,正等待确认。
  5. 第 5 个报文段,服务器发给客户端,ACK 报文段,ack=301,rwnd=600。服务器收到 101~300 号字节,共 200 字节数据,接收窗口调整,后沿向前滑动 200 字节,表示已收好 200 字节。向客户端发送 ACK 确认,确认字段值 ack=301,表示 301 号之前数据收好,下一个希望接收的字节是 301 号字节。注意,此刻 200 字节数据仍在接收缓冲区内,服务器的应用进程还没将它们拉走,接收窗口的大小 rwnd= 800 – 200 = 600。报文段中通告的窗口值为 600。
  6. 第 6 个报文段,客户端发给服务器,数据报文段,seq=301。客户端收到确认 ack=301,rwnd=600。客户端知道服务器已经收好 101~300 号字节,就可以删除这些数据,发送窗口调整,后沿向前滑动 200 字节,至01 处。但前沿不能向前滑动,因为现在接收方通告的 rwnd=600,前沿=301+600=901。客户端发送数据报文段,携带 300 字节数据,数据字节编号 301~600,序号字段eq=301。
  7. 第 7 个报文段,服务器发给客户端,ACK 报文段,ack=601,rwnd=400。服务器收到第二次的 301~600 号字节数据,共 300 字节数据。接收窗口调整,后沿向前滑动 300 字节,至 601 处。因 TCP 使用累积确认,向客户端发送的确认为 ack=601,表示 601 号之前所有数据收好,下一个希望接收的字节是 601 号字节。注意,此刻 200+300=500 字节数据仍在接收缓冲区内。这时,服务器的应用进程拉走 100 字节数据,接收缓冲区的 101~200 号字节空间被释放,但 201~601的 400 字节数据滞留在接收缓冲区内。接收窗口的大小 wnd= 800 – 400 = 400。通告窗口值为 rwnd=400。客户端收到确认 ack=601,rwnd=400。客户端知道服务器已经收好 601 号之前的数据,就可以删除这些数据,发送窗口调整,后沿向前滑动至 601 处。因为现在接收方通告的 rwnd=400,前沿=601+400=1001。前沿向前滑动至 1001 处。
  8. 第 8 个报文段,服务器发给客户端,ACK 报文段,ack=601,rwnd=600。服务器的应用进程又拉走 200 字节数据,接收缓冲区的 201~400 号字节空间被释放,但 401~601 的 200 字节数据仍滞留在接收缓冲区内。接收窗口的大小 rwnd= 800 – 200 = 600。通告窗口值为 400。对于确认来说,服务器现在收好的是 601 号字节之前的数据,确认为 ack=601,表示希望接收的下一个字节是 601 号字节。客户端收到确认 ack=601,wnd=600。客户端知道服务器已经收好 601 号之前的数据,发送窗口的后沿就在 601 处,不需滑动。因为现在接收方通告的rwnd=600,前沿=601+600=1201。前沿向前滑动至 1201 处。

糊涂窗口综合症

假如 TCP 发送的报文段只含有 1 个字节的数据,那么意味着为发送 1 字节的数据,而发送了 41 个字节的报文段,20 个字节的 TCP 首部和 20 个字节的 IP首部。此时的效率是 1/41。这一现象称为糊涂窗口综合症(Silly Window Syndrome)。糊涂窗口综合症是怎样产生的呢?

由发送方产生的糊涂窗口综合症(Syndrome Created by the Sender)

如果发送方 TCP 正在为一个产生数据很缓慢的应用程序服务,例如一次产生 1 字节数据,就有可能产生糊涂窗口综合症。解决方法是使用 Nagle 算法。

Nagle 算法

  1. 发送方 TCP 把它从应用进程收到的第一块数据发送出去,即使只有 1 字节。
  2. 在发送一个报文段后,发送方 TCP 就在输出缓存中累积数据并等待,直至收到接收方发来的确认,或者已积累了足够的数据已达到报文段的最大长度时,就立即发送一个报文段。
  3. 重复步骤 2。

Nagle 算法之巧妙,在于其巧妙地平衡了应用程序产生数据速度和网络传输速度。如果应用程序比网络速度快,报文段就大(最大报文段长度),如果应用程序比网络速度慢,报文段就小。

由接收方产生的糊涂窗口综合症(Syndrome Created by the Receiver)

如果接收方 TCP 正在为一个消耗数据很缓慢的应用程序服务,例如一次消耗 1 字节数据,接收方每次发送 rwnd=1 的通告,就有可能产生糊涂窗口综合症。解决方法的是推迟确认。

报文到达时,不立即发送确认,接收方等待一段时间,直到输入缓存有足够的空间(或者接收缓存已有一个最长报文段的空间,或者接收缓存已有一半空闲的空间),就发送确认报文。但推迟确认不能超过 500ms。

拥塞控制

拥塞控制是 TCP 协议中最重要的一部分。理解 TCP 的拥塞控制,关键在于真正理解网络拥塞这一现象,理解了拥塞,以后的内容都会顺理成章的很好理解。

拥塞概述

两个主机,通过中间的一个传输网,连接在一起。正是因为中间有网络,就有了网络拥塞问题。谈网络拥塞之前,先回忆一下路由器的原理。网络层的路由器是一种“尽力而为”的机制。当超过路由器的能力时,路由器就将会丢弃数据报。假设路由器每秒能转发 1000 个数据报,此刻来了 1200 个数据报,路由器就将后 200 个数据报丢弃。注意:当没有超过路由器的负载能力时,路由器是不会丢弃数据报的。换一句话说,就是某个路由节点拥塞了,才会丢弃数据报。

怎么解决拥堵呢?很明显有两种方案,用公路网来打比方就是增加路的数量和减少驶入公路网的车。从协议的角度考虑,自然是做不到增加路的数量,所以我们就要控制发送到网络中的数据量。

传输网络

当网络拥塞时,如同城市交通堵塞,南城的人去不了北城,北城的人一样也去不了南城,路都堵死了,谁也走不了。也就是说,拥塞时,网络外围的所有主机,发送的数据包都会被丢掉,所以一定不会有返回的 ACK 确认,超时定时器一定会闹响。也就是说,网络拥塞时,所有主机都会超时。这样问题就解决了,简单归纳为一句话,超时就表示网络拥塞。

超时就表示网络全拥塞。

因为 TCP 协议中以超时做为网络拥塞的判断依据,重传定时器的值需要估算合适,这很重要。值估算小了,实际网络不拥塞,确产生了超时重传,误判为拥塞,就不能充分使用网络的传输能力。值估算大了,实际网络已经拥塞,确没有产生超时重传,误判为通畅,就会使拥塞更加恶化,最终通信崩溃。在日常的生活中,城市的交通堵塞一定是渐渐堵死的,绝无可能在前一分钟,全城都是通畅的,后一分钟,全城所有的道路都堵死。总是开始时某些路段堵死,然后慢慢扩大,最后全部堵死。如果在某些路段堵死的时候,就开始疏导,有可能不会演变为全堵死。同理在计算机网络中,也很难相信,在前一分钟,所有的路由器都负载很轻,后一分钟,所有的路由器都超负载。应该是,某些路由器超负载了,其他路由器正常,这时后续的数据包就会自动绕路。假设某主机,连续发送了 2,3,4,5号数据包,2 号数据包碰到超负荷的路由器,被路由器丢弃,3,4,5 号数据包绕路到达目的主机,目的主机发送了 3 个 ACK 确认,请求 2 号数据包。当发送方收到 3 个重复 ACK 时,就会判断,网络是部分拥塞的,前面的数据包堵死在路上,后面的数据包绕路走了,已经到达目的地。简单归纳为一句话,3 个重复ACK 就表示网络部分拥塞,我称为半拥塞。

3 个重复 ACK 就表示网络半拥塞。

至此,外围的主机有了推测中间传输网络状态的办法,这两个事件就标志着网络的两种状态。用两个事件标志两种网络状态的方法,需要认真领会。TCP 的拥塞控制不能算闭环,没有一个具体的设备发出一个网络拥塞的信号,因为拥塞是全网的状态,不是某一个路由器的状态。一个路由器超载,可以绕其他路由器。TCP 的拥塞控制也不能算开环,“超时”与“3ACK”这两个事件确实反馈了中间传输网络的状态,为决策提供了依据。了解了网络现在的状态,就好办了。全拥塞有全拥塞的处理办法,半拥塞有半拥塞的处理办法。

拥塞窗口

在上文中,讨论过 TCP 的流量控制,发送方窗口大小是由接收方的可用缓存空间(rwnd)决定的,就是由接收方指示发送方应当使用多大的窗口,这当然可以保证接收方不会溢出。但是,这个方法没考虑网络的存在,上文说过,要调控网络拥塞,就要根据当前网络的状态,调整发送到网络中的数据量。也就是说,TCP 需要一个控制变量,即TCP 发送方使用拥塞窗口 cwnd (Congestion Window)作为控制变量,根据当前网络的拥塞程度,拥塞窗口的大小动态地变化,调整发送的数据量。这样一来发送窗口大小不仅取决于接收方通告的接收窗口 rwnd,还取决于网络的拥塞状况 cwnd,进而 实际的发送窗口 = min( rwnd , cwnd )

拥塞检测

TCP 的发送方使用两个事件作为判断网络全拥塞和半拥塞的依据。超时表示网络全拥塞。3 次重复 ACK 表示网络半拥塞。

超时

上文已经解释过拥塞的现象,我们现在简单理解为:发送方的超时事件就表示中间网络全部堵死了。

发送方 TCP 在整个连接期间,只维护一个 RTO 计时器。发送方发送段 1 和 段 2,计时器启动,接收方发回 ACK,发送方收到 ACK 后,计时器清零。在启动计时器,发送段 3,段 4,段 3 丢失,段 4 到达,接收方将段 4 存储下来,因为段 3 丢失,接收方留出一个间隙,表明数据是不连续的,接收方只能再发送对段 2 的确认 ACK。发送方收到确认,但因为不是对段 3,段 4 的确认,计时器不能清零,计时器超时,就会重传段 3,并重启计时器,这次段 3 正常到达,接收方发送 ACK,发送方收到,将计时器清零。

02.04-网络-运输层 - 图15

三次重复 ACK,也称做“快重传”(Fast retransmission)。如下图:发送方发送 2 个段后,正常收到 ACK,这个 ACK 是原始的 ACK,超时计时器清零。发送方再发送 4 个段,并再次启动超时计时器,段 3 丢失,段 4,5,6 到达。当接收方收到失序的数据段时,立即发送 ACK。接收方会发出 3 个重复的 ACK。发送方收到三个重复的 ACK,就立即重传丢失的报文段,而不等待计时器超时,并重启计时器。这一规则称为“快重传”,目前的 TCP 都遵守这规则。

02.04-网络-运输层 - 图16

拥塞控制策略

TCP 拥塞策略基于两个阶段,慢启动(slow-start,SS)阶段和拥塞避免(congestion avoidance,CA)阶段。在慢启动阶段,发送方从非常慢的速率开始,很快达到一个门限值。当到达门限值,进入拥塞避免阶段。

慢启动(SS, Slow start)

指数增大,拥塞窗口 cwnd 从 1 个最大报文段 MSS 开始。每收到一个 ACK 确认,拥塞窗口增加一个 MSS。慢启动算法开始很慢,但它是以指数增大的。

按 ACK 计算, cwnd = cwnd + 1。

如图,从 cwnd=1 开始,第 1 个 ACK 到达后,cwnd 加 1,就是 2。这时,就可发送 2 个段,相应的回来 2 个 ACK,对于每个 ACK,cwnd 加 1,就是 4 了。是按指数增大的。

02.04-网络-运输层 - 图17
拥塞避免(CA,Congestion avoidance)

加法增大,在慢启动阶段,当拥塞窗口达到慢开始门限 ssthresh 的值时,慢启动停止,进入拥塞避免阶段。此时,拥塞窗口按加法增大。每次整个“窗口”的所有段都被确认后,拥塞窗口增加 1。

举例,发送方以 cwnd=4 开始,此刻发送方只能发 4 个段,在 4 个 ACK 到达后,拥塞窗口才加 1。如果按往返时延 RTT 观察,拥塞窗口是每一轮次加 1。

按 ACK 计算, cwnd = cwnd + ( 1 / cwnd )
按 RTT 计算, cwnd = cwnd + 1

02.04-网络-运输层 - 图18
在拥塞避免阶段,拥塞窗口加法增大。拥塞避免阶段会一直持续下去吗?继续下去,会是什么情况?显然,拥塞避免阶段继续下去,网络只会有处于通畅,半拥塞,全拥塞三种状态中的一种。就如同城市交通一样,只会是不堵车,部分堵死,全部堵死这三种情况之一。

通畅

标志是无事件发生。拥塞避免阶段继续,拥塞窗口继续按加法增大。

半拥塞

  • 标志事件是:发送方收到三次重复 ACK(3dupACKs)。
  • 处理办法是:
    • ssthresh 门限值设为此刻 cwnd 的一半 ssthresh = cwnd / 2
    • 将拥塞窗口设为门限值。 cwnd = ssthresh
    • 进入拥塞避免阶段。

全拥塞

  • 标志事件是:发送方超时。
  • 处理办法是:
    • ssthresh 门限值设为此刻 cwnd 的一半 ssthresh = cwnd / 2
    • 将拥塞窗口重新设置设为 1。 cwnd = 1
    • 进入慢启动阶段

02.04-网络-运输层 - 图1902.04-网络-运输层 - 图20

连接管理

TCP 是一种面向连接的协议。TCP 以全双工方式传送数据。在 TCP 中,面向连接的传输需要经过三个阶段:连接建立,数据传输,连接断开。TCP 连接采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client),被动等待连接建立的应用进程叫做服务器(server)。

TCP 连接管理的三规则

  • 规则 1:TCP 规定,SYN 报文段不能携带数据,但要消耗掉一个序号
  • 规则 2:TCP 的标准规定,ACK 报文段可以携带数据。但如果不携带数据则不消耗序号。
  • 规则 3:TCP 规定,FIN 报文段即使不携带数据,它也消耗掉一个序号。

连接建立

TCP 建立连接的过程叫做三次握手(three-way handshaking)。服务器首先打开一个端口,端口处于监听态,称为被动打开。客户端发起连接请求,连接到服务器的打开的端口上,连接就建立了。

02.04-网络-运输层 - 图21
举例:客户端发送的第 1 个 SYN 报文段,序号为 8000,服务器发送的第 2 个 SYN+ACK 报文段,序号为 15000,此后,客户端发送了第 3 个报文段,未携带数据,第 4 个报文段,携带 100 字节数据,问客户端发送的第 3,4 个报文段的序号是什么?

解析:第 3 个报文段,序号为 8001,因为序号 8000 已经被 SYN 报用掉了。 第 4 个报文段,序号仍然是 8001,注意不是 8002,因为第 3 个报文段是一个 ACK 报文段,并且没携带数据,所以不消耗序号,就是说第 3 个报文的序号 8001 没有被用掉,在第 4 个报文中继续使用。

数据传输

连接建立后,可进行双向的数据传输。客户端和服务器都可以发送数据和确认。TCP 连接使用了序号和确认号的机制。

序号

TCP 把要发送的数据都按字节编上号。两个方向的编号是相互独立的。编号并不是从 0 开始,而是使用一个随机数作为初始编号,初始编号在建立连接的第一个 SYN 报文段里通告给对方。每个 TCP 报文段都有序号字段,序号字段值是这个报文段中第一个数据字节的编号。

TCP 报文的序号字段值是这个报文段中第一个数据字节的编号。

确认号

TCP 使用确认机制。当报文段中 ACK 标志置 1,报文的确认号字段有效,TCP 的确认是累计确认,确认号字段值是完全接收好的数据的最后一个字节的编号+1,表示此值前的数据已收好,期望接收的下一字节是此值。举例,确认号是5644,表示从开始到 5643 号字节的数据都已收好,希望接收 5644 号字节。

TCP 是累积确认。确认号字段值是期望接收的下一个字节的编号。

举例:客户端发送一报文段,序号 8001,确认号 15001,携带 1000 字节数据。服务器发送的下一个报文段,序号,确认号是多少?服务器的回复携带 2000 字节数据,则客户端发送的再下一个报文,序号,确认号是多少?

解析:因第 1 个报文的确认号 15001,是期望接收的下一个报文段的序号,所以,服务器发送的下一个报文段的序号是 15001。因第 1 个报文的序号 8001,携带 1000 字节数据,服务器收到了 8001-9000 编号的 1000 字节数据,确认号是9001,表示 9001 号字节之前的数据已经收好,希望接收的下一个字节是 9001号字节。同理,第 3 个报文,序号为 9001,确认号是 17001。

连接断开

数据传输结束后,客户端和服务器任一方都可以发起断开连接。一般来说客户端发起断开连接。TCP 连接释放过程是四次握手。

02.04-网络-运输层 - 图22