这是我参加「第三节青训营~后端场」笔记创作活动的第2篇笔记
UDP通信的特点
- (无连接):知道对端的IP和端口号就直接进行传输,不需要建立连接
- (不可靠):没有确认机制,没有重传机制;如果因为因为网络故障无法发到对方,UDP协议层也不会给应用层返回任何错误信息。
- (面向数据报):不能够灵活的控制读写数据的次数和数量,应用层交给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。
连接迁移
- TCP的连接重连之痛 一条 TCP 连接是由四元组标识的(源 IP,源端口,目的 IP,目的端口)。什么叫连接迁移呢?就是当其中任何一个元素发生变化时,这条连接依然维持着,能够保持业务逻辑不中断。当然这里面主要关注的是客户端的变化,因为客户端不可控并且网络环境经常发生变化,而服务端的 IP 和端口一般都是固定的。
比如大家使用手机在 WIFI 和 4G 移动网络切换时,客户端的 IP 肯定会发生变化,需要重新建立和服务端的 TCP 连接。
又比如大家使用公共 NAT 出口时,有些连接竞争时需要重新绑定端口,导致客户端的端口发生变化,同样需要重新建立 TCP 连接。
所以从 TCP 连接的角度来讲,这个问题是无解的。
- 基于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 握手(两次),如下图所示。
HTTPS 的握手过程,包含 TCP 握手和 TLS 握手:
TCP握手:
需要两个RTT
TLS 握手:密钥协商(1.3 版本):
只需要1个RTT
(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头部结构图如下:
缓存区
- UDP没有真正意义上的发送缓冲区,实质上是由网络层进行传输动作
- UDP有接收缓存区,但是不能发送UDP报的顺序和接收UDP报的顺序一致,如果缓冲区满了,再到达的UDP数据就会被丢弃。
- 双全工: UDP的socket既能读,也能写 tips :一个UDP能传输的数据最大长度是64kb。如果超出64kb,就要再应用层手动分包,然后进行多次发送,并且在接收端进行手动拼装。
基于UDP应用层协议
- NFS:网络文件系统
- TFTP:简单文件传输协议
- DHCP:动态主机配置协议
- BOOTP:启动协议
- DNS:域名解析协议
- 自己写的UDP程序中自定义的应用层协议
字节的作业分析
课后作业1- UDP socket 实现 ack,感知丢包重传
1、添加seq/ack机制,确保数据发送到对端 2、添加发送和接收缓冲区,主要用于用户超时重传。 3、添加超时重传机制。
详细说明:发送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x+1的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。如果超过一定时间还没收到ack,那么重传数据。
作业要求:
- 学会 UDP socket 编程 略
- 先从简单的 ack 学习,客户端等待 ack 再发包 答:这里我采用
- 添加seq/ack机制,确保数据发送到对端
- 添加发送缓存区和接收端缓存区,主要用于用户超时重传。
- 添加超时重传机制。
- 什么时候客户端认为是丢包? 答: 如果我的这个ack在缓存里呆了一段时间了,还是没有从内存中删除,客户端就认为该包丢了。
- 重传怎么考虑效率? 答: 发送的数据会存在缓存区中,只有当收到ack数据包的时候,才删除缓存区的数据。 这也发现丢包要重传的时候就比较方便找包,为了防止内存溢出的问题,要有一个专用的垃圾回收线程(定时任务来实现)来回收内存里存了比较长的一段时间的数据。
- 能不能不阻塞只穿丢掉的中间的段? 如果发送端有一段时间没有发送成功数据,则这个发送端放弃这个数据包的发送(可能是对面网络不好),继续下一个数据包的发送。