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 引入慢启动机制,核心思想就是先从少量数据开始发送,如果网络通畅则逐步增大,否则就逐步减少。

发送方维护一个拥塞窗口 cwndcwnd 初始为较小的一个数,通常为最大分段大小(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 如果想要实现可靠传输,可以在数据段手动添加 ACKSEQ 机制,自行判断 ACKSEQ 是否符合,并配合发送和接收缓冲区以及维护一个超时重传表来实现。

已有的可靠 UDP 有:RUDP(实现了 TCP 中的流量控制)、RTP()等。