3 运输层
3.1 概述
运输层:为运行在不同主机上的应用进程 之间提供了**逻辑通信(logical communication)**功能。
(网络层:提供了主机之间的逻辑通信)
运输层分组(数据传输单元):报文段(segment)。
运输层协议:在端系统(而不是路由器)中实现,负责将来自应用程序的报文移动到网络边缘(网络层),或将网络层的数据报分发给各个进程端口,即多路复用与多路分解(基本服务)。
- TCP:可靠传输服务、面向连接的服务、流量控制、拥塞控制
- UDP:进程到进程的数据交付(不可靠、无连接)、差错检查
基于不同运输层协议的应用层协议
3.2 多路复用与多路分解
多路复用(multiplexing):在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(用于分解)生成报文段,然后将报文段传递到网络层。
多路分解(demultiplexing):将运输层中的数据交付到正确的套接字。
要求:
- 套接字有唯一标识符(一个进程可以有多个套接字)。
- 报文段中有字段(源端口号、目的端口号)用于指示该报文段要交付到的套接字。
通常,应用程序的客户端让运输层自动地(透明地)分配端口号,服务器端分配一个特定的端口号。
3.2.1 无连接的多路复用与多路分解
UDP套接字标识:二元组(目的IP地址,目的端口号)
不同源进程的报文段到达同一目的进程使用同一套接字。
3.2.2 面向连接的多路复用与多路分解
TCP套接字标识:四元组(源IP地址,源端口号,目的IP地址,目的端口号)
不同源进程的报文段到达同一目的进程使用不同套接字。
Web服务器使用端口号的方法:
- 为每一个用户连接创建一个具有新套接字的新进程。
- 只使用一个进程,为每个用户连接创建一个具有新套接字的新线程。
3.3 UDP
UDP(User Datagram Protocol,用户数据报协议)
工作流程
- UDP从应用程序得到数据,附加上首部字段,交付给网络层。
- 网络层将该运输层报文段封装到一个IP数据报中,尽力而为将其交付给接收主机。
- 若报文段到达接收主机,UDP将报文段中的数据交付给目的端口号对应的套接字。
特点
- 无连接:发送报文段之前,发送方和接收方的运输层实体没有握手。
- 不可靠 :提供差错检测,但无法进行恢复(丢弃受损报文段 或交给应用程序并发出警告 )。
- 注:使用UDP的应用仍可以通过在应用程序自身建立可靠性机制实现可靠数据传输。
优点
- 关于发送什么数据以及何时发送数据的应用层控制更为精细:TCP的拥塞控制机制可能遏制TCP发送方。
- 无须建立连接:建立TCP连接需要进行三次握手,时延较大。
- 无连接状态:不需要在端系统中维护连接状态(接受和发送缓存、拥塞控制参数、序号与确认号参数)。
- 分组首部开销小:仅8B,TCP报文段首部为20B+。
UDP报文段结构
- 首部(8B):源端口号(16b)、目的端口号(16b)、长度(首部+数据,16b)、检验和(16b)
- 应用数据(报文)
检验和(checksum)
- 在发送端,计算所有16bits字(除检验和字段)的检验和,填写在报文段的检验和字段。
- 在接收端,计算所有16bits字(包括检验和字段)的检验和,若没有差错,则结果为全1(1111111111111111)。
计算方法:
- 对两个16bits字做二进制加法,若有溢出(结果>16位),则将溢出部分与剩余部分做二进制加法,最终得到一个新的16bits字。
- 将得到的结果与第三个字做相同操作,以此类推,将所有字相加。
- 将最终结果按位取反(反码),即可得到检验和。
3.4 可靠数据传输原理
3.4.1 可靠数据传输协议(reliable data transfer protocol,rdt)
-
经完全可靠信道的可靠数据传输
rdt1.0:
-
rdt_send(data):接收到上层协议传入的数据data
-
make_pkt(data):将data打包成分组
-
udt_send(packet):将分组packet发送到信道中
-
rdt_rcv(packet):接收到下层协议传入的数据data
-
extract(packet, data):从分组packet中取出数据
-
deliver_data(data):将数据data上传给上层协议
-
-
经具有比特差错信道的可靠数据传输
rdt2.0:
自动重传请求(Automatic Repeat reQuest,ARQ)协议 :**停等(stop-and-wait)**协议(发送方处于等待ACK和NAK的状态时,不能从上层获得更多的数据)
-
差错检测:使用检验和等差错检测和纠错技术。
-
接收方反馈:若经过检测发现分组正确,则发送肯定确认ACK;若分组受损,则发送否定确认NAK。
-
重传:若发送方收到了NAK,则要重传分组。
- make_pkt(data, checksum):为提供差错检测服务,发送方在生成分组时应携带检验和或其他额外比特
- make_pkt(ACK/NAK):创建ACK/NAK分组
- isNAK(rcvpkt):判定rcvpkt是一个NAK
- isACK(rcvpkt):判定rcvpkt是一个ACK
- corrupt(rcvpkt):判定rcvpkt受损
- notcorrupt(rcvpkt):判定rcvpkt正确
rdt2.1:
-
ACK和NAK有可能受损:一旦发送方收到含糊不清的ACK或NAK时,只需重传当前数据分组即可(引入了冗余分组(duplicate packet),接收方无法判定接收到的分组是新的分组还是重传的分组)。
-
**序号(sequence number)**字段:发送方为每个分组分配一个序号,放在分组首部的序号字段中,接收方只需检查序号即可确定收到的分组是否为重传(接收方只要收到了连续两个序号相同的分组,就证明这是一个重传,发送方没有正确接收接收方的ACK或接收到了NAK)。
- make_pkt(seq, data, checksum):加入序号字段(seq=0/1)
- make_pkt(ACK/NAK, checksum):在ACK/NAK分组中加入检验和字段
- has_seq0/1(rcvpkt):判定收到的rcvpkt的序号是0/1
rdt2.2:
- 无NAK:在ACK分组中加入序号字段。
- make_pkt(ACK, seq, checksum):在ACK分组中加入序号字段,表明收到了seq=0/1的分组
- is_ACK(rcvpkt, seq):发送方收到了接收方对seq=0/1的分组的肯定确认
-
-
经具有比特差错的丢包信道的可靠数据传输
rdt3.0:
倒计时定时器(countdown timer):每次发送一个分组(新分组和重传分组)时,发送方启动一个定时器;超时(timeout)时,重传分组并重启定时器;接收到正确ACK后,终止定时器。
比特交替协议(alternating-bit protocol):rdt3.0的分组序号在0和1之间交替,故被称为比特交替协议。
rdt3.0接收方的FSM(有限状态机)与rdt2.2接收方相同
- start_timer/stop_timer:启动/重启/终止定时器
- timeout:超时
3.4.2 流水线(pipeline)可靠数据传输协议
rdt3.0的性能问题:停等(stop-and-wait)
流水线(pipeline):允许发送方发送多个分组而无需等待确认。
- 增加序号范围:每个传输中的分组必须有唯一的序号。
- 发送缓存和接收缓存:发送方要缓存已发送但未确认的分组,接收方可能也要缓存已正确接收的分组。
3.4.2.1 回退N步(Go-Back-N,GBN)
发送方看到的GBN协议序号范围
- 窗口长度N:流水线中未确认的分组数不能超过N,GBN协议也称为滑动窗口协议(sliding-window protocol)。
- 基序号(base):最早未确认分组的序号。
- 下一个序号(nextseqnum):最小的未使用序号(下一个待发分组序号)。
发送方需要响应的事件
- 上层的调用:先检查发送窗口是否已满,若未满,则生成分组并发送,然后更新相关变量;若已满,则将数据返回给上层/缓存数据/仅在窗口未满时允许上层调用。若发送的分组序号等于基序号(最早的已发送但未确认分组),则启动定时器。
- 收到ACK:若收到ACK但仍有未确认分组,则重启定时器;若无未确认分组,则终止定时器。
- 超时:重传所有已发送但未确认的分组。启动定时器。
接收方的操作
- 接收方对序号为n的分组的确认采用累积确认(cumulative acknowledgment),表明接收方正确接收到序号为n及n以前的所有分组。
- 若正确接收一个按序分组n,则发送ACK n;若接收到失序分组,则丢弃该分组(该分组将因超时被重传),无需缓存任何失序分组,继续发送上一个ACK。
3.4.2.2 选择重传(Selective Repeat,SR)
GBN的缺点:大量不必要重传的分组可能充斥整条流水线。
选择重传:发送方仅重传那些它怀疑在接收方出错(丢失/受损)的分组,从而避免不必要的重传。
接收方采用**选择确认(selective acknowledgment)**对正确接收的分组进行确认:确认所有正确接收的分组,不论其是否按序。失序的分组将被缓存,直到它前面的所有分组都被正确接收为止,它才会被按序交付给上层。
SR中发送方和接收方看到的窗口是不同的:若序号空间过小,则接收方无法确定收到的分组是新的还是重传的,序号空间大小必须大于等于窗口长度的二倍。
发送方的操作
- 从上层收到数据:发送方检查下一个可用序号是否在窗口内,若在,则生成分组并发送;否则,返回上层或缓存数据。
- 超时:每个分组拥有自己的逻辑定时器,用定时器防止丢包。
- 收到ACK:若ACK序号在窗口内,则将该分组标记为已接收,若序号等于基序号(send_base),则窗口前移至基序号等于最小未确认分组序号处。
接收方的操作
- 正确接收窗口内的分组n:发送ACK n,若该分组没收到过,则缓存该分组,若分组序号等于基序号(rcv_base),则将该分组及其连续分组交付给上层,并将窗口前移至基序号等于最小未接收分组序号处。
- 正确接收前一个窗口内的分组n:发送ACK n。
- 其他:忽略该分组。
3.5 TCP:面向连接、可靠传输、流量控制
3.5.1 面向连接(connection-oriented)
连接(connection)
- 是一条逻辑连接:共同状态仅保留在两个通信端系统的TCP程序中,不是一条物理电路。
- 全双工(full-duplex):数据可以在两个进程间相互流动。
- 点对点(point-to-point):单个发送方对单个接收方,不能多播(一对多)。
- 组成:两台主机中的发送缓存和接收缓存、进程的套接字、变量。
建立连接------三次握手(three-way handshake)
- 客户向服务器发起连接:发送一个特殊的TCP报文段(SYN segment),其中字段SYN=1,随机选择一个初始序号(client_isn)放置于序号字段,不包含应用层数据。
- 服务器向客户表示允许:服务器为该连接分配缓存和变量,并发送允许报文段(SYNACK segment),其中字段SYN=1,确认号字段=client_isn+1,随机选择一个初始序号(server_isn)放置于序号字段,不包含应用层数据。
- 客户向服务器确认收到:客户为该连接分配缓存和变量,并发送一个报文段,其中字段SYN=0,确认号字段=server_isn+1,序号字段=client_isn+1,可以包含应用层数据。
关闭连接------四次挥手(four-way wavehand)
假设客户(服务器也可以发起关闭连接)决定关闭连接
-
客户通知服务器关闭连接:发送一个特殊的TCP报文段,其中字段FIN=1。
-
服务器确认收到:发送响应TCP报文段,其中字段FIN=0,ACK=1。
(服务器等待一段时间,直到所有报文传输完毕再告知客户关闭连接)
-
服务器告知客户关闭连接:发送一个特殊的TCP报文段,其中字段FIN=1。
-
客户确认收到:发送响应TCP报文段,其中字段FIN=0,ACK=1。
(客户等待一段时间,若确认报文丢失,则重传确认报文,直到没有一段时间内没有服务器传来的报文,释放资源;服务器一旦收到客户的确认报文,则释放资源)
3.5.2 可靠传输(reliable transfer)
TCP报文段结构
-
源端口号(16b)/目的端口号(16b)
-
序号(32b):报文段首字节的字节流编号
-
确认号(32b):表示接收方期望收到的下一字节的序号,因此TCP是提供累积确认的
-
首部长度(4b):首部中有不定长的选项字段(options),当其为空时,首部长度为20B
-
SYN(1b)/FIN(1b):连接管理
-
接收窗口(16b):流量控制
-
检验和(16b):差错检测
往返时间与超时间隔
- 往返时间(RTT):从一个报文段发出到它被确认的时间
- 超时间隔(TimeoutInterval):必须大于该连接的往返时间
- 样本RTT(SampleRTT):测得的一个实际RTT,但不测量重传报文段的样本RTT
- 估算RTT(EstimatedRTT) :样本RTT的指数加权移动平均(Exponential Weighted Moving Average,EWMA)
- RTT偏差(DevRTT):样本RTT偏离估算RTT的程度
一旦测得了一个新的SampleRTT,TCP根据以下公式更新EstimatedRTT和DevRTT,其中 α 推荐值为 0.125,β 推荐值为 0.25
EstimatedRTT=(1-\\alpha)\\ EstimatedRTT+\\alpha \\ SampleRTT
DevRTT=(1-\\beta)\\ DevRTT+\\beta\\ \|SampleRTT-EstimatedRTT\|
若已知EstimatedRTT和DevRTT,可由以下公式得出TimeoutInterval的值,其中推荐的初始TimeoutInterval值为1s
TimeoutInterval=EstimatedRTT+4\\ DevRTT
可靠数据传输
/* 假定发送方不受流量控制和拥塞控制的限制,来自上层的数据长度小于MSS(最大报文段长度),且数据传输单向进行 */
NextSeqNum=InitialSeqNumber
SendBase=InitialSeqNumber
loop (forever) {
switch(event)
event: data received from application above
create TCP segment with sequence number NextSeqNum
if (timer currently not running)
start timer
pass segment to IP
NextSeqNum=NextSeqNum+length(data)
break;
event: timer timeout
retransmit not-yet-acknowledged segment with smallest sequence number
start timer
break;
event: ACK received, with ACK field value of y
if (y > SendBase) { //累积确认
SendBase=y
if (there are currently any not-yet-acknowledged segments)
start timer
}
break;
} /* end of loop forever */
-
发送方不会重传序号小于收到的ACK号的任何报文段。
-
若发生重传,则TCP将下一次超时间隔设置为原值的2倍,而不使用EstimatedRTT和DevRTT推算的值,这种修改提供了一个形式受限的拥塞控制。
-
快速重传(fast retransmit)
-
长超时周期迫使发送方延迟重传丢失的分组,增加了端到端时延。
-
机制:收到三个冗余ACK (即连续出现四个相同的ACK),则快速重传序号为ACK的确认号的分组。
-
可以使用以下伪代码替换上述循环分支中的第三个事件:
event: ACK received, with ACK field value of y
if (y > SendBase) {
SendBase=y
if (there are currently any not yet acknowledged segments)
start timer
}
else {/* a duplicate ACK for already ACKed segment /
increment number of duplicate ACKs received for y
if (number of duplicate ACKS received for y==3)
/ TCP fast retransmit */
resend segment with sequence number y
}
break;
-
3.5.3 流量控制(flow-control)
流量控制:使发送方的发送速率与接收方的读取速率相匹配,消除发送方使接收方缓存溢出的可能性。
- rwnd(receive window,接收窗口):发送方维护这一变量,表示接收方还有多少可用的缓存空间。
- RcvBuffer(接收方的接收缓存)
- LastByteRead(接收方从缓存读取的最后一个字节编号)
- LastByteRcvd(接收方接收到并放入缓存的最后一个字节编号)
- LastByteSend(发送方已发送的最后一个字节编号)
- LastByteAcked(发送方收到确认的最后一个字节编号)
接收方 通过以下公式计算出当前rwnd值,并将其放入发给发送方的报文段的接收窗口字段中
LastByteRcvd-LastByteRead\\leq RcvBuffer
rwnd=RcvBuffer-\[LastByteRcvd-LastByteRead\]
发送方 通过以下公式确保未确认的数据量不大于接收方的接收窗口
LastByteSend-LastByteAcked\\leq rwnd
当接收方rwnd=0时,发送方继续发送只有一个字节数据的报文段,接收方将会确认这些报文段。直至接收方rwnd>0,它将发送一个包含非零rwnd值的确认报文段,此时接收方可以继续传输数据。
3.6 拥塞控制原理
流量控制(flow-control):使发送方的发送速率与接收方的读取速率相匹配,消除发送方使接收方缓存溢出的可能性。
拥塞控制(congestion control):TCP发送方因IP网络拥塞而被遏制,防止发送方在网络拥塞时发送大量数据致使网络更加拥塞(路由器缓存溢出)。
网络拥塞代价
- 分组到达速率接近链路容量时,分组经历巨大的排队时延。
- 发送方必须重传分组,以补偿因缓存溢出而被丢弃的分组,在遇到大时延时,发送方可能会进行不必要的重传,使拥塞更加严重。
- 当一个分组沿一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组而使用的传输容量被浪费掉了。
拥塞控制方法
- 端到端拥塞控制:TCP通过超时 和三个冗余ACK 得知网络可能拥塞(还可能通过增加的往返时延得知拥塞的程度),从而减小拥塞窗口长度,进行端到端拥塞控制。
- 网络辅助的拥塞控制:路由器向发送方提供关于网络中拥塞状态的显示反馈信息。
3.7 TCP:拥塞控制
拥塞窗口(congestion window,cwnd):发送方中未确认的数据量小于等于min(cwnd, rwnd)
简化:仅考虑接收缓存足够大的情况(忽略rwnd的限制)
发送方的发送速率约等于cwnd/RTT
发送方判定丢包(网络拥塞):超时 、三个冗余ACK
TCP拥塞控制算法
-
慢启动(slow start)
-
初始化cwnd值为1个MSS(最大报文段长度)
-
每当已发送的报文段首次被确认,cwnd = cwnd + MSS,因此慢启动状态cwnd呈指数增长
-
超时:ssthresh = cwnd / 2(ssthresh为慢启动阈值),cwnd = 1MSS
-
达到阈值ssthresh:进入拥塞避免状态
-
检测到三个冗余ACK:ssthresh = cwnd / 2,cwnd = cwnd + 3MSS,执行快速重传 ,然后进入快速恢复状态
-
-
拥塞避免(congestion avoidance)
- 每经过一个RTT,cwnd = cwnd + MSS,cwnd呈线性增长(实现:每收到一个新的确认,cwnd = cwnd + MSS (MSS / cwnd) )
- 超时:ssthresh = cwnd / 2,cwnd = 1MSS,进入慢启动状态
- 检测到三个冗余ACK:ssthresh = cwnd / 2,cwnd = cwnd + 3MSS,执行快速重传 ,然后进入快速恢复状态
-
快速恢复(fast recovery)
-
每再次收到一个冗余ACK,cwnd = cwnd + MSS,cwnd呈指数增长
-
当收到需要的ACK,cwnd = ssthresh,进入拥塞避免状态
-
超时:ssthresh = cwnd / 2,cwnd = 1MSS,进入慢启动状态
-
快速恢复是TCP推荐的而非必需的构件:Reno版TCP在收到三个冗余ACK时使用快速恢复,超时时进入慢启动状态,而Tahoe版TCP在超时和收到三个冗余ACK时均进入慢启动状态
-
-
FSM
-
加性增、乘性减(Additive-Increase Multiplicative-Decrease,ACMD)
- 忽略一条连接开始时的慢启动状态,假定丢包由三个冗余ACK而不是超时指示
- 拥塞避免状态中,每经过1个RTT,cwnd增加1个MSS,即线性(加性)增加
- 检测到三个冗余ACK时,cwnd减半,即乘性减