夜光序言:
一个人时要坚强,泪水没肩膀依靠就昂头,没有谁比自己爱自己更实在;一个人的日子我们微笑,微笑行走,微笑面对。
正文:
可靠数据传输的原理
仅考虑在一般情况下可靠数据传输的间题,仅考虑单向数据传输的情况,即数据传输是从发送方到接收方的。可靠的、双向数据传输 (即全双工数据传输)的情况从概念上讲是一样的。
主要目的是帮助大家理解 TCP 的可靠数据传输机制。
1.完全可靠信道上的可靠数据传输:rdt1.0
考虑最简单的情况,即底层信道是完全可靠的,是完美信道。
no bit errors
no loss of packets
因此应用层调用 rdt_send(data),
packet = make_pkt(data) //发送方从较高层接收数据,生成一个包含该数据的分组
udt_send(packet) //将分组发送到信道中
接收方应用层调用 rdt_rcv(packet),(注意是接收方主动接收)
extract (packet,data) //接收方接收提取,
deliver_data(data) //交付数据
2.具有比特差错信道上的可靠数据传输:rdt2.0
在分组的传输、传播或缓存的过程中,这种比特差错通常会出现在网络的物理部件中。
因此需要引入checksum 检测比特位错误
需要引入确认机制:
acknowledgements (ACKs): receiver 明确告诉 sender 数据包接收 OK
negative acknowledgements (NAKs): receiver 明确告诉 sender 数据包存在错误
sender 收到 NAK,重传数据包(pkt)
rdt2.0 (在 rdt1.0 基础上)采用的新机制:
校验和:错误检测
接收者反馈: 控制信息(ACK,NAK)
重传
但是确认信息本身出错、引起重复的传输怎么办呢? rdt2.1如果 ACK/NAK 出错,那么发送者直接重传当前的数据报
发送者为数据报添加字段:序号 (sequence number )接收者抛弃重复的数据报
3.具有比特差错的丢包信道上的可靠数据传输:rdt3.0
现在假定除了比特受损外,底层信道还会丢包。
发送者等待 ACK 足够的时间
然后重传(假如还是没有 ACK)
如果数据包(or ACK) 延迟 (但没有丢失):
重传导致重复, 顺序号的使用可以处理这种情况,但是接收者必须指定所确认数据包的顺序号
TCP
如果仅仅为了理解网络工作原理,那么只要了解 TCP 是可靠传输,数据传输丢失时会重传就可以了。
那么下面内容很有可能成为考查的知识点,其中的重点是序号/确认号的编码、超时定时器的设置、可靠传输和连接的管理。
1 TCP 连接
TCP 面向连接,在一个应用进程开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即它们必须相互发送某些预备报文段,以建立连接。
连接的实质是双方都初始化与连接相关的发送/接收缓冲区,以及许多 TCP 状态变量。
这种 “连接”不是一条如电话网络中端到端的电路,因为它们的状态完全保留在两个端系统中。
TCP 连接提供的是全双工服务,应用层数据就可在从进程 B 流向进程 A 的同时,也从进程 A 流向进程 B。
TCP 连接也总是点对点的,即在单个发送方与单个接收方之间建立连接。
一个客户机进程向服务器进程发送数据时,客户机进程通过套接字传递数据流。
客户机操作系统中运行的 TCP 软件模块首先将这些数据放到该连接的发送缓存里,然后会不时地从发送缓存里取出一块数据发送。
TCP 可从缓存中取出并放入报文段中发送的数据量受限于最大报文段长 MSS,通常由最大链路层帧长度来决定(也就是底层的通信链路决定)。例如一个链路层帧的最大长度 1500字节,除去数据报头部长度 20 字节, TCP 报文段的头部长度 20 字节,MSS 为 1460 字节。
报文段被往下传给网络层,网络层将其封装在网络层 IP 数据报中。然后这些数据报被发送到网络中。
当 TCP 在另一端接收到一个报文段后,该报文段的数据就被放人该连接的接收缓存中。
应用程序从接收缓存中读取数据流(注意是应用程序来读,不是操作系统推送)。
TCP 连接的每一端都有各自的发送缓存和接收缓存。
因此TCP连接的组成包括:主机上的缓存、控制变量和与一个进程连接的套接字变量名,以及另一台主机上的一套缓存、控制变量和与一个进程连接的套接字。
在这两台主机之间的路由器、交换机中,没有为该连接分配任何缓存和控制变量。
2 报文段结构
TCP 报文段由首部字段和一个数据字段组成。数据字段包含有应用层数据。由于 MSS 限制了报文段数据字段的最大长度。当 TCP 发送一个大文件时,TCP 通常是将文件划分成长度为 MSS 的若干块。
TCP 报文段的结构。
首部包括源端口号和目的端口号,它用于多路复用/多路分解来自或送至上层应用的数据。另外,TCP 首部也包括校验和字段。报文段首部还包含下列字段:
32 比特的序号字段和 32 比特的确认号字段。这些字段被 TCP 发送方和接收方用来实现可靠数据传输服务。
16 比特的接收窗口字段,该字段用于流量控制。该字段用于指示接收方能够接受的字节数量。
4 比特的首部长度字段,该字段指示以 32 比特的字为单位的 TCP 首部长度。一般 TCP 首部的长度就是 20 字节。
可选与变长的选项字段,该字段用于当发送方与接收方协商最大报文段长度,或在高速网络环境下用作窗口调节因子时使用。
标志字段 ACK 比特用于指示确认字段中的 ACK 值的有效性,即该报文段包括一个对已被成功接收报文段的确认。SYN 和 FIN 比特用于连接建立和拆除。
PSH、URG 和紧急指针字段通常没有使用。
•序号和确认号
TCP 报文段首部两个最重要的字段是序号字段和确认号字段。
TCP 把数据看成一个无结构的但是有序的字节流。TCP 序号是建立在传送的字节流之上,而不是建立在传送的报文段的序列之上。
一个报文段的序号是该报文段首字节在字节流中的编号。
例如,假设主机 A 上的一个进程想通过一条 TCP 连接向主机 B 上的一个进程发送一个数据流。主机 A 中的 TCP 将对数据流中的每一个字节进行编号。假定数据流由一个包含 4500字节的文件组成(可以理解为应用程序调用 send 函数传递过来的数据长度),MSS 为 1000 字节(链路层一次能够传输的字节数),如果主机决定数据流的首字节编号是 7。
TCP 模块将为该数据流构建 5 个报文段(也就是分 5 个 IP 数据报)。第一个报文段的序号被赋为 7;第二个报文段的序号被赋为 1007,第三个报文段的序号被赋为 2007,以此类推。前面 4 个报文段的长度是 1000,最后一个是 500。
确认号要比序号难理解一些。前面讲过,TCP 是全双工的,因此主机 A 在向主机 B 发送数据的同时,也可能接收来自主机 B 的数据。从主机 B 到达的每个报文段中的序号字段包含了从 B 流向 A 的数据的起始位置。因此主机 B 填充进报文段的确认号是主机 B 期望从主机 A 收到的下一报文段首字节的序号。
假设主机 B 已收到了来自主机 A 编号为 7-1006 的所有字节,同时假设它要发送一个报文段给主机 A。主机 B 等待主机 A 的数据流中字节 1007 及后续所有字节。所以,主机 B 会在它发往主机 A 的报文段的确认号字段中填上 1007。
再举一个例子,假设主机 B 已收到一个来自主机 A 的包含字节 7-1006 的报文段,以及另一个包含字节 2007-3006 的报文段。由于某种原因,主机 A 还没有收到字节 1007-2006 的报文段。
在这个例子中,主机 A 为了重组主机 B 的数据流,仍在等待字节 1007。因此,A 在收到包含字节 2007-3006 的报文段时,将会又一次在确认号字段中包含 1007。因为 TCP 只确认数据流中至第一个丢失报文段之前的字节数据,所以 TCP 被称为是采用累积确认。
TCP 的实现有两个基本的选择:
①接收方立即丢弃失序报文段;或者
②接收方保留失序的字节,并等待缺少的字节以填补该间隔。
一条 TCP 连接的双方均可随机地选择初始序号。这样做可以减少将那些仍在网络中的来自两台主机之间先前连接的报文段,误认为是新建连接所产生的有效报文段的可能性。
•例子 telnet
Telnet 由是一个用于远程登录的应用层协议。它运行在 TCP 之上,被设计成可在任意一对主机之间工作。
假设主机 A 发起一个与主机 B 的 Telnet 会话。因为是主机 A 发起该会话,因此主机 A被标记为客户机,主机 B 被标记为服务器。用户键入的每个字符(在客户机端)都会被发送至远程主机。远程主机收到后会复制一个相同的字符发回客户机,并显示在 Telnet 用户的屏幕上。
这种“回显”用于确保由用户发送的字符已经被远程主机收到并处理。因此,在从用户击键到字符显示在用户屏幕上之间的这段时间内,每个字符在网络中传输了两次。
现在假设用户输入了一个字符“C”,假设客户机和服务器的起始序号分别是 42 和 79。
前面讲过,一个报文段的序号就是该报文段数据字段首字节的序号。因此,客户机发送的第一个报文段的序号为 42,服务器发送的第一个报文段的序号为 79。前面讲过,确认号就是主机期待的数据的下一个字节序号。在 TCP 连接建立后但没有发送任何数据之前,客户机等待字节 79,而服务器等待字节 42。
如图所示,共发了 3 个报文段。
第一个报文段是由客户机发往服务器,其数据字段里包含一字节的字符“C”的 ASCII 码,其序号字段里是 42。另外,由于客户机还没有接收到来自服务器的任何数据,因此该报文段中的确认号字段里是 79。
第二个报文段是由服务器发往客户机。它有两个目的:第一个目的是为服务器所收到的数据提供确认。服务器通过在确认号字段中填入 43,告诉客户机它已经成功地收到字节 42及以前的所有字节,现在正等待着字节 43 的出现。第二个目的是回显字符“C”。因此,在第二个报文段的数据字段里填入的是字符“C”的 ASCII 码,第二个报文段的序号为 79,它是该 TCP 连接上从服务器到客户机的数据流的起始序号,也是服务器要发送的第一个字节的数据。
这里客户机到服务器的数据的确认被装载在一个服务器到客户机的数据的报文段中,这种确认被称为是捎带确认.
第三个报文段是从客户机发往服务器的。它的唯一目的是确认已从服务器收到的数据
3 往返时延的估计与超时
TCP 如同前面所讲的 rdt 协议一样,采用超时/重传机制来处理报文段的丢失问题。
最重要的一个问题就是超时间隔长度的设置。显然,超时间隔必须大于 TCP 连接的往返时延 RTT,即从一个报文段发出到收到其确认时。否则会造成不必要的重传。
•估计往返时延
TCP 估计发送方与接收方之间的往返时延是通过采集报文段的样本 RTT 来实现的,就是从某报文段被发出到对该报文段的确认被收到之间的时间长度。
也就是说 TCP 为一个已发送的但目前尚未被确认的报文段估计 sampleRTT,从而产生一个接近每个 RTT 的采样值。但是,TCP 不会为重传的报文段计算 RTT。
为了估计一个典型的 RTT,采取了某种对 RTT 取平均值的办法。
TCP 据下列公式来更新
即估计 RTT 的新值是由以前估计的 RTT 值与 sampleRTT 新值加权组合而成的。
参考值是 a=0.125,因此是一个加权平均值。显然这个加权平均对最新样本赋予的权值要大于对老样本赋予的权值。因为越新的样本能更好地反映出网络当前的拥塞情况。从统计学观点来讲,这种平均被称为指数加权移动平均除了估算 RTT 外,还需要测量 RTT 的变化,RTT 偏差的程度,因为直接使用平均值设置计时器会有问题(太灵敏)。
RTT 偏差也使用了指数加权移动平均。B 取值 0.25.
•设置和管理重传超时间隔
假设已经得到了估计 RTT 值和 RTT 偏差值,那么 TCP 超时间隔应该用什么值呢?
TCP 将超时间隔设置成大于等于估计 RTT 值和 4 倍的 RTT 偏差值, 否则将造成不必要的重传。但是超时间隔也不应该比估计 RTT 值大太多,否则当报文段丢失时,TCP 不能很快地重传该报文段,从而将给上层应用带来很大的数据传输时延。因此,要求将超时间隔设为估计 RTT 值加上一定余量。
当估计 RTT 值波动较大时,这个余最应该大些;当波动比较小时,这个余量应该小些。因此使用 4 倍的偏差值来设置重传时间。
TimeoutInterval = EstimatedRTT + 4*DevRTT
4 可信数据传输
因特网的网络层服务是不可靠的。
IP 不保证数据报的交付,不保证数据报的按序交付,也不保证数据报中数据的完整性。
TCP 在 IP 不可靠的尽力而为服务基础上建立了一种可靠数据传输服务。
TCP 提供可靠数据传输的方法涉及前面学过的许多原理。
TCP 采用流水线协议、累计确认。
TCP 推荐的定时器管理过程使用单一的重传定时器,即使有多个已发送但还未被确认的报文段也一样。重传由超时和多个 ACK 触发。
在 TCP 发送方有 3 种与发送和重传有关的主要事件:从上层应用程序接收数据,定时器超时和收到确认 ACK。
从上层应用程序接收数据。一旦这个事件发生,TCP 就从应用程序接收数据,将数据封装在一个报文段中,并将该报文段交给 IP。注意到每一个报文段都包含一个序号,这个序号就是该报文段第一个数据字节的字节流编号。如果定时器还没有计时,则当报文段被传给 IP时,TCP 就启动一个该定时器。
第二个事件是超时。TCP 通过重传引起超时的报文段来响应超时事件。然后 TCP 重启定时器。
第三个事件是一个来自接收方的确认报文段(ACK)。当该事件发生时,TCP 将 ACK 的值 y与变量 SendBase(发送窗口的基地址)进行比较。TCP 状态变量 SendBase 是最早未被确认的字节的序号。就是指接收方已正确按序接收到数据的最后一个字节的序号。
TCP 采用累积确认,所以 y 确认了字节编号在 y 之前的所有字节都已经收到。
如果 Y>SendBase,则该 ACK 是在确认一个或多个先前未被确认的报文段。因此发送方更新其 SendBase 变量,相当于发送窗口向前移动。
另外,如果当前有未被确认的报文段,TCP 还要重新启动定时器。
快速重传
超时触发重传存在的另一个问题是超时周期可能相对较长。当一个报文段丢失时,这种长超时周期迫使发送方等待很长时间才重传丢失的分组,因而增加了端到端时延。所以通常发送方可在超时事件发生之前通过观察冗余 ACK 来检测丢包情况。
冗余 ACK 就是接收方再次确认某个报文段的 ACK,而发送方先前已经收到对该报文段的确认。
当 TCP 接收方收到一个序号比所期望的序号大的报文段时,它认为检测到了数据流中的一个间隔,即有报文段丢失。这个间隔可能是由于在网络中报文段丢失或重新排序造成的。
因为 TCP 使用累计确认,所以接收方不向发送方发回否定确认,而是对最后一个正确接收报文段进行重复确认(即产生一个冗余 ACK)
如果 TCP 发送方接收到对相同报文段的 3 个冗余 ACK. 它就认为跟在这个已被确认过 3次的报文段之后的报文段已经丢失。一旦收到 3 个冗余 ACK,TCP 就执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。
5 流量控制
前面讲过,一条 TCP 连接双方的主机都为该连接设置了接收缓存。当该 TCP 连接收到正确、按序的字节后,它就将数据放入接收缓存。
相关联的应用进程会从该缓存中读取数据,但没必要数据刚一到达就立即读取。事实上,接收方应用也许正忙于其他任务,甚至要过很长时间后才去读取该数据。如果应用程序读取数据时相当缓慢,而发送方发送数据太多、太快,会很容易使这个连接的接收缓存溢出。
TCP 为应用程序提供了流量控制服务以消除发送方导致接收方缓存溢出的可能性。因此,可以说流量控制是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读速率相匹配。
前面提到过,TCP 发送方也可能因为 IP 网络的拥塞而被限制,这种形式的发送方的控制被称为拥塞控制(congestion control )。
TCP 通过让接收方维护一个称为接收窗口的变量来提供流量控制。接收窗口用于告诉发送方,该接收方还有多少可用的缓存空间。因为 TCP 是全双工通信,在连接两端的发送方都各自维护一个接收窗口变量。主机把当前的空闲接收缓存大小值放入它发给对方主机的报文段接收窗口字段中,通知对方它在该连接的缓存中还有多少可用空间。
6 TCP 连接管理
客户机中的 TCP 会用以下方式与服务器建立一条 TCP 连接:
第一步:客户机端首先向服务器发送一个 SNY 比特被置为 1 报文段。该报文段中不包含应用层数据,这个特殊报文段被称为 SYN 报文段。另外,客户机会选择一个起始序号,并将其放置到报文段的序号字段中。为了避免某些安全性攻击,这里一般随机选择序号。
第二步:一旦包含 TCP 报文段的用户数据报到达服务器主机,服务器会从该数据报中提取出 TCP SYN 报文段,为该 TCP 连接分配 TCP 缓存和控制变量,并向客户机 TCP 发送允许连接的报文段。这个允许连接的报文段还是不包含应用层数据。但是,在报文段的首部却包含3 个重要的信息。
首先,SYN 比特被置为 1。
其次,该 TCP 报文段首部的确认号字段被置为客户端序号+1
最后,服务器选择自己的初始序号,并将其放置到 TCP 报文段首部的序号字段中。这个允许连接的报文段实际上表明了:“我收到了你要求建立连接的、带有初始序号的分组。我同意建立该连接,我自己的初始序号是 XX”。这个同意连接的报文段通常被称为 SYN+ACK 报文段。