TCP数据传输简述:

客户进程通过套接字(该进程之门)传递数据流。数据一旦通过该门,它就由客户中运行的TCP控制了。

TCP将这些数据引导到该连接的发送缓存(send buffer)里,发送缓存是发起三次握手期间设置的缓存之一。接下来TCP就会不时从发送缓存里取出一块数据,并将数据传递到网络层。

TCP可从缓存中取出并放入报文段中的数据数量受限于最大报文段长度 (Maximum Segment Size, MSS)。MSS通常根据最初确定的由本地发送主机发送的最大链路层帧长度(即所谓的最大传输单元(Maximum Transmission Unit, MTU))来设置。设置该MSS要保证一个TCP报文段(当封装在一个IP数据报中)加上TCP/IP首部长度(通常40字节)将适合单个链路层帧。以太网和PPP链路层协议都具有1500字节的MTU, 因此MSS的通常值为1460 字节。

TCP报文段格式:
首部包括源端口号和目的端口 号,它被用于多路复用/分解来自或送到上层应用的数据。另外,也包括检验和字段

还包含下列字段:

  • 32比特的序号字段(sequence number field )和32比特的确认号字段(acknowl edgment number field)。这些字段被TCP发送方和接收方用来实现可靠数据传输服务。
  • 16比特的接收窗口字段 (receive window field),该字段用于流量控制。该字段用于指示接收方愿意接受的字节数量。
  • 4比特的首部长度字段(header length field),该字段指示了以32比特的字为单位 的TCP首部长度。由于TCP选项字段的原因,TCP首部的长度是可变的。(通常, 选项字段为空,所以TCP首部的典型长度是20字节)
  • 可选与变长的选项字段(options field),该字段用于发送方与接收方协商最大报文段长度(MSS)时,或在高速网络环境下用作窗口调节因子时使用。
  • 6比特的标志字段

《自顶向下》——运输层(TCP协议) - 图1

序号和确认号
TCP把数据看成一个无结构的、有序的字节流。 所以序号是建立在传送的字节流之上,而不是建立在传送的报文段的序列之上。

假设主机A上的一个进程想通过一条TCP连接向主机B上的一个进程发送一个数据流。主机A中的TCP将隐式地对数据流中的每一个字节编号。序号是根据数据流、MSS的大小进行编号(加入数据流大小为50000,MSS大小是1000,那么每段数据流的编号就是1000、2000,以此类推),然后每一个序号被填入到相应TCP 报文段首部的序号字段中。
《自顶向下》——运输层(TCP协议) - 图2

而从主机B到达的每个报文段中都有一个序号用于从B流向A的数据。
所以主机A填充进报文段的确认号是主机A期望从主机B收到的下一字节的序号。

PS: 一条TCP连接的双方均可随机地选择初始序号。这样做可以减少将那些仍在网络中存在的来自两台主机之间先前已终止的连接 的报文段,误认为是后来这两台主机之间新建连接所产生的有效报文段的可能性。

学习案例:
《自顶向下》——运输层(TCP协议) - 图3

① 主机A发送一个序列号为42(随机生成)和确认号为79,以及携带数据“C”。
(确认号有两个含义:1.我收到了78及78以前的报文 2. 渴望收到序号为79的报文)
② 主机B回显,序号为79(与发送方的确认号相关),ACK为发送方的序号+data数据大小
③ 主机A发送第二次报文 此时的seq是原seq+字节数据大小(此时’C’大小为一个字节)

TCP实现可靠数据传输的方式
前提:TCP的可靠数据传输服务确保一个进程从其接收缓存中读出的数据流是 无损坏、无间隙、非冗余和按序的数据流;即该字节流与连接的另一方端系统发送出的字 节流是完全相同。

采用的定时器: 使用单一的重传定时器(只记录最小的序号报文)

下面是分析的3个与发送和重传有关的主要事件:

  1. 从上层应用程序接收数据: 当TCP从应用程序接收数据,将生成序号( 这个序号就是该报文段第一个数据字节的字节流编号),并数据封装在一个报文段中,并把该报文段交给IP。 当报文段被传给IP时,TCP就启动该定时 。 该定时的过期间隔是Timeoutinterval(初始是一秒,后面会不断根据RTT平均值和RTT偏差进行估算)
  2. 定时超时: TCP通过重传引起超时的报文段来响应超时事件(只重传最小的报文段)。然后TCP 重启定时器 。
  3. 收到ACK:  当收到来自接收方的确认ACK时,TCP将ACK的值 y与它的变量SendBase进行比较。TCP状态变量SendBase是最早未被确认的字节的序号。 TCP采用累积确认,所以y确认了字节编号在y之前的所有字节都已经收到。如 y > SendBase,则该ACK是在确认一个或多个先前未被确认的报文段。 然后发送方会更新它的SendBase变量;如果当前有未被确认的报文段,TCP还要重新启动定时器。

几个重传的有趣现象
《自顶向下》——运输层(TCP协议) - 图4

超时间隔加倍
定时器时限过期后超时间隔的长度是会发生变化的。
每当超时事件发生时,TCP重传具有最小序号的还未被确认的报文段。只是每次TCP重传时都会将下一次的超时间隔设为先前值的两倍,而不是用从RTT平均值和RTT偏差进行估算出的。
然而,每当定时器在另两个事件(即收到上层应用的数据和收到ACK)中的任意一个启动时,Timeoutinterval又恢复原来的算法。

快速重传
超时触发重传存在的问题之一是超时周期可能相对较长

但是发送方通常可在超时事件发生之前通过注意所谓冗余ACK来较好地检测到丢包情况。
冗余ACK (duplicate ACK)就是再次确认某个报文段的ACK,而发送方先前已经收到对该报文段的确认。

当TCP接收方收到一个序号大于下一个所期望的报文段时,这就是说有报文段丢失。这个间隔可能是由于在网络中报文段丢失或重新排序造成的。因为TCP不使用否定确认,所以接收方不能向发送方发回一个显式的否定确认。相反,它只是对已经接收到的最后一个按序字节数据进行重复确认(即产生一个冗余ACK)即可。

对于发送方而言,如果TCP发送方接收到对相同数据的3个冗余ACK, 它把这当作一种指示,说明跟在这个已被确认过3次的报文段之后的报文段已经丢失。TCP就执行快速重传(fast retransmit)

《自顶向下》——运输层(TCP协议) - 图5

流量控制
前提:一条TCP连接的每一侧主 机都为该连接设置了接收缓存。当该TCP 连接收到正确、按序的字节后,它就将数据放入接收缓存。相关联的应用进程会从该缓存中读取数据,但不必是数据刚一到达就立即读取。事实上,接收方应用也许正忙于其他任务,甚至要过很长时间后才去读取该数据。如果某应用程序读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易地使该连接的接收缓存溢出。

根据前面这种现象,我们有两种解决方法:流量控制、拥塞控制。
TCP为它的应用程序提供了流量控制服务(flowcontrol service)以消除发送方使接收方缓存溢出的可能性。流量控制因此是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。
以及,TCP发送方也可能因为IP网络的拥塞而被遏制;这种形式的发送方的控制被称为拥塞控制(congestion control)

即使流量控制和拥塞控制采取的动作非常相似(对发送方的 遏制),但是它们显然是针对完全不同的原因而采取的措施。

流量控制
TCP通过让发送方维护一个称为接收窗口( receive window)的变量来提供流量控制。
通俗地说,接收窗口用于给发送方一个指示 —— 该接收方还有多少可用的缓存空间

接收方有这么两个变量:

  • LastByteRead:主机B上的应用进程从缓存读出的数据流的最后一个字节的编号。
  • LastByteRcvd: 从网络中到达的并且已放入主机B接收缓存中的数据流的最后一个字节的编号。

由于TCP不允许已分配的缓存溢岀,下式必须成立:
LastByteRcvd - LastByteRead <= RcvBuffer(接收缓存)

接收窗口用rwnd表示,根据缓存可用空间的数量来设置:
rwnd = RcvBuffer - [ LastByteRcvd - LastByteRead ]

如图:
《自顶向下》——运输层(TCP协议) - 图6

主机B通过把当前的rwnd值放入它发给主机A的报文段接收窗口字段中 ,通知主机A它在该连接的缓存中还有多少可用空间。
而发送方主机A则直接把发送的窗口大小设置为这个值,则完成了发送与接收速率匹配。