Notes On Network Protocol UDP and TCP
UDP 和 TCP from 极客时间 - 趣谈网络协议
UDP
UDP 包头格式

IP头里面有个8位协议, 里面会存放, 数据里面到底是TCP还是UDP。处理完传输层的事情, 内核的事情基本就干完了, 里面的数据应该交给应用程序自己去处理。根据端口号, 将数据交给响应的应用程序。
UDP 的三大特点
- 沟通简单, 没有复杂的数据结构, 处理逻辑, 包头字段. 相信网络通路默认就是很容易送达的,不容易被丢弃的。
- 轻信他人, 它不会建立连接,虽然有端口号,但是监听在这个地方,谁都可以传给他数据,他也可以传给任何人数据,甚至可以同时传给多个人数据。
- 愣头青, 做事不懂权变。它不会根据网络的情况进行发包的拥塞控制,无论网络丢包丢成啥样了,它该怎么发还怎么发。
UDP的三大使用场景
- 需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用。
DHCP就是基于UDP协议的(广播形式)。一般的获取 IP 地址都是内网请求,而且一次获取不到 IP 又没事,过一会儿还有机会。PXE操作系统镜像的下载使用的TFTP,这个也是基于 UDP 协议。
- 不需要一对一沟通,建立连接,而是可以广播的应用。
UDP不面向连接, 使得可以继承广播或多播协议。D类地址(组播地址), 使用这个地址, 可以将包组播给一批机器。当一台机器上的某个进程想监听某个组播地址的时候,需要发送 IGMP 包,所在网络的路由器就能收到这个包,知道有个机器上有个进程在监听这个组播地址。当路由器收到这个组播地址的时候,会将包转发给这台机器,这样就实现了跨路由器的组播。
- 需要处理速度快,时延低,可以容忍少数丢包,但是要求即便网络拥塞,也毫不退缩,一往无前的时候。
如果实现的应用需要有自己的连接策略, 可靠保证, 时延要求, 使用
UDP, 然后在应用层实现这些是再好不过了。
UDP使用的五个例子
网页或者APP的访问
HTTP 协议是基于 TCP 的,建立连接都需要多次交互,对于时延比较大的目前主流的移动互联网来讲,建立一次连接需要的时间会比较长,然而既然是移动中,TCP 可能还会断了重连,也是很耗时的。而且目前的 HTTP 协议,往往采取多个数据通道共享一个连接的情况,这样本来为了加快传输速度,但是 TCP 的严格顺序策略使得哪怕共享通道,前一个不来,后一个和前一个即便没关系,也要等着,时延也会加大。
QUIC(全称Quick UDP Internet Connections,快速 UDP 互联网连接)是Google提出的一种基于UDP改进的通信协议, 目的是降低网络通信的延迟, 提供更好的用户互动体验。流媒体协议
直播协议多使用
RTMP。RTMP协议也是基于TCP的。很多直播应用,都基于
UDP实现了自己的视频传输协议。网络层不好, 应用选择性丢帧。
实时游戏
在异步
IO机制引入之前,UDP尝尝是应对海量客户端链接的策略。游戏对实时要求较为严格的情况下,采用自定义的可靠 UDP 协议,自定义重传策略,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成的影响。
IoT 物联网
物联网领域终端资源少, 维护
TCP协议代价太大. 物联网对实时性要求也很高.Google推出的物联网通信协议Thread就是基于UDP协议的移动通信领域
在
4G王国利, 移动流量上网的数据面对的协议GTP-U是基于UDP的。GTP协议本身就包含复杂的手机上线下线的通信协议。
TCP
TCP天然认为网络环境是恶劣的,丢包,乱序,重传,拥塞都是常用的事情,一言不合就可能送达不到了,因此要从算法层面来保证可靠性。
TCP 包头格式

序号:解决乱序问题,确认哪个先来哪个后到。
确认序号:确认发出去的包,如果没有收到就应该重新发送,知道送达。解决不丢包的问题。
TCP 状态位
SYN发起一个链接ACK是回复RST重新连接FIN结束链接
TCP是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。就像人与人之间的信任会经过多次交互才能建立。
窗口大小(流量控制):TCP要做流量控制, 通信双方各声明一个窗口,标识自己当前能够的处理能力,别发送的太快,撑死我,也别发的太慢,饿死我。
拥塞控制:TCP拥塞控制。控制自己,也即控制发送速度。不能改变世界,就改变自己。
TCP 特点
- 顺序问题,稳重不乱
- 丢包问题,承诺靠谱
- 连接维护,有始有终
- 流量控制,把握分寸
- 拥塞控制,知进知退
TCP 三次握手
请求->应答->应答之应答
- A 发起一个连接请求
- 第一个请求杳无音信
- 包丢了
- 包饶弯路, 超时了
- B没有响应, 不想和我连接
- 再次发送
- 终于到达 B, A 暂时还不知道
- 第一个请求杳无音信
- B 收到了请求包, 知道了 A 的存在, 知道 A 要和它建立链接. 应答
- B 不乐意建立连接, A 会重试一阵后放弃, 连接建立失败
- B 乐意建立链接, 则会发送应答包给 A
- 不能认为连接建立好
- 应答包会丢失
- 应答包会饶弯路
- A 已经挂了
- 不能认为连接建立好
- B 发送的应答包可能会发送多次, 但是只要一次到达 A, A 就认为连接已经建立了, 对于A来说, 他的消息有去有回
- A 给 B 发送应答之应答
- B 也在等待这个消息, 才能确认连接的建立, 只有等到了这个消息, 对于 B 来讲, 才算他的消息有去有回
- 应答之应答也会丢失, 绕路, 甚至 B 挂了. 只要双方的消息都有去有回, 就基本可以了.
需要双方发送的消息都有去有回.
大部分情况下, A 和 B 建立了连接之后, A 会马上发送数据, 一旦 A 发送数据就解决了问题.
- 应答之应答丢失. 当 A 连续发送数据的时候, B 可以认为这个连接已经建立.
- B 挂了. A 发送的数据, 会报错, 说 B 不可达, A 就知道 B 出事情了.
keepalive, 即使没有真实的数据包, 也有探活包.
如果 A 长时间不发包, B 可以主动关闭。
为什么两次握手不行
- A 和 B 原来建立了连接, 做了简单通信, 结束了连接
- 最早 A 第一次发起请求的时候, 重复发了很多次包, 如果这个时候请求到达. B 会认为这也是一个正常的请求的话, 因此建立了连接(如果两次握手就建立链接), 就没有终结了.
TCP 包的序号问题
- A 要告诉 B, 发起包的序号
- B 同样要高速 A, 发起包的序号
序号不能从1开始, 这样往往会冲突.
1 | A 发送 1 2 3. |
- 每个连接都要有不同的序号.
- 这个序号的起始序号是随着时间变化的.
- 32位的计数器, 每
4ms加一.如果到重复, 需要4个多小时, 绕路的包早都死了. 以为IP包头里有个TTL, 也即生存时间.
- 32位的计数器, 每
连接连接过程的状态变化

TCP 四次挥手
- A: B 不玩了
- B: 你不玩了, 我知道了
B 不能在 ACK 的时候, 直接关闭. 有可能A是发完了最后的数据就准备不玩了, 但是 B 还没做完自己的事情, 还是可能在发送数据的, 所以称为半关闭的状态.
A 可以选择不再接收数据, 也可以选择最后再接收一段数据, 等待 B 也主动关闭.
- B: A 好啊, 我也不玩了, 拜拜
- A: 好的, 拜拜
解释
- A : B 不玩了
- B: 你不玩了, 我知道了
- A 没收到回复
- 重新发送”不玩了”
- A 收到回复
- A 跑路了, B 发起的请求得不到A的应答
- B 跑路了, A 不知道 B 是还有事情要处理, 还是过一会会发送结束
断开时序图

A : B 不玩了, FIN-WAIT-1
B: 你不玩了, 我知道了 CLOSE-WAIT
A 收到回复 FIN-WAIT-2
- B 跑路了,
Linux调整tcp_fin_timeout这个参数, 设置超时时间
- B 跑路了,
B: A 好啊, 我也不玩了, 拜拜 LAST_ACK
- A: 好的, 拜拜
- 发送 ACK, FIN-WAIT-2结束.
- 如果这个 ACK, B 收不到
- B 重新发送一个
A 好啊, 我也不玩了, 如果这个时候 A 已经跑路了, B 就再也收不到 ACK 了
- B 重新发送一个
TCP协议要求 A 最后等待一段时间TIME-WAIT, 这个时间要足够长到如果 B 没收到 ACK, B说A 好啊, 我也不玩了会重发的- A 会重新发一个 ACK 并且足够时间到达 B
- 如果 A 直接跑路, 端口就直接空出来了, 但是 B 不知道, B 原来发过的很多包可能都还在路上. 如果 A 的端口被一个新的应用占用了, 就会收到 B 发过来的包(虽然需要会重新生成). 双保险, 为了防止混乱, 需要等足够长的时间, 等待原来B发送的所有包都死翘翘, 再空出端口.
等待时间:等待时间设为2MSL
MSL 是 Maximum Segment Liftetime, 报文最大生存时间. 它是任何报文在网络上存在的最长时间, 超过这个时间报文将被丢弃.
IP头中有一个TTL, 是IP数据报可以经过的最大路由数, 每经过一个处理他的路由器此值就减1, 当此值为0则数据报将被丢弃, 同时发送ICMP报文通知主机.协议规定
MSL为2分钟, 实际应用中常用的是30秒, 1分钟和2分钟等.
超过了 2MSL
- 按照 TCP 的原理, B 重发
FIN - A 收到
FIN, 表示超过时间了. 直接发送RST
TCP 状态机

数字是连接状态变化
虚线是 A 的连接
- 实线是 B 的连接