文章目录
上课内容
- 用于在不可靠网络中建立可靠链接
- 网络层的复用,让多个应用层应用使用一个网络连接(通过端口及其相关机制区分)
服务原语
简单传输层服务的原语:
服务原语的使用:
- TCP的socket原语(Berkeley Socket)
- 这里的accept相当于之前的listen,
一、运输层的端口
TCP/IP端口号为16位,但其指具有本地意义,表只应用层中各个进程与运输层交互时的层间接口。
服务器端口号
两大类:
- 系统端口号:0-1023,他们被用于一些特殊用途,一般不改动
- 登记端口号:1024-49151 为没有系统端口号的应用程序使用。
客户端端口号
49152-65535 仅仅在酷虎进程运行时才动态选择
!扩展问题:一台机器所能容纳TCP连接的数量
- 对于客户端机器,TCP的连接使用系统分配的端口(一般在linux为50000个左右),以及自身的ip地址决定,所以单ip地址下客户机能够建立的连接受
sysctl -a | grep ip_local_port_range
提供的动态端口范围限制。但docker等技术可以让客户机虚拟化多个网卡,这样就拥有了多个ip,此时最大连接数为每个容器ip地址下range大小的限制,当然也受总内存的限制 - 对于服务端机器,TCP连接实际上是{自身ip,自身port,client ip,client port}四元组唯一决定,所以理论上其能维持的TCP连接无上限,但要维护一个连接最少需要3.3k内存,所以它维持连接数与内存有关
二、用户数据报协议UDP
特点:
- 无连接
- 尽最大努力交付
- 面向报文:发送方对应用程序交的报文,添加首部后就交给IP层,不合并也不拆分,保留其边界
适用场景
- UDP没有拥塞控制,故对实时应用(电话,视频会议),可丢失但不允许大时延的场景有用
- 支持一对一,一对多,多对一和多对多
- 首部开销小(8byte)
首部字段结构
可以看到,首部字段时四个2字节字段:
- 源端口
- 目的端口
- 长度:UDP用户数据报长度,最小是8(也即头部)
- 检验和:检验包括头部和数据报(IP层只检验头部)是否有错,有则丢弃?
可以看到,同样的IP报,可以通过UDP实现基于端口的分用和复用
伪首部
UDP在计算checksum之前要增加12字节伪首部,如图
其中UDP长度与真正首部长度是相同的。伪首部只是临时添加的(不下传也不上交,信息是IP地址信息是应用层给的)。之后,将伪头部,真头部和数据包部分一起计算校验和。
UDP以16bit为1个checksum单位(因为校验和是16位),故当有奇数个数据字节时,会补上1个全0字节:
三、传输控制协议TCP的基础
特点
- TCP是面向连接的运输层协议
- 每一条连接只能点对点
- 提供可靠交付服务
- 提供全双工通信
- 面向字节流:它把应用层交下来的数据看作无结构的字节流
TCP拥有缓存,并且不关心应用程序发来报文的长度,它根据对方给出的window size和当前网络拥塞程度决定一个报文段应该包含多少字节
TCP连接
TCP连接的端点是套接字(socket)。而端口号拼接到IP地址即构成了套接字:
而每一条TCP连接唯一地被通信两端的两个socket确定:
四、TCP可靠传输的工作原理
停止等待协议
令A位发送方,B为接收方。停等协议指每发完一个分组就停止发送,等待对方的确认,收到确认后在发送下一个分组
无差错情况与有差错情况
最简单的情况是上图(a),接到了再发
如果B接受M1时出错,就扔掉分组,之后什么也不做(不通知A没收到),或者中间丢了,也是什么都不知道,相当于没通知A。一段时间后,A在超时计时器到期时没收到确认,就撤销超时计时器,并且重传M1分组,要实现这样的机制应当注意:
- A发完分组后应当保存已发分组的副本直到其确认接受
- 分组和确认分组需要编号
- 显然,超时计时器重传时间要比数据在分组传输的平均时间长
确认丢失和确认迟到
对之前的出错情况,如果B的确认包传给A时丢了,A不知情,一段时间后这个分组M都会再被传过来,此时B收到了2次正确的M,它应该采取2个行动:
- 丢弃M不向上交付
- 向A发送重传确认分组
这是确认丢失的方法,情况如下:
而如果B发回去的确认包迟到了,在A重传之后才到,这是B会把第二次收到的M丢弃,并且发送重传确认分组。而A则收下迟到的分组但什么也不做。A需要等收到重发的M的确认分组之后再传之后的分组。这是确认迟到的方法
情况如下:
信道利用率
停等协议的优点是简单,但缺点是信道利用率太低
令:
- T D T_D TD=发送分组时间
- R T T RTT RTT=往返时间
- 处理分组时间忽略不计
- T A T_A TA=发送确认分组时间
则信道利用率为:
U
=
T
D
T
D
+
R
T
T
+
T
A
U=\frac{T_D}{T_D+RTT+T_A}
U=TD+RTT+TATD
为了提高效率,需要使用流水线传输的连续ARQ协议
连续ARQ协议
使用滑动窗口,是TCP协议的精髓所在,后仔细讨论
五、TCP报文段首部格式
TCP传送的数据单元是报文段,分首部和数据段。
TCP首部前20个字节是固定的,后有>=0的4N个byte的option,首段固定部分各个字段的示意图和意义如下
字段名称 | 字段长度 | 字段含义 |
---|---|---|
源端口和目标端口(src/des port) | 各2byte | src和des的端口号 |
序号(Sequence Number) | 4byte | 其值= 真实序号% 2 32 2^{32} 232,序号的编址单位是字节,整个要穿的字节流的其实序号要在连接建立时设置,而首部的需要字段值是本报文段发送数据的第一个字节的序号。 |
确认号(ACK number) | 4byte | 是期望收到对方下一个报文段的第一个数据字节的序号,也即期望收到下一个TCP包的Seq number。它也意味着:若ACK=N,则N-1及之前的所有数据都已经正确收到。 |
首部长度/数据偏移(header length) | 4bit | 指出TCP报文段的数据起始处距离TCP报文段的头部起始处有多远。这其实指示了TCP首部长度(因为有option,所以它是必要的)。但要注意的是,数据便宜的单位是32bit(4字节,因为option也是按这个单位增加的),这也意味着option最长不超过40bit,但其实之后的保留位也使得option有增长空间 |
保留位 | 已经减少到了4bit | 留作后用 |
CWR(Congestion Window Reduced) | 1bit | 让接收者直到发送者已经减缓发送,并且可以停止发送ECN-Echo |
ECE(Explicit Congestion Notation) | 1bit | 向接收者发送ECN-Echo信号 |
URG(URGent pointer) | 1bit | 置位时使紧急指针有效,表示报文段中有紧急数据,应尽快传送(实际上是插到队列最前面),而不要按照排队顺序(例:Ctrl+C的中断应当立刻执行) |
ACK | 1bit | 确认号,指示确认字段。TCP规定连接建立后所有报文段ACK都应当置1 |
PSH(PuSH,推送) | 1bit | 少用。发送方把PSH置1时,接收方收到后就尽快把信息交付给接受的进程,而不必等待缓存填满 |
RST(ReSeT,复位) | 1bit | 它置位时,表示TCP连接种出现严重差错,必须释放连接后重新连接;他也用来拒绝一个非法报文段或拒接连接 |
SYN(同步) | 1bit | 用于同步序号SYN=1,ACK=0时,表示这是一个连接请求报文段,如果同意,应让ACK和SYN置位。 |
FIN(终止) | 1bit | 置位时,表明报文端发送完毕,并要求释放运输连接 |
窗口长度(Window size) | 2byte | 指发送方对应的接收窗口,表示从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。这个限制基于接收方有限的缓存空间。它是接收方让发送方设置其发送窗口的依据。 |
校验和(checksum) | 2byte | 包括首部和数据,和UDP一样要加上12byte伪头部,格式相同,但应改为TCP的协议号和对应数据 |
紧急指针(Urgent Pointer) | 2byte | 与URG通用,指出紧急数据的末尾位置(紧急数据后是普通数据),因此它也指示本报文段种紧急数据的字节数。 |
选项(options) | 0-40byte | 变长选项 |
六、TCP可靠传输的实现细节
以字节为单位的滑动窗口
基本示意图(A发,B接受):
如图,假定A收到了B的ACK报文段,其中window size是20(单位是byte),而确认号ack=31(表示B期望从31开始收)。这样A构造出了发送窗口。
超时重传时间选择
RTT = 收到确认时间-发出时间
使用加权的
R
T
T
s
RTT_s
RTTs表示RTT,其中:
R
T
T
s
=
(
1
−
α
)
×
o
l
d
R
T
T
S
+
α
×
n
e
w
s
a
m
R
T
T
s
RTT_s = (1-\alpha)\times oldRTT_S+\alpha \times newsamRTT_s
RTTs=(1−α)×oldRTTS+α×newsamRTTs,α多取0.125
超时重传时间RTO应该设置为: R T T S + 4 × R T T D RTT_S+4\times RTT_D RTTS+4×RTTD
其中RTTD是RTT偏差的加权平均值:
R
T
T
D
=
(
1
−
β
)
×
o
l
d
R
T
T
D
+
β
×
∣
R
T
T
S
−
n
e
w
s
a
m
R
T
T
S
∣
RTT_D = (1-\beta)\times oldRTT_D+\beta\times |RTT_S-newsamRTT_S|
RTTD=(1−β)×oldRTTD+β×∣RTTS−newsamRTTS∣,β去0.25
报文每重传1次,重传时间为2倍的重传时间,不再重传时恢复上述算法
选择确认(Selective ACK,SACK)
解决收到报文只有少量未按序号/序号缺失的情况。
如果要使用SACK,就要在TCP的首部中添加允许SACK的选项。使用后可以报告收到不连续的字节块的边界,而因为首部长度限制,其最多只能生命4个字节块,SACK应用较少。
七、TCP的流量控制
利用滑动窗口实现
发送方的发送窗口不能超过接收方的接收窗口(单位为字节)
TCP窗口的单位是字节
流量控制在ACK置位时才有意义
persistence timer
当B从无空间到有空间,发送有存储空间的ACK时,如果这个包丢了,A就会一直等B发送非0窗口,B也会一直等A发数据,就会产生死锁。
为了防止这种情况,TCP设置了持续计时器,它在一方收到0窗口时开始,如到期就发送一个1byte数据,接收窗口为0的探测报文段,对方会确认这个报文段,并给出接收窗口,如果还是0意味着确实没处理完,如果不是那么僵局就可以打破了。
传输效率
发送时机选择策略:
- 缓存达到MSS时发
- 发送方应用进程进行push操作
- 使用timer周期性发
Nagle算法:
应用进程要放到TCP缓存时,先发第一个byte,收到ACK后把缓存中所有数据打包。另外如果缓存数据达到发送窗口大小/达到MSS,立即发送一个报文段
糊涂窗口综合征及其解决办法:
接收方一次处理1byte,发送方一次发1byte,这会产生极大overhead,此时可以让发送方等待,使得接收缓存有一半空闲或能容纳一个MSS,再发出ACK通知窗口大小。并且发送方也要积累接收方缓存一半/MSS的报文段。
八、TCP的拥塞控制
一般原理
拥塞控制是防止过多数据进入网络导致过载,流量控制是点对点通信量的控制
横轴输入是单位时间内输入分组,纵轴是单位时间内输出分组。
拥塞控制方法
假定:数据单方传送,接收方缓存足够,单位是一个MSS
1. 慢开始和拥塞避免
发送方维持一个拥塞窗口cwnd(congestion window)的状态变量,其取决于网络拥塞程度且动态变化,不超过2~4个SMSS(发送方最大报文段)。发送方让自己的发送窗口小于等于拥塞窗口。
1.1 慢开始算法
主机发数据是从小到大逐渐增大发送窗口(事实上是cwnd数值)
通常在开始时,先把cwnd设置为1,而每收到一个对新报文段确认后,把cwnd增加1。
1.2 拥塞避免算法
2.快重传&快回复
快重传:接收方应当及时发送重复确认,接收方收到3个重复确认后就重传重复确认指定的报文
快恢复:
3. 主动队列管理AQM
网络层策略对TCP拥塞空值有影响,其中影响最大的就是router分组丢弃策略。如果按照最简单的FIFO原则,叫尾部丢弃。此时TCP的发送速率将急剧下降,可能导致全局同步(大量连接速率同快同慢)。
AQM的流行方法是随机早期丢弃:
- 平均队列长度<minthreshold,enque
- 平均队列长度>max threshold,丢弃
- min<平均队列长度<max,以概率p丢弃新到达分组
九、TCP的运输连接管理
连接建立
一开始,B的TCP服务器进程先创建传输控制块TCB, 准备接受客户进程的连接请求。如有,即作出响应。
A的TCP客户进程也是首先创建传输控制模块TCB。 然后 , 在打算建立TCP连接时,向B发出连接请求报文段, 这时首部中的同步位SYN = 1
, 同时选择一个初始序号 seq = x
。TCP规定, SYN报文段(即 SYN=1的报文段)不能携带数据, 但要消耗掉一个序号。 这时, TCP客户进程进入SYN-SENT
(同步已发送)状态。
B收到连接请求报文段后, 如同意建立连接, 则向A发送确认。 在确认报文段中应把SYN位和ACK位都置1, 确认号是ack = x + 1
, 同时也为自己选择一个初始序号seq = y
。 请注意, 这个报文段也不能携带数据, 但同样要消耗掉一个序号。 这时 TCP服务器进程进入SYN-RCVD
(同步收到)状态。
TCP客户进程收到B的确认后, 还要向B给出确认。 确认报文段的ACK置1, 确认号ack = y+ 1
, 而自己的序号seq = x+1
。TCP的标准规定, ACK报文段可以携带数据。 但如 果不携带数据则不消耗序号, 在这种情况下, 下一个数据报文段的序号仍是seq = x+1
。这 时, TCP连接已经建立, A进入ESTABLISHED
(已建立连接)状态。
当B收到A的确认后, 也进入ESTABLISHED
状态。
issue
- A为什么要最后一次确认?
防止已失效的连接请求报文突然传送到了B。如果一个连接请求报文在长时间阻塞后(指A不在和B建立连接),B返回的确认报文A自然不会理睬,但B却认为连接已建立,耗费资源等待数据(或是自己空发数据)。
连接释放
通信双方都可释放连接。现在A和B都处于ESTABLI SHED
状态。A的应用进程先向其TCP发出连接释放报文段, 并停止再发送数据, 主动关闭TCP连接。 A把连接释放报文段首部的终止控制位FIN置1, 其序号seq = u, 它等于前面已传送过的数据的最后一个字节序号+1.此时A进入FIN-WAIT-1
状态,等待B确认,即便不携带数据也消耗一个信号。
B收到连接释放报文段后即发出确认, 确认号是ack = u + 1, 而这个报文段自己的序号是v,等于B之前传送的最后一个byte序号+1,之后B进入CLOSE-WAIT
(关闭等待)状态。TCP服务器进程这时应通知高层应用进程, 因而从A到B这个方向的连接 释放了, 这时的 TCP连接处于半关闭(half-close)状态, 即A 已经没有数据要发
送了, 但B若发送数据, A仍要接收。 也就是说, 从B到A这个方向的连接并未关闭, 这个状态可能会持续一段时间。
A收到来自B的确认后, 就进入FIN-WAIT-2(终止等待2)状态, 等待B发出的连接释放报文段。