UDP socket编程|青训营笔记

377 阅读6分钟

这是我参加「第三节青训营~后端场」笔记创作活动的第2篇笔记

UDP通信的特点

  1. (无连接):知道对端的IP和端口号就直接进行传输,不需要建立连接
  2. (不可靠):没有确认机制,没有重传机制;如果因为因为网络故障无法发到对方,UDP协议层也不会给应用层返回任何错误信息。
  3. (面向数据报):不能够灵活的控制读写数据的次数和数量,应用层交给UDP多长的报文,UDP都会 原样发送,不会拆分,也不会合并。

QUIC

QUIC就是基于UDP的,是一种基于UDP的低时延的互联网传输层协议。 QUIC 的握手连接更快,因为它使用了 UDP 作为传输层协议,这样能够减少三次握手的时间延迟。而且 QUIC 的加密协议采用了 TLS 协议的最新版本 TLS 1.3,相对之前的 TLS 1.1-1.2,TLS1.3 允许客户端无需等待 TLS 握手完成就开始发送应用程序数据的操作,可以支持1 RTT 和 0 RTT,从而达到快速建立连接的效果。

虽然 QUIC 没有使用 TCP 协议,但是它也保证了可靠性,QUIC 实现可靠性的机制是使用了 Packet Number,这个序列号可以认为是 synchronize sequence number (syn)的替代者,这个序列号也是递增的。与 syn 所不同的是,不管服务器有没有接收到数据包,这个 Packet Number 都会 + 1,而 syn 是只有服务器发送 ack 响应之后,syn 才会 + 1。

image.png

连接迁移

  1. TCP的连接重连之痛 一条 TCP 连接是由四元组标识的(源 IP,源端口,目的 IP,目的端口)。什么叫连接迁移呢?就是当其中任何一个元素发生变化时,这条连接依然维持着,能够保持业务逻辑不中断。当然这里面主要关注的是客户端的变化,因为客户端不可控并且网络环境经常发生变化,而服务端的 IP 和端口一般都是固定的。

比如大家使用手机在 WIFI 和 4G 移动网络切换时,客户端的 IP 肯定会发生变化,需要重新建立和服务端的 TCP 连接。

又比如大家使用公共 NAT 出口时,有些连接竞争时需要重新绑定端口,导致客户端的端口发生变化,同样需要重新建立 TCP 连接。

所以从 TCP 连接的角度来讲,这个问题是无解的。

  1. 基于UDP的QUIC连接迁移实现 当用户的地址发生变化时,如 WIFI 切换到 4G 场景,基于 TCP 的 HTTP 协议无法保持连接的存活。QUIC 基于连接 ID 唯一识别连接。当源地址发生改变时,QUIC 仍然可以保证连接存活和数据正常收发。

那 QUIC 是如何做到连接迁移呢?很简单,QUIC是基于UDP协议的,任何一条 QUIC 连接不再以 IP 及端口四元组标识,而是以一个 64 位的随机数作为 ID 来标识,这样就算 IP 或者端口发生变化时,只要 ID 不变,这条连接依然维持着,上层业务逻辑感知不到变化,不会中断,也就不需要重连。

由于这个 ID 是客户端随机产生的,并且长度有 64 位,所以冲突概率非常低。

握手的区别

HTTP 协议在传输层是使用了 TCP 进行报文传输,而且 HTTPS 、HTTP/2.0 还采用了 TLS 协议进行加密,这样就会导致三次握手的连接延迟:即 TCP 三次握手(一次)和 TLS 握手(两次),如下图所示。

image.png HTTPS 的握手过程,包含 TCP 握手和 TLS 握手:

TCP握手: 需要两个RTT image.png TLS 握手:密钥协商(1.3 版本): 只需要1个RTT image.png (1)客户端:生成随机数 a,选择公开的大数 G 和 P,计算 A=aG%P,将 A 和 G 发送给服务器,也就是 Client Hello 消息 (2)服务器:生成随机数 b,计算 B=bG%P,将 B 发送给客户端,也就是 Server Hello 消息

UDP头部的结构:

16位源端口号:标识发送方应用程序 16位目的端口号:标识接收方应用程序 16位UDP数据报长度:表示发送方传输数据报的大小 16位检验和:校验数据报是否完整,不完整是重发数据 UDP首部共为8字节 (16+16+16+16)/ 8 = 8字节 UDP头部结构图如下: image.png

缓存区

  1. UDP没有真正意义上的发送缓冲区,实质上是由网络层进行传输动作
  2. UDP有接收缓存区,但是不能发送UDP报的顺序和接收UDP报的顺序一致,如果缓冲区满了,再到达的UDP数据就会被丢弃。
  3. 双全工: UDP的socket既能读,也能写 tips :一个UDP能传输的数据最大长度是64kb。如果超出64kb,就要再应用层手动分包,然后进行多次发送,并且在接收端进行手动拼装。

基于UDP应用层协议

  1. NFS:网络文件系统
  2. TFTP:简单文件传输协议
  3. DHCP:动态主机配置协议
  4. BOOTP:启动协议
  5. DNS:域名解析协议
  6. 自己写的UDP程序中自定义的应用层协议

字节的作业分析

课后作业1- UDP socket 实现 ack,感知丢包重传

1、添加seq/ack机制,确保数据发送到对端 2、添加发送和接收缓冲区,主要用于用户超时重传。 3、添加超时重传机制。

详细说明:发送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x+1的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。如果超过一定时间还没收到ack,那么重传数据。

作业要求:

  1. 学会 UDP socket 编程 略
  1. 先从简单的 ack 学习,客户端等待 ack 再发包 答:这里我采用
  2. 添加seq/ack机制,确保数据发送到对端
  3. 添加发送缓存区和接收端缓存区,主要用于用户超时重传。
  4. 添加超时重传机制。
  1. 什么时候客户端认为是丢包? 答: 如果我的这个ack在缓存里呆了一段时间了,还是没有从内存中删除,客户端就认为该包丢了。
  1. 重传怎么考虑效率? 答: 发送的数据会存在缓存区中,只有当收到ack数据包的时候,才删除缓存区的数据。 这也发现丢包要重传的时候就比较方便找包,为了防止内存溢出的问题,要有一个专用的垃圾回收线程(定时任务来实现)来回收内存里存了比较长的一段时间的数据。
  1. 能不能不阻塞只穿丢掉的中间的段? 如果发送端有一段时间没有发送成功数据,则这个发送端放弃这个数据包的发送(可能是对面网络不好),继续下一个数据包的发送。