传输层向上层提供的服务是进程和进程之间的以报文为单位的逻辑的通信。使不同进程的主机好像直接相连一样,如下图:

运输层协议是在端系统中实现的,而不是在路由器中实现的。
- 应用层通过SAP向传输层交付报文
- 传输层将报文划分成较小的报文段,每段都加上运输层首部以生成运输层报文段
- 在发送端系统中,将报文段交付给网络层
- 网络层将报文段封装成数据报发送给目的端
传输层提供了进程间的逻辑通信,网络层提供了主机之间的逻辑通信。
传输层的服务依赖于网络层的服务,还要加强网络层的服务为应用层提供更好的服务
服务加强的例子:
- IP提供的服务是不可靠的和不保序的,传输层的TCP协议在IP服务上提供了可靠的、保序的传输。
- 网络层提供的是主机与主机之间的逻辑通讯,传输层在网络层提供的服务之上,通过复用和解复用提供了进程之间的逻辑通讯。
.
注意,有些服务是不可以被加强的:带宽,延迟。
【UDP是去掉了握手的时间,提高了实时性; 而不是通过提升带宽来提升速度。】
这里举一个形象的例子:
在东海岸和西海岸有2个家庭,每个家庭里各有12个小孩,他们每周都会互相写信;每封信都会通过单独的信封通过邮局服务传送。
每个家庭都有一个管事的小孩,东海岸的是Ann,西海岸的是Bill。Ann负责收集他们家的信件,Bill也一样;Ann和Bill收集到信封后,就会将信封交给每天都会来到家门口的提供邮政服务的邮车上。
当邮车送信件到家门口时,也是Ann和Bill负责去统一拿信,再具体交付到自家小孩的手里。
再在上面的例子中:

可以看到Ann和Bill为家庭的小孩提供了复用解复用的服务;且Ann和Bill都是在家里进行工作的,所以运输层协议只运行在端系统,它只负责将应用层报文交付到网络边缘(来家门口的邮车)。
在邮寄过程中,邮车是不会对信封内容进行查看的;即中间路由器即不处理也不识别传输层加在应用层报文中的任何信息。
Ann和Bill提供的服务可能会存在差别,Ann比较勤快,会去查询信件的邮寄情况,当信件没有按时送到会使用信件副本再次邮寄【TCP】;而Bill比较懒,放到邮车上就行,是否送达到东海岸并不关心,所以邮件可能会出现丢失【UDP】。
提供的服务:
TCP、UDP都不提供的服务:
- 延时保证
- 带宽保证
一个进程有一个或多个套接字(socket),传输层相当于从网络向进程传递数据和从进程向网络传递数据的门户。

多路分解:
上图中间的主机的运输层将网络层收到的报文段分解后交给应用层的
P
1
P_1
P1或
P
2
P_2
P2进程;这一过程是通过将到达的报文段数据定向到对应进程的套接字完成的。
即Ann将收到的信件,按照信件上的信息(socket)分发给指定的小孩的过程。
多路复用:
从多个套接字接收来自多个进程的报文,根据套接字对应的IP地址和端口号等信息对报文段用头部加以封装
即Ann收集信件时,按照发信人的信息来填写信封信息的过程。

如上图,左边的应用进程通过SAP向传输层传递socket和message,TCP通过socket获取该message的源端口和目的端口,并封装成TCP报文段。
传输层将封装好的报文段传递给网络层,网络层通过socket获取该message的源IP和目的IP,并封装成IP数据报,通过网卡发送出去。
以上就是发送端的复用过程,根据上层传递的socket封装信息。
当右边的主机收到了IP数据报,根据数据报格式可以分解出源IP和目的IP,然后向上层传递;传输层同理分解出源端口和目的端口;这个四元组可以指向某一个socket,从而将数据发送给socket对应的PID(进程号)。
注意:
在分解时,只要四元组中的其中一个不同,都会指向不同的socket号。
.
如下图,中间的主机C向Web服务器B发起了两个HTTP会话,这两个会话源IP、目的IP、目的端口号都是一样的(都跑在服务器的80号端口上);但只因为源端口号不一样,则在Web服务器B多路分解时指向了不同的socket,所以Web服务器B能够正常的分解这个源IP、目的IP、目的端口号的连接。
.

如上图,左边的应用进程通过SAP向传输层传递socket、message和&cad(指向目标IP和目标Port的指针),UDP通过socket和&cad获取该message的源端口和目的端口,并封装成UDP报文段。
传输层将封装好的报文段传递给网络层,网络层通过socket获取该message的源IP和目的IP,并封装成IP数据报,通过网卡发送出去。
以上就是发送端的复用过程,根据上层传递的socket和&cad封装信息。
当右边的主机收到了IP数据报,根据数据报格式可以分解出源IP和目的IP,然后向上层传递;传输层同理分解出源端口和目的端口;得到的二元组(目的IP和目的Port)可以指向某一个socket,从而将数据发送给socket对应的PID(进程号)。
TCP是四元组,一个sock只能使服务器与一个客户端连接。但UDP是二元组,一个服务器的sock可以同时与多个客户端进行交互。
.
如上面举的Web服务器B的例子,若传输层使用的是UDP协议,那么对于目的IP和目的Port相同的主机A和主机C的请求,都会被同一个进程所响应。
UDP在IP之上仅添加了复用/分用功能及少量的差错检验。
所以应用程序使用的是UDP协议,则该程序差不多是直接与IP打交道;而IP提供的是“尽力而为”的服务,所以UDP提供的也是“尽量而为”的服务。
UDP提供的是==“尽力而为”==的服务,所以报文段可能:
(1) 丢失;
(2)送到应用进程的报文段乱序。
UDP是无连接的:
(1)发送报文段之前,UDP发送端和接收端之间没有握手;
(2)每个UDP报文段都被独立地处理
UDP 被用于:
(1)流媒体(丢失不敏感,速率敏感、应用可控制传输速率);
(2)DNS;
如果希望运用udp的高效率,又希望它可靠。那只能在应用层层面上去解决可靠性问题。
.
如:QUIC,一种基于UDP的低时延的互联网传输层协议。QUIC基于UDP传输数据,在应用层面解决可靠性问题,同时又能享受UDP协议的低时延优点。

源端做校验和的计算,目的端做校验和判断,判断UDP报文段从源端到达目的端的移动时,其中的比特是否发生了改变,改变了则丢弃。
检验流程:

注意:
结果为0表示传输过程中必然出现了差错,结果为全1不能表示过程中一定没出现差错,只能表示大概率没出错。
可靠数据传输(rdt)在应用层、传输层和数据链路层都很重要。
rdt的任务:
在下层提供的服务是不可靠的情况下,本层的协议机制/实体要靠哪些时空资源的安排、要靠哪些机制的安排,向上层提供可靠的服务。
下图是本小节学习可靠性传输的整体框架
等待上层原语的调用,rdt规定本层的协议动作确保可靠性传递数据,然后对下层的服务进行数据的发送
发送端:
上层通过调用rdt_send(),将数据向下层传递,本层协议通过一系列操作后(确保可靠性的操作),将数据打包成packet,通过调用函数udt_send()使用下层提供的服务发送给接收端。【udt表示不可靠数据传输】
接收端:
分组从信道到达接收端时,rdt协议将调用rdt_rcv()接收发过来的packet,通过一系列确保可靠性服务的操作后,调用dliver_data()将数据向高层交付。
我们将下层的服务抽象成一个信道,在使用信道服务的基础上实现rdt协议。下面将讨论在不同信道请情况下,rdt协议的实现方式。
发送方:
调用rdt_send()触发rdt_send(data)事件,通过make_pkt(data)动作产生一个包含数据的分组,并通过udt_send(packet)动作将分组发送到信道上。
接收端:
通过rdt_rcv(data)事件从底层信道接收一个分组,通过extract (packet,data)动作从分组中取出数据,并通过deliver_data(data)动作上交给高层。

因为信道是可靠的,所以rdt协议只需要在使用下层可靠的服务基础上实现数据的分组封装和解封装。
问题:怎样从差错中恢复。
举个例子:
打电话,当对方听清了你说的话,会回复你一个“OK”,表示正确接收了;当对方没有听清你说的话,会回复你“不OK”,要求你在重说一次;
在计算机网络中,上述的过程可被称为自动重传请求协议(ARQ):
rdt2.0协议的FSM描述如下:

发送方:
调用rdt_send()触发rdt_send(data)事件,通过make_pkt(data)动作产生一个包含数据的分组,并通过udt_send(packet)动作将分组发送到信道上。
接下来发送方协议等待来自接收方的ACK或NAK分组。
若收到NAK分组触发rdt_rcv(rcvpkt)&&isNAK(rcvpkt)事件,并调用udt_send(sndpkt)将分组重传;
若收到ACK分组触发rdt_rcv(rcvpkt)&& isACK(rcvpkt)事件,回到初始状态等待上层的调用。
接收方:

通过rdt_rcv(data)事件从底层信道接收一个分组,通过校验和判断分组中的比特是否发生翻转。
若发生翻转,则触发rdt_rcv(rcvpkt)&&corrupt(rcvpkt)事件,采取udt_send(NAK)动作,向发送方发送NAK分组。
若没发生翻转,则触发rdt_rcv(rcvpkt)&¬corrupt(rcvpkt)事件,采取extract (packet,data)动作从分组中取出数据,然后通过deliver_data(data)动作上交给高层,并向发送方发送ACK分组【udt_send(ACK)】。
rdt2.0协议发送完一个数据,等待回复后才会发送下一个数据;这样的协议被称为停等(stop-and-wait)协议
rdt2.0的致命缺陷:没有考虑到ACK或NAK分组受损的可能性。
考虑处理受损ACK和NAK的三种可能性:
对于第三种可能,它向接收方的信道中引入了冗余分组;这会使接收方在收到冗余分组时,不能判断接收到的分组是新的分组还是旧分组的重传。
为了解决上面的问题,在数据分组中添加序列号,两个序列号(0,1)就足够了。【足够表示新旧分组,所以rdt2.1相比rdt2.0添加了序号。】
因为假设下层信道是不会丢失分组的,所以对于ACK\NAK不需要指明它们需要确认的分组序号。发送方会知道ACK\NAK是响应最近发送的分组的。
rdt2.1的运行图:

rdt 2.1协议的FSM描述如下:

发送方:
初始状态是序号为0的状态,当发送序号为0的的数据分组后,发送方会处于序号0的状态并等待ACK/NAK的到来,即上图右上角的状态。当发送方收到受损的ACK/NAK,会再次序号0数据分组,并继续处于序号0的状态,等待ACK/NAK的到来。
当收到了正确的ACK/NAK,发送方就会进入序号为1的状态并等待上层向接收方发送序号1数据分组的调用。状态为序号1和状态为序号0的发送过程一致。

接收方:
初始状态为序号0,等待序号0的数据分组的到来;
当收到数据,检验后未出错,并且序号为0,则向高层提交数据并向发送方发送ACK。然后状态转到序号1;
当收到数据且检验后出错,则向发送方发送NAK,并继续处于序号0状态。
当收到数据,检验后未出错,但是序号为1,则表示接收方收到了旧的分组,则向发送方发送ACK,让发送方发送新的序号为0的分组。
状态为1时与上述类似。
rdt2.1的运行图:

rdt2.2功能同rdt2.1,但只使用ACK(ack 要编号)。接收方对最后正确接收的分组发ACK,以替代NAK,如下图:

例如:
当发送方已经发送了序号为1的分组,且收到ACK0;则表示接收方最近正确接收的分组序号为0,序号为1的分组可能发生了损坏,发送方需要重传序号为1的分组;因此ACK0代替了NAK1。
rdt2.2运行图:

rdt2.2已经通过序号、检验和、ACK分组和重传解决了信道比特受损的问题。
但以上措施不足与解决下层信道丢失分组的问题;
如当发送方发送了一个数据分组,接收方接收并返回一个ACK,但ACK在传输过程中出现了丢失,那么发送方就会持续等待这个ACK,而接收方会持续等待发送方的下一个分组,从而造成了死锁。
所以rdt3.0在rdt 2.2的基础上引入了超时重传机制:发送方等待ACK一段合理的时间(需要一个倒计数定时器,发送后开始计时),如果到时没有收到ACK,发送端超时重传数据分组。
也许ACK并没有丢失,只是被延迟了。超时重传将会导致数据重复,但利用序列号已经可以处理这个问题。
rdt 3.0协议的FSM描述如下:

rdt3.0的运行图:

当超时计时器设置的时间过短时:
过早超时(延迟的ACK)也能够正常工作;但是效率较低,一半的分组和ACK是重复的;所以设置一个合理的超时时间也是比较重要的;
rdt 3.0性能问题的核心在于它是个停等协议。
假设有台端系统正在通信,两个端系统之间的往返传播时延RTT为30毫秒。彼此通过一条发送速率R为1Gbps的信道相连。现发送一条分组长度L为1000字节(8000比特)的数据分组,假设ACK分组很小,可以忽略发送时间。
则发送方的信道利用率为:
U
s
e
n
d
e
r
=
将
比
特
送
进
信
道
的
时
间
发
送
时
间
=
L
/
R
R
T
T
+
L
/
R
U_{sender}=\frac{将比特送进信道的时间}{发送时间}=\frac{L/R}{RTT+L/R}
Usender=发送时间将比特送进信道的时间=RTT+L/RL/R
图例如下图所示:

可以看到信道利用率非常的低
rdt 3.0在可靠性上已经达到了要求,但是在效率上很难满足现在的需求。
rdt 3.0 好比在一条很长的高速公路上运输货物,因为是停等协议,所以一次性只能发一辆货车,这条马路在很长一段时间内只有一辆车在跑;而且还要等目的端的卡车跑回来说已经送达了才能发出下一辆,从而导致高斯公里的利用率很低。
.
所以为什么不一次性发几辆车呢?这就是后面的流水线协议。
所以解决rdt 3.0的方法是:不以停等方式运行,允许发送方发送多个分组而无需等待确认。
发送方一次性向接收方发送的多个分组可以看成是多个分组填充到了一条水流中去,故该技术被称为流水线技术。
如下图,在等待确认之前发送3个报文,其利用率也基本上提高3倍。

实现流水线技术需要在原本的可靠数据传输协议之上实现:

发送窗口与发送缓冲区的关系如下图:
【发送窗口是发送缓冲区的一个子集】

绿色部分表示缓冲区,存放着要发送给接收方的数据。
发送窗口由窗口前沿和后沿,前沿和后沿圈出得蓝色部分即发送窗口,表示0号数据分组已经发送但未收到确认。
即发送窗口是指已发送但未收到确认的分组。
而落在绿色的发送缓冲区的1~4号数据分组可以发送给接收方。
发送窗口的移动:
如下图,发送0号分组前:
如下图,发送0号分组后,前沿前移
如下图,收到已发送的0号分组的ACK前:
如下图,收到已发送的0号分组的ACK后:
因为0号分组是缓冲区的“前沿”,所以缓冲区窗口整体向前移

G B N GBN GBN发送窗口 > 1 >1 >1,接收窗口 = 1 =1 =1。
因为接收窗口=1,所以当收到分组后就直接交付给上一层。所以不允许乱序接收,只能按序接收。
正常情况下 G B N GBN GBN的2个窗口互动:

异常情况下 G B N GBN GBN的2个窗口互动:

G
B
N
GBN
GBN发送方的
F
S
M
FSM
FSM:

①
\color{red}{①}
①、初始状态,窗口后沿
b
a
s
e
=
1
base=1
base=1,窗口前沿
n
e
x
t
s
e
q
n
u
m
=
1
nextseqnum=1
nextseqnum=1,
b
a
s
e
=
=
n
e
x
t
s
e
q
n
u
m
base==nextseqnum
base==nextseqnum即当前窗口大小为0。
【在真实情况中,发送窗口的序号是双方约定的,不一定从1开始】
② \color{red}{②} ②、
③ \color{red}{③} ③、
④ \color{red}{④} ④、
⑤ \color{red}{⑤} ⑤、
G
B
N
GBN
GBN接收方的
F
S
M
FSM
FSM:

① \color{red}{①} ①、
③ \color{red}{③} ③、
正常情况下 S R SR SR的2个窗口互动:
异常情况下 S R SR SR的2个窗口互动:
选择重传SR的运行:

点对点:
一个发送方,一个接收方
提供可靠的、按顺序的字节流服务
没有报文边界的理解:
- 对于UDP:
发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。因此,应用程序必须选择合适大小的报文。- 对于TCP:
TCP不像UDP一样那样一个个报文独立地传输,而是在不保留报文边界的情况下以字节流方式进行传输。
TCP头部占20字节,以太网最大传输单元(MTU)为1500字节,所以TCP对上层交付的报文拆解成最大报文长度(MSS)为1480字节的报文。
流水线协议
具有发送和接收缓存

全双工通信
在同一连接中数据流双向流动
面向连接
在数据交换之前,通过握手(交换控制报文)初始化发送方、接收方的状态变量
有流量控制
发送方不会淹没接收方

TCP对于接收方如何处理乱序的报文段没有规定:
丢弃或者保存都可以
在大型网络中,传播时间是很分散的,如下图:

将 R T T RTT RTT设置在红色的位置, R T T RTT RTT太短,会导致太早超时,引起不必要的重传。
将 R T T RTT RTT设置在蓝色的位置, R T T RTT RTT太长,会导致对报文段丢失反应太慢,消极。
那么如何估计 R T T RTT RTT呢?
可以进行采样,发出一条报文段,然后测量从报文段发出到收到确认的时间,作为
S
a
m
p
l
e
R
T
T
SampleRTT
SampleRTT(如果有重传,忽略此次测量)。
但由于路由器的拥塞和端系统负载的变化,这些报文段的
S
a
m
p
l
e
R
T
T
SampleRTT
SampleRTT会随之波动。所以,任何
S
a
m
p
l
e
R
T
T
SampleRTT
SampleRTT的值都是非典型的;因此,为了获取一个典型的
R
T
T
RTT
RTT,这里采用加权求和的方式来得到平滑的
R
T
T
RTT
RTT预估值:
E
s
t
i
m
a
t
e
d
R
T
T
=
(
1
−
α
)
∗
E
s
t
i
m
a
t
e
d
R
T
T
+
α
∗
S
a
m
p
l
e
R
T
T
EstimatedRTT = (1- α)*EstimatedRTT + α*SampleRTT
EstimatedRTT=(1−α)∗EstimatedRTT+α∗SampleRTT

除了估算
R
T
T
RTT
RTT外,测量
R
T
T
RTT
RTT的变化也是有价值的。这里定义了
R
T
T
RTT
RTT偏差
D
e
v
R
T
T
DevRTT
DevRTT,用于估算
S
a
m
p
l
e
R
T
T
SampleRTT
SampleRTT偏离
E
s
t
i
m
a
t
e
d
R
T
T
EstimatedRTT
EstimatedRTT的程度:
D
e
v
R
T
T
=
(
1
−
β
)
∗
D
e
v
R
T
T
+
β
∗
∣
S
a
m
p
l
e
R
T
T
−
E
s
t
i
m
a
t
e
d
R
T
T
∣
DevRTT = (1-β)*DevRTT +β*|SampleRTT-EstimatedRTT|
DevRTT=(1−β)∗DevRTT+β∗∣SampleRTT−EstimatedRTT∣
D
e
v
R
T
T
DevRTT
DevRTT代表当前网络的波动程度;当
D
e
v
R
T
T
DevRTT
DevRTT较大时,应该设置较大的安全边界时间,即将
R
T
T
RTT
RTT预估值调大,以适应波动。所以最后的超时时间间隔设置为:
T
i
m
e
o
u
t
I
n
t
e
r
v
a
l
=
E
s
t
i
m
a
t
e
d
R
T
T
+
4
∗
D
e
v
R
T
T
TimeoutInterval = EstimatedRTT + 4*DevRTT
TimeoutInterval=EstimatedRTT+4∗DevRTT
可以理解前面的加权平均得到的 E s t i m a t e d R T T EstimatedRTT EstimatedRTT表示为均值, D e v R T T DevRTT DevRTT看作方差。假设大型网络中的往返时长符合正态分布,那么将值设置为[均值+4*方差]可以满足99%以上的设备之间的往返时长。
T C P TCP TCP是在 I P IP IP不可靠的尽力而为上创建了一种可靠数据传输服务。
但 T C P TCP TCP与 G B N GBN GBN和 S R SR SR都存在不同,可以看作 G B N GBN GBN和 S R SR SR的组合。
T C P TCP TCP:
下图是忽略重复的确认、流量控制和拥塞控制得到的简化版的 T C P F S M TCP\ FSM TCP FSM:

①
\color{red}{①}
①、
初始状态,发送窗口前沿和后沿等于发送双方约定的一个初始序号;
②
\color{red}{②}
②、
从上层应用层程序接收到数据,并生成具有序号
N
e
x
t
S
e
q
N
u
m
NextSeqNum
NextSeqNum的报文段,然后将报文段交付给
I
P
IP
IP层;
发送窗口前沿移动
l
e
n
g
t
h
(
d
a
t
a
)
length(data)
length(data)个字节;
若此时计时器没有运行,启动计时器;
③
\color{red}{③}
③、
若发生了
t
i
m
e
o
u
t
timeout
timeout(定时器超时),重传具有最小序号但任未答应的报文段,并重新启动计时器;
④
\color{red}{④}
④、
若收到
A
C
K
ACK
ACK,且具有
A
C
K
ACK
ACK字段值
y
y
y;
当
y
y
y大于发送窗口后沿,因为是累计确认,所以将窗口后沿移动到
y
y
y;
若此时没有任何应答报文段,启动定时器。

【下述例子种,蓝色代表已到达;白色代表还未收到】
第一种情况:按序到达;
如下图,当报文段
1
1
1按序到达,
0
0
0以及以前的报文段都已经发送了
A
C
K
ACK
ACK;
此时接收方最多停留500
m
s
ms
ms等待序号
2
2
2报文段的到达;若时间间隔内未到,发送
A
C
K
2
ACK2
ACK2,表示2之前的都收到了,希望下一个发送序号为2的报文段;

第二种情况:
报文段按序到达,在500
m
s
ms
ms内下一个报文段也按序到达,立即发送单个累计
A
C
K
ACK
ACK,即
A
C
K
3
ACK3
ACK3,表示3号之前的报文段我都收到了,希望下一个发送序号为3的报文段。

第三种情况:乱序到达;
立即发送
A
C
K
2
ACK2
ACK2,指明下一个期待的报文段序号是2;

第四种情况:
若第三种情况中间的2报文段到达了,填充了间隔的低端,则立即发送
A
C
K
4
ACK4
ACK4。
超时触发重传存在的问题之一是超时周期可能会相对比较长。当报文段出现了对丢失,这种长超时周期会增加端到端的时延。
幸运的是,发送方可以通过冗余ACK来提前检测到丢包情况。
冗余 A C K ACK ACK即再次确认某个报文段的 A C K ACK ACK;根据上一小节,当出现情况3时,即出现中间间隔时会发送冗余的 A C K ACK ACK;
这个间隔可能是由于在网络中报文段丢失或者重新排序造成的。因此冗余的 A C K ACK ACK可以看作报文段丢失的一个信号。
如果发送方收到同一数据的 3 \color{red}{3} 3个冗余 A C K ACK ACK,就会立即重传最小序号的段,不用等待定时器过时。
注意:
- T C P TCP TCP采用的是累计确认,所以3次冗余ACK都是对同一个报文段的确认。
- 总的来说,发送方收到了 4 4 4次(第1次正常的确认和后面3次冗余的确认)相同报文段的 A C K ACK ACK确认才会启动快速重传。
流量控制:
接收方控制发送方,不让发送方发送的太多、太快以至于让接收方的缓冲区溢出。
T C P TCP TCP双方在正式交换数据之前,发送方和接收方握手建立通信关系:
采用 2 2 2次握手建立连接可行吗?
先来看看正常的 2 2 2次握手建立连接:
类似小明和小红要进行聊天,彼此之间不知道对方是否想要进行聊天;小红先向小明发出询问:“让我们一起聊天吧”,小明收到小红的询问后向小红回话:“好的”。此次小红和小明之间的双向连接就建立了起来。如下图:

但是上面的情况是基于下层可靠的信道(不丢失、不乱序)下进行的。
下面我们基于现实中的信道讨论2次握手的失败场景:
场景1:半连接(只在服务器端维护了连接!)
客户端向服务器端发出
T
C
P
TCP
TCP连接请求
r
e
q
_
c
o
n
n
(
x
)
req\_conn(x)
req_conn(x),服务器端收到请求后同意连接并响应请求
a
c
c
_
c
o
n
n
(
x
)
acc\_conn(x)
acc_conn(x);
一段时间后,客户端还没收到服务器的响应,重新发送连接请求
r
e
q
_
c
o
n
n
(
x
)
req\_conn(x)
req_conn(x);过了一段时间客户端第一次请求的响应请求到达客户端,连接建立。
数据传输完毕后,双方中段连接。但之前重发的连接请求
r
e
q
_
c
o
n
n
(
x
)
req\_conn(x)
req_conn(x)到达了服务器端,服务器端会维护这一段连接的状态并响应;客户端收到响应之后会丢弃,但服务器并不知道,所以在服务器端维护了虚假的半连接。

场景2:老的数据被当成新的数据接受了
在场景1的基础上,在连接是否之前重传了一次
d
a
t
a
(
x
+
1
)
data(x+1)
data(x+1)数据;
在重传的数据
d
a
t
a
(
x
+
1
)
data(x+1)
data(x+1)和重传的连接请求
r
e
q
_
c
o
n
n
(
x
)
req\_conn(x)
req_conn(x)到达服务器之前,双方就已经中断了连接,但是重传的请求到达服务器端之后,服务器建立了虚假的半连接;重传的旧数据
d
a
t
a
(
x
+
1
)
data(x+1)
data(x+1)到达了服务器端,就会被服务器当成新的数据接收了。(但
d
a
t
a
(
x
+
1
)
data(x+1)
data(x+1)是残留在网络上的无用数据)。

注意:
因为 2 2 2次连接双方彼此不能约定参数,所以发送的数据序号都从一个固定值开始。从而加大旧数据被当成新数据接收的概率。
所以,2次连接会造成:
下面来看一看 T C P 3 TCP\ 3 TCP 3次握手:

3次握手解决:半连接和接收老数据问题
场景1:
服务器收到重传的连接请求后,会同一建立连接并向客户端发出应答(和2次连接无异)。
客户端收到服务器的响应后,会很发出拒绝连接的请求,服务器收到拒绝连接的请求后就会释放服务器端对于该连接的状态,避免维护没有必要的半连接。

场景2:
旧的数据被当成老的数据接收是因为有半连接的存在,因为场景1的问题被解决了,场景2的问题自然也被解决了。
当然也有可能在旧数据到达之前,双方又建立起了
T
C
P
TCP
TCP连接,但因为初始序号的随机选择,旧的数据也会因为序号不在范围内而被丢弃。

T
C
P
TCP
TCP是全双工通信,连接建立过程可以看作是两个方向上的连接建立过程,如下图所示;但中间的两条可以合并成一条,因此变成3次握手。

参与一条 T C P TCP TCP连接的两个进程中的任何一个都能终止该连接。连接释放后,主机中的“资源”(缓存和变量)将被释放。
所以客户端、服务器分别关闭他自己这一侧的连接。

在一个 T C P TCP TCP连接的生命周期内,运行在每台主机中的 T C P TCP TCP协议在各种TCP状态之间变迁。
服务器端 T C P TCP TCP经历的典型的 T C P TCP TCP状态序列如下:

拥塞的非正式定义:
太多的数据需要网络传输,超过了网络的处理能力。
拥塞的表现:
因此,分组重传作为网络拥塞的征兆
场景环境:

A
A
A主机和
B
B
B主机都以
λ
i
n
λ_{in}
λin字节/秒的平均速率将数据发送到连接中。
下图显示单条连接的吞吐量(接收方每秒接收的字节数)与该连接发送速率之间的函数关系:

当发送速率在
0
R
/
2
0~R/2
0 R/2之间时,接收方的吞吐量等于发送方的发送速率;
当发送速率超过
R
/
2
R/2
R/2时,接收方的吞吐量只能达到
R
/
2
R/2
R/2。该上线是由于两条连接之间共享链路容量造成的。
取得
R
/
2
R/2
R/2的吞吐量可能会加大数据传输的延迟,如下图;当发送速率接近
R
/
2
R/2
R/2,平均时延就会越来越大。
当超过
R
/
2
R/2
R/2时,路由器中的平均分组数就会无限增长,平均时延也会变成无穷大

所以拥塞的代价:
流量强度越大,延时就越大,强度趋近1时延时将趋近无穷大
场景环境:
发送报文段(包含原始数据和重传数据)的速率用 λ i n ′ λ^{'}_{in} λin′字节表示, λ i n ′ λ^{'}_{in} λin′有时也被称为网络的供给载荷

假设发送方仅当在确定了一个分组已经丢失时才重传(如将超时时间设置的足够长);在这种情况下,性能就可能如下图所示;

探究供给载荷 λ ′ = R / 2 λ^{'}=R/2 λ′=R/2时的情况,数据交付给接收方的速率为 R / 3 R/3 R/3;从平均的角度来说,在 0.5 R 0.5R 0.5R的传输数据中, 0.333 R 0.333R 0.333R字节/秒是初始数据,而 0.166 R 0.166R 0.166R字节/秒是重传数据。
在此处,网络拥塞的代价是:
发送方必须执行重传以补偿因为缓存溢出而丢弃的分组
假设发送方会提前重传发生超时并重传在队列中在排队还未丢失的分组;这种情况初始数据和重传分组都可能到达接收方;在这种情况下路由器转发重传的分组是在做无用功,因为初始数据可能到达接收方。而路由器可以利用链路的传输能力去发送其他真正需要发送的分组。
因此,网络拥塞的代价是:
发送方在遇到大时延时时所进行的不必要重传会引起路由器利用其链路带宽来转发不必要的分组副本
(简单概述)
场景环境:

假设
A
A
A要与
C
C
C通信,需要通过
R
1
R1
R1和
R
2
R2
R2路由
B
B
B要与
D
D
D通信。
B
B
B要与
D
D
D通信,需要通过
R
4
R4
R4和
R
1
R1
R1路由
B
B
B要与
D
D
D通信。
当 A A A向 C C C发送的数据量很大时,会占用 R 1 R1 R1路由的链路资源;而 B B B向 D D D发送数据时,会通过 R 1 R1 R1,若 R 1 R1 R1缓存满了,就会丢弃 B B B的通过 R 4 R4 R4传递过了的数据,从而浪费了 R 4 R4 R4的传输能力。
因此,网络拥塞的代价:
当一个分组沿着一条路径被丢弃时,每个上游路由器用于转发该分到丢弃该分组而使用的传输容量最终被浪费掉了。
网络拥塞时:
2种常用的拥塞控制方法:
A B R ( a v a i l a b l e b i t r a t e ) ABR(available\ bit\ rate) ABR(available bit rate)提供的是“弹性服务”:
发送方与接收方发送的数据以信元为单位,包含 ( 1 ) (1) (1)数据信元; ( 2 ) R M (2)RM (2)RM(资源管理)信元;
发送端在发送数据时,会在数据信元中间隔插入 R M RM RM信元; R M RM RM信元存在一些控制信息:
上述比特位会由中间经过的路由器进行标记,当接收端收到
R
M
RM
RM信元,会不做任何该变返回给发送端。
发送方会根据返回的
R
M
RM
RM信元的控制信息进行发送速率的调整。

T C P TCP TCP采用的是端到端的拥塞控制机制,端系统根据自身得到的信息,判断是否发生拥塞,从而采取动作。
拥塞控制的几个问题:
发送端如何探测到拥塞?
一旦超时,就认为拥塞了,有一定误判,但是总体控制方向是对的如何控制发送端发送的速率?
C o n g W i n CongWin CongWin是动态的,是感知到的网络拥塞程度的函数:
TCP拥塞控制和流量控制的联合动作:
流量控制是发送端与接收端之间的局部约束,而拥塞控制是整个网络的全局约束。
发送端在控制往网络中注入数据的速率时,既要考虑双方之间的局部约束,也要考虑网络的全局约束。所以发送缓存满足:
S
e
n
d
W
i
n
=
m
i
n
{
C
o
n
g
W
i
n
,
R
e
c
v
W
i
n
}
SendWin=min\{CongWin, RecvWin\}
SendWin=min{CongWin,RecvWin}
T C P 慢 启 动 : \color{red}{TCP慢启动:} TCP慢启动:
指数性增长方式是指:
只要不发生超时或者收到3个冗余ACK,每一个RTT,
C
o
n
g
W
i
n
CongWin
CongWin加倍;即每收到一个ACK,
C
o
n
g
W
i
n
加
1
CongWin加1
CongWin加1。

虽然较慢启动,但一点都不慢;初始速率很慢,但是加速却是指数性的。所以在慢启动阶段, C o n g W i n CongWin CongWin值飞快增长。
所以从长期来看,慢启动的事件可以忽略不记。
超 时 事 件 发 生 : \color{red}{超时事件发生:} 超时事件发生:
乘
性
减
:
\color{blue}{乘性减:}
乘性减:
发生丢失事件后将
C
o
n
g
W
i
n
CongWin
CongWin降为
1
1
1,将
C
o
n
g
W
i
n
/
2
CongWin/2
CongWin/2作为阈值,进入慢启动阶段(倍增直到
C
o
n
g
W
i
n
/
2
CongWin/2
CongWin/2)
加
性
增
:
\color{blue}{加性增:}
加性增:
当
C
o
n
g
W
i
n
>
CongWin>
CongWin>阈值时,一个
R
T
T
RTT
RTT如没有发生丢失事件 ,将
C
o
n
g
W
i
n
CongWin
CongWin加
1
M
S
S
1MSS
1MSS;即每个
R
T
T
RTT
RTT,
C
o
n
g
W
i
n
CongWin
CongWin加一。
收 到 3 个 冗 余 A C K : \color{red}{收到3个冗余ACK:} 收到3个冗余ACK:
在旧版本种,无论是遇到超时事件还是收到3个冗余ACK,都会将阈值设置为 C o n g W i n CongWin CongWin的一半,并将 C o n g W i n CongWin CongWin设置为1。
在此基础上引入快重传和快恢复:
- 快重传:
当收到3个冗余的ACK,直接重传对方未收到的报文段,而不是等待超时计时器的超时- 快恢复:
当收到3个冗余ACK,将 阈 值 阈值 阈值设置为 C o n g W i n CongWin CongWin的一半,并将 C o n g W i n CongWin CongWin设置为1如下图:

TCP的平均吞吐量是多少,使用窗口window尺寸W和RTT来描述?
首先忽略慢启动阶段,假设发送端总有数据传输。
那么窗口尺寸变化可以如下图所示:

可发生数据大小的窗口值在
W
/
2
−
W
W/2 - W
W/2−W之间变化,这里取均值作为平均窗口大小,即
W
2
+
W
2
=
3
4
W
\frac{\frac{W}{2}+W}{2}=\frac{3\ }{4}W
22W+W=43 W,所以吞吐量公式为:
平
均
吞
吐
量
=
3
4
W
R
T
T
b
y
t
e
s
/
s
e
c
平均吞吐量=\frac{\frac{3\ }{4}W}{RTT} bytes/sec
平均吞吐量=RTT43 Wbytes/sec
公平性目标:
如果
K
K
K个
T
C
P
TCP
TCP会话分享一个链路带宽为
R
R
R的瓶颈,每一个会话的有效带宽为
R
/
K
R/K
R/K.
以简单的两条连接为例子,如下图:

假设 1 1 1和 2 2 2之间一开始是不公平的, 1 1 1占用了大量的带宽,如下图:

下一把,
T
C
P
TCP
TCP连接1和2会将拥塞窗口加1,红点向45°的方向移动:

因为超出链路带宽
R
R
R了,就会出现丢失;则将窗口值设为一半,如下图:

可以发现总体趋势是向公平分配的方式迈进的,上述的过程会重复多次,最后会收敛于绿线与蓝线的交点,即 1 1 1和 2 2 2各占 R / 2 R/2 R/2
而对于 U D P UDP UDP来说,发送的速率不受限制,想发就发, U D P UDP UDP对 T C P TCP TCP来说是"不友好"的。
学习完上述内容后,可以看出 T C P / I P TCP/IP TCP/IP框架将复杂性放在了网络边缘,从而减轻了网络核心的负担。