传输层为不同主机的进程提供逻辑通信服务。传输层向高层用户屏蔽了下面网络层的核心细节,使得进程和进程之间仿佛有一条端到端的逻辑通信信道。
- 发送方:将应用层传输的报文封装成报文段,并向下传输给网络层
- 接收方:接收网络层传输过来的报文段,组合成报文并向上发送给应用层
传输层协议
- UDP(用户数据报协议)
- 非面向连接
- 尽力而为
- TCP (传输控制协议)
- 流量控制
- 拥塞控制
- 面向连接
多路复用 / 分用
传输层提供进程和进程之间的逻辑通信服务,而一个主机可能存在多个进程。为了区分不同的进程,传输层需要提供多路复用 / 分用功能
发送端进行多路复用
传输层从多个 Socket 接收数据,为每块数据封装上头部信息,生成报文段并发送给网络层
接收端进行多路分用
传输层接收网络层发来的报文段,解析头部信息并将报文发送给正确的 Socket
无连接的多路分用
无连接的多路分用使用二元组标识:目的 IP 地址和目的端口号
面向连接的多路分用
面向连接的多路分用使用四元组标识:源 IP 地址、源端口号、目的 IP 地址和目的端口号
UDP
- 基于 IP 协议,实现了多路复用 / 分用、错误校验
- 尽力而为服务,UDP 数据报可能会丢失,非按序到达
- 无连接,发送方和接收方不需要握手和挥手
- UDP 对报文不拆分,只添加 UDP 首部
UDP 首部
UDP 首部包含源端口号、目的端口号、长度和校验和,共 8 字节
长度:包括 UDP 首部和 UDP 数据部分
校验和
- 发送方将数据报视为多个16 bit 整数
- 将整数求和,进位加在和的后边
- 将求和结果按位取反得校验和
- 接收方重新计算校验和,并与发送方的 UDP 首部校验和比较,相等认为数据传输正确
优点
- 无连接,减少延迟
- 实现简单,不需要维护连接状态
- 头部开销少,UDP 头部 8 字节
- 没有拥塞控制,应用程序可以更好的控制发送时间和速率
应用
- 常用于流媒体应用:容忍数据丢失但对速率敏感
- 应用于应用层 DNS 协议
TCP
TCP 是传输层的可靠数据传输协议
- 点对点
- 可靠、按序字节流
- 流水线机制,提高传输性能
- 发送方和接收方有缓存
- 面向连接
- 全双工通信
- 单工通信:单向传输
- 半双工通信:双向交替传输
- 全双工通信:双向同时传输
- 流量控制和拥塞控制
可靠指数据不错、不丢、不乱序
- 不错:校验和、序号、确认号
- 不丢:定时器(等待合理时间)和重传机制
- 不乱:序号
TCP 首部
- 序号:用于对字节流进行编号,比如序号 200 表示报文段第一个字节的编号是 200 。建立 TCP 连接时,双方随机选择起始序列号
- 确认号:期望接收到的下一个字节流的序号,确认号是累计确认机制,该确认号之前的所有字节均被正确接收到
- 数据偏移: TCP 首部的长度
- URG:紧急数据,一般不使用
- ACK:标记确认号是否有效,ACK 等于 1 表示确认号有效,TCP 建立连接后 ACK 始终等于 1
- PSH:标记立刻把数据推送给上层
- RST:连接错误
- SYN:建立连接时用来同步连接,三次握手的前两次携带 SYN = 1
- FIN:发送释放连接请求,四次挥手双方各发一次 FIN = 1
- 窗口:接收窗口的大小,所愿意接收的字节的数目
- 校验和:校验数据
TCP 三次握手
TCP 在三次握手和四次挥手时不会采用流水线机制
- TCP 三次握手的目的是为了防止已失效的连接请求到达服务器,让服务器产生连接的误判,从而造成服务器资源的浪费。如果客户端向服务器发送连接请求,但由于网络拥塞等情况导致服务端迟迟没有收到连接请求,客户端等待超时会重新发送连接请求,这时第一次发送的连接请求即为已失效的连接请求。但是滞留在网络中的已失效的连接请求最后也到达了服务器,服务器无法区分请求是否失效,而是认为客户端又发送了一次新的请求,此时服务端会发送确认报文。如果只有两次握手,会导致服务端打开失效连接等待客户端发送数据,而失效的请求不会发送任何数据,从而导致服务端资源的浪费。而有了三次握手,客户端会丢弃失效连接,不会发送失效请求的确认报文,所以不会造成服务端打开失效连接。
- TCP 三次握手是为了保证“在不可靠信道进行可靠数据传输”这一需求所导致的,TCP 通过三次握手建立可靠的全双工通信。第一次握手和第二次握手建立客户端到服务器的可靠连接,第二次握手和第三次握手建立服务器到客户端的可靠连接。由于服务端的响应报文和连接请求可以放在一起,所以三次握手就能保证建立的可靠的全双工通信。
TCP 四次挥手
- 主动请求关闭连接的一方发送
FIN = 1
的请求报文,对方发送 ACK 响应报文。此时 TCP 处于半关闭状态,表示主动请求关闭连接的一方没有更多的数据需要发送,但是可以接收来自另一端的数据 - 另一端发送完数据后发送
FIN = 1
的请求报文,主动请求关闭连接的一方进行确认
主动请求关闭连接的一方会进入 TIME_WAIT 状态,TIME_WAIT 需要经过 2个 MSL(最大报文段生存时间)才能关闭,使用 2MSL 定时器记录 TIME_WAIT 的时间。
- 如果客户端主动关闭连接,由于 IP 协议不可靠导致服务端最后没有收到客户端发送的 ACK ,服务端就会在超时后重新发送 FIN 。如果没有 TIME_WAIT 而是直接关闭就会导致客户端无法响应服务端再次发送的 FIN,最终服务端收到 RST 连接错误报文。而 TIME_WAIT 能够保证客户端再次接收 FIN 并发送 ACK
- 如果客户端主动关闭连接后立刻建立新连接,并且源端口号和原来相同,一般不会有问题。但如果老连接的数据仍然滞留在网络中并最后传输到服务器,会导致服务端无法区分新数据和老数据,造成数据传输错误。2 倍的 MSL 能够保证本次连接的所有数据都能够从网络中消息
在高并发短连接的 TCP 服务器中,当服务器处理完请求后立刻主动正常关闭连接,会出现大量 Socket 处于TIME_WAIT 状态。如果不到必须解决这个问题最好不要动 TCP 的断开连接的方式,可以使用负载均衡的方式解决高并发问题。
可靠传输
- RTT :指一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值
- RTO:重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传
为了保证 TCP 协议的可靠性,当超时或连续收到3次重复确认号时需要重传。超时需要设置重传定时器阀值 RTO ,重传定时器阀值的选择可以参考 RTT 指标,RTO 的计算公式为:
$$
EstimatedRTT = (1-\alpha)EstimatedRTT+\alpha RTT \
DevRTT = (1-\beta) DevRTT + \beta |RTT-EstimatedRTT| \
Timeout = EstimatedRTT + 4*DevRTT
$$
即求 RTT 的指数移动平均值与 4 倍指数移动方差的和。重传数据的 RTT 不会参与计算,理由很简单,因为程序已经不在正常状态下了,估计出来的数据也是没有意义的。
TCP 采用流水线机制,使用滑动窗口协议保证数据传输速度,一次能够发送多个报文段。一个滑动窗口共享一个重传计数器,重传计数器记录第一个已发送数据但未收到确认请求的报文段。
流量控制
流量控制是为了控制发送方发送的速率,保证接收方来得及接收。TCP 发送方和接收方都有缓存并且缓存大小有限,如果发送方速度太快导致接收方缓存空间用尽,将导致大量的数据被扔掉。
接收方知道自己的剩余缓存大小,把剩余缓存放入 TCP 头部的窗口字段中发送给发送方,然后发送方根据接收方能够接收的数据大小调整自己发送数据的频率。
如果发送端收到接收端数据段的窗口大小是 0 ,发送端无法发送数据。当接收端有一定大小的缓存能够接收数据时,接收端会向发送端发送非零窗口大小的报文段。但是如果这个非零窗口大小的报文段在传输过程中丢失,导致发送端无法接收该报文段,那么发送端会一直等待下去,而接收到并不知道报文段丢失而会一直等待发送端发送数据,从而导致死锁。
坚持定时器主要是解决零窗口大小通知可能导致的死锁问题。TCP 为每个连接设置一个坚持定时器,当客户端收到服务器的 0 滑动窗口报文时,启动一个定时器来计时,并在定时器溢出的时向服务器发送探测报文,查询窗口是否已经增大,如果得到非零的窗口就重新开始发送数据,如果得到零窗口就再开一个新的定时器准备下一次查询。
拥塞控制
太多主机发送太多数据或数据发送过快导致网络无法处理。拥塞的表现包括分组丢失(路由器缓存溢出)或分组排队时延过大(在路由器缓存中排队)
比较
- 可靠传输:主要解决端到端的数据传输问题
- 流量控制:限制发送发的发送速率以免接收方缓存溢出
- 拥塞控制:防止网络拥塞,解决的是整个网络的数据传输问题
解决方法
- 端到端的拥塞控制:网络层不需要显示的提供拥塞控制的支持,传输层根据分组丢失或分组时延来调整发送方的发送速率。 TCP 使用端到端的拥塞控制
- 网络辅助的拥塞控制:路由器向发送方显示的反馈网络拥塞信息
TCP 拥塞控制
发送方限制发送速率
动态调整窗口大小,以改变发送速率
感知网络拥塞
当发生超时或收到 3 个重复 ACK 后,认为分组丢失并且网络拥塞,发送方降低发送速率
合理调整发送速率
加性增-乘性减:每个 RTT 为增加窗口一个报文段,从而避免网络拥塞;当发生网络拥塞时,将窗口大小减半
慢启动:刚启动 TCP 窗口大小呈指数增长
TCP 窗口指数增长和线性增长需要进行切换,可以设置 threshold ,值设置为发生拥塞事件前窗口的一半。慢启动解决 TCP 刚建立连接起速太慢的问题;加性增-乘性减是避免数据传输过程中网络拥塞的问题。下图中蓝线是早期版本,黑线是改进版本
TCP 对不同的拥塞有不同的处理机制
- 3 个重复 ACK
- 窗口切到一半,然后线性增长
- 发送方收到 3 个重复 ACK 意味着网路还能够进行传输,拥塞比较低
- 超时
- 窗口设置为 1 ,然后指数增长。当达到 threshold 时线性增长
- 超时意味着网路拥塞更严重
总结:
- 当窗口小于 threshold 时处于慢启动状态,窗口呈指数增长
- 当窗口大于 threshold 时处于拥塞避免状态,窗口呈线性增长
- 当收到 3 个重复 ACK 时,窗口大小减半,threshold 减为发生阻塞时窗口大小的一半
- 当超时时,窗口减为 1,threshold 减为发生阻塞时窗口大小的一半
保活定时器
保活定时器是为了应对 TCP 连接双方出现长时间的没有数据传输的情况。如果客户端与服务器建立了 TCP 连接之后,客户端由于某种原因导致主机故障,则服务器就不能收到来自客户端的数据,而服务器不可能一直处于等待状态,保活定时器就是用来解决这个问题的。服务器每收到一次客户端的数据,就重新设置保活定时器,通常为 2 小时,如果 2 小时没有收到客户端的数据,服务端就发送一个探测报文,以后每隔75秒发送一次,如果连续发送 9 次探测报文段后仍没有收到客户端的响应,服务器就认为客户端出现了故障,就可以终止这个连接
支持参数配置:
net.ipv4.tcp_keepalive_time=7200
:timeout 的时间net.ipv4.tcp_keepalive_intvl = 75
:timeout 后发送探测报文的间隔时间net.ipv4.tcp_keepalive_probes = 9
:发送探测报文的次数
TCP 机制
- 确认应答机制
- 超时重传机制:超时
- 快速重传机制:3个重复 ACK
- 滑动窗口机制
- 流量控制
- 拥塞控制
- 延迟应答机制:接收方处理数据很快,延迟应答保证接收方窗口尽可能的大,提高传输效率
- 捎带应答机制:ACK 联通返回数据一并发送给接收端
TCP 粘包
发送端需要等缓冲区满时才发送出去,造成粘包
接收端不及时的接收缓冲区内的包,造成多个包接收