TCP和UDP
TCP和UDP
1. TCP
TCP 是可靠传输,使用流量控制和拥塞控制,仅支持一对一,首部最小 20 字节最大 60 字节。
1.1 连接的建立与断开
1.1.1 三次握手建立连接
- 第一次握手:C 向 S 发送一个连接请求,包括一个
SYN = 1, seq = 随机数 i
,C 进入SYN_SENT
状态。 - 第二次握手:S 收到请求后,回应一个:
SYN = 1, ACK = 1, ack = i + 1, seq = 随机数 j
,S 进入SYN_RCVD
状态。 - 第三次握手:C 收到回应后,发送一个
ACK = 1, ack = j + 1
,S 接收并确认后,双方建立连接,进入ESTABLISHED
状态。
1.1.2 四次挥手断开连接
- 第一次挥手:C 发送一个
FIN = 随机数 k
,C 进入FIN_WAIT_1
状态,此时 C 已停止发送数据,但 S 还在监听,还能发送数据。 - 第二次挥手:S 回应一个
ack = k + 1
,S 进入CLOSE_WAIT
状态,C 确认后进入FIN_WAIT_2
状态,此时服务器已经获取到 C 即将断开的信号,但 S 仍在活跃状态。 - 第三次挥手:S 发送一个
FIN = 随机数 l
,S 进入LAST_ACK
状态,S 停止监听,进入等待断开状态,此时 C 和 S 均已停止数据交互。 - 第四次挥手:C 确认了 S 的
FIN
后,C 进入TIME_WAIT
状态,再发送一个ACK = 1, ack = l + 1
,S 进入CLOSED
状态,挥手完成,连接断开。
1.2 连接的控制
1.2.1 流量控制
接收端通知发送端自己可接受的数据大小,叫窗口大小,TCP 首部有个 16 位的窗口大小字段,16 位最大表示 65535,但在 TCP 的 40 字节首部选项中还有个窗口扩大因子 M
,实际的窗口大小等于窗口大小左移 M
位。
接收端通过窗口大小告知发送端可发送的数据大小,通常可将缓冲区的大小设置为窗口大小,当缓冲区快满了,接收端就逐渐减小窗口大小,直到为 0,则发送端不再发送数据,但会定期发送一个窗口探测数据段,以保持获取接收端的窗口大小。如果接收端回复探测数据段的数据帧丢失,就会导致连接中断,所以发送端如果超时未接收到回复,就会重新发送探测数据段。
1.2.2 拥塞控制
除了考虑到流量控制的情况,还需要考虑到:接收方的窗口大小是在 ACK
的时候才回传的,可以理解为,发送方总是慢半拍才知道接收方的窗口大小,因此不能每次都贸然发送最大的数据后再获取窗口大小来调整。为此 TCP 引入慢启动机制,核心思想就是先从少量数据开始发送,如果网络通畅则逐步增大,否则就逐步减少。
发送方维护一个拥塞窗口 cwnd
,cwnd
初始为较小的一个数,通常为最大分段大小(Maximum segment size,MSS)的两倍,也即“慢启动”,但如果收到了 ACK
,则 cwnd
增大一倍(指数增长),因此慢启动只是启动初值低,而增长速度很快。如果 cwnd
增大到了慢启动阈值(Slow Start Threshold,SSThresh),则再次收到 ACK
时不再以指数增长,而是以 cwnd = cwnd + (1 / cwnd)
,每次增长自身的倒数,且每经过一次数据包往返时间(Round Trip Time,RTT),就自增 1:cwnd = cwnd++
,变成线性增长,以避免出现拥塞。
如果发生超时重传(Retransmission TimeOut,RTO),则认为出现了拥塞,则将ssthresh设置为当前cwnd的一半:ssthresh = cwnd / 2,并重置cwnd变回初始值。
因此,当 cwnd < ssthresh
时,可以认为需要慢启动,进入快速增长,当 cwnd > ssthresh
时,可以认为需要避免拥塞,进入线性增长,当 cwnd = ssthresh
时,可以使用慢启动算法,也可以使用拥塞算法。
接收方在接收数据后,会回复一个确认帧,确认帧包括了期待下一次接收的数据的序列号,发送方在接收到该确认帧后,会将下一个数据帧的序列号设置为接收方期待的序列号。而如果接收方接收到的数据中的序列号,不是自己所期待的,说明自己所期待的数据发生了丢失导致未能接收到,则需要根据所设置的重复阈值 N(例如3),连续 N 次向发送方发送 ACK
,且 ACK
中包含了已丢失的那份自己所期待的数据的序列号,发送方在连续 N 次接收到了这样的 ACK
后,则认为发生了数据丢失,而不是拥塞,则进入快速重传阶段,立即发送中间缺失的数据(也即发送方没有收到对应 ACK
导致 RTT
的数据),同时执行快速恢复算法,将 ssthresh
调整为当前 cwnd
的一半,由于调整后 cwnd > ssthresh
,因此执行拥塞避免算法。
1.3 TCP/IP四层模型
OSI 模型对应 TCP/IP 四层模型(从上到下):
- 应用层、表示层、会话层 - 应用层
- 传输层 - 传输层
- 网络层 - 网络互联层
- 数据链路层、物理层 - 主机到网络层
1.3.1 网络层和传输层的区别
网络层面向的是设备与设备之间的通信,而传输层面向的是设备与设备上运行的进程之间的通信。例如,IP 属于网络(互联)层,它负责定位一个网络中的各个设备,设备和设备之间的数据交互不一定使用 TCP 或 UDP,但需要知道 IP 地址。而 TCP、UDP 属于传输层,负责将数据从某个端口(也可以映射成一个进程)到另一端口(另一个进程),以区分不同的进程,实现不同的应用服务之间的数据传输服务。
UDP
UDP 是不可靠传输,没有流量控制和拥塞控制,UDP 支持单播(一对一)、多播(一对多和多对一)、广播(多对多),首部开销小(8 字节),适用于实时应用(IP 电话、视频会议、直播等)。
远程视频流传输,可以分别利用 UDP 的可靠传输和不可靠传输。
视频中的帧有关键帧和普通帧,关键帧是可以直接解码出帧图像的帧,它包含了一个帧所有的信息,而普通帧,是根据其依附的关键帧,只记录了与之相差的部分,不能直接解码出帧图像,这样做的好处,是大大缩小了视频的体积,因为每个关键帧之间的普通帧,只需要记录自己和所依附的关键帧之间的差别,然后在播放的时候只需要改变显示有差别的地方即可。因此,远程视频传输,可以在传输关键帧的时候,使用可靠传输,确保关键帧传输到位,而普通帧使用不可靠传输,这样即使普通帧丢包,也不会影响整体显示效果。
UDP 如果想要实现可靠传输,可以在数据段手动添加 ACK
和 SEQ
机制,自行判断 ACK
和 SEQ
是否符合,并配合发送和接收缓冲区以及维护一个超时重传表来实现。
已有的可靠 UDP 有:RUDP(实现了 TCP 中的流量控制)、RTP()等。