1.TCP和UDP
2.TCP为什么慢于UDP
3.可靠UDP
1.TCP和UDP
1).通过打电话的方式说明TCP和UDP
a.TCP(传输控制协议), 就像打电话
- 需要先拨号, 接通, 问候(建立连接)
- 你一句, 我一句, 对方没有听清会要求你重复(确认与重传)
- 保证对话有条不紊, 内容准确无误(可靠, 有序)
- 如果信号不好, 你们会下意识地慢点说(拥塞控制)
- 最后会礼貌道别(断开连接)
b.UDP(用户数据报), 就像学校广播站发公告
- 拿起麦克风就直接说, 不管下面有没有听(无连接)
- 只管自己念完, 不关心学生听没听清, 记没记住(无确认, 不可靠)
- 念得飞快, 一口气念完所有内容(高效, 速度块)
- 各个班级听到的顺序可能不同(无序)
- 即使有学生喊"没有听清", 广播站也不会重复(不重传)
2).TCP(传输控制协议)
a.连接性
面向连接, 传输数据前必须通过三次握手建立一条逻辑连接通道; 传输结束后通过四次挥手断开连接
b.可靠性
高可靠性, 确保数据无差错, 不丢失, 不重复, 按次序到达
c.流量控制
有, 由接收方控制发送方的速率, 防止发送过快导致接收方缓冲区溢出
d.拥塞控制
有, 根据网络状况动态调整发送速率, 避免网络瘫痪
e.有序性
有序, 对每个数据字节进行编号, 接收端会重新排序, 保证应用程序收到的数据和发送顺序一致
f.数据模式
面向字节流, 将数据看作一连串无结构的字节流, 没有边界; 所以会出现黏包问题, 需要应用层自行解决
g.首部开销
首部结构复杂, 包含序列号, 确认号, 窗口大小, 校验等控制信息
h.传输效率
速度相对慢, 延迟较高; 因为需要建立连接, 确认, 重传, 流量控制和拥塞控制, 带来了额外的延迟
i.双工性
在一条TCP连接上, 双方可以同时进行数据的发送和接收
j.应用场景
要求数据绝对准确, 完整性大于速度的场景, 比如: 电子邮件(SMRTP, POP3, IMAP), 文件传输(FTP)
3).UDP(用户数据报)
a.连接性
无连接, 无需建立和断开连接, 想发数据就直接发
b.可靠性
尽最大努力交付, 不保证数据一定到达终点, 可能丢失, 乱序或重复
c.流量控制
无, 发送速率不受接收方控制, 可能因发送过快导致接收方丢包
d.拥塞控制
没有拥塞控制, 即使网络拥堵, 也以恒定速率发送数据, 可能会加剧网络拥堵
e.有序性
每个数据报都是独立的, 不保证先后顺序, 接收端收到的是什么顺序就是什么顺序
f.数据模式
面向报文的, 发送和接收的都是完整的, 有边界的数据; 应用层交付的数据包不会合并也不会拆分
g.首部开销
首部非常简单, 仅包含源/目标端口, 长度, 校验等基本信息
h.传输效率
没有复杂的控制机制, 传输效率高, 延迟低
i.双工性
支持一对一, 一对多, 多对多
j.应用场景
要求传输速度和实时性, 速度大于完整性的场景; 比如: 音视频直播, 语音通话等
2.TCP为什么慢于UDP
TCP慢于UDP, 是因为TCP为了保证可靠性而牺牲部分速度
1).确认应答(ACK)和超时重传
- UDP, 发送方发送完数据就结束了, 不管对方是否收到, 就像仍纸团, 扔出去就不管了
- TCP, 发送方每发送一个数据包, 都必须收到接收方的确认回复(ACK)后, 才会发送下一个; 如果一段时间内没有收到
ACK, 发送方就认为数据包丢了, 会重新发送
由于TCP每次发送和等待ACK都需要等待时间(即RTT - 往返时间), 网络延迟本身就高的环境下(如卫星链路), 这种一来
一回的等待就会非常明显; 重传更是增加了额外的时间成本
2).按序交付
- UDP, 接收方收到什么就立刻上交什么, 不管顺序; 后发的包可能先到
- TCP, 必须保证接收方应用程序读到的数据顺序和发送方发出的顺序完全一致; 如果中间某个数据包丢失或延迟了, 即
使后面的数据包已经到达, 接收方也必须将它们缓存起来, 等待丢失的那个包重传成功并组装顺序后, 才交给应用层
由于TCP需要按序交付, 这造成了队头阻塞, 一个包的丢失或延迟就会拖累后面所有已到达但无序的数据无法及时处理
3).流量控制
- 目的, 防止发送方发送的过快, 导致接收方的缓冲区溢出
- 机制, 主要通过TCP首部的窗口大小来实现, 接收方告诉发送方"我目前还能接收多少数据"; 发送方发送的数据量不能
超过这个窗口
- 带来的延迟, 如果接收方处理速度慢(比如应用程序读取数据慢), 窗口会变小, 设置为0; 发送方就必须暂停发送, 等待
接收方腾出缓冲区并告知新的窗口大小; 这个等待就引入了延迟
4).拥塞控制
- 目的, 防止发送方发送的过快, 导致网络中间设备过载, 它感知网络的承载能力
- 核心机制, 一套复杂的算法(慢启动, 遇到拥塞等), 通过动态调整一个拥塞窗口的值来控制发送速率
慢启动: 连接刚建立时, 发送速率会从很低的值开始, 然后像指数增长一样迅速增加, 直到遇到阈值或发现丢包(网络拥
塞的信号)
遇到拥塞: 一旦检测到丢包(超时), TCP会认为网络堵了, 会剧烈地减小发送速率, 然后再开始缓慢增长
- 带来的延迟/速度变化, 拥塞控制使得TCP的速度不是稳定的, 而是像一个锯齿波一样上下波动, 它永远在试探网络的极限
带宽, 一旦碰壁后就后退; UDP一直以恒定的速率发送, 挤占其他连接带宽, 直到把网络塞满
3.可靠UDP
游戏开发中的可靠UDP是有选择地实现部分TCP的功能, 只会实现当前业务最需要的那部分; 必须实现的功能
1).序列号
a.判断丢包, 没有序列号, 根本无从知道包2和包3之间是不是丢了一个包
b.判断重复包, 网络抖动可能让我们收到两个一样的包, 序列号可以帮我们去重
c.对于需要按顺序执行的指令, 接收方可以依据序列号进行排序后提交给游戏逻辑处理
注: 在接收方, 序列号用于判断和排序, 不一定要像TCP那样阻塞等待; 比如包3到了但包2没到, 对于非关键数据可以直接
处理包3, 对于关键指令则可以等待包2重传后再按顺序处理
2).丢包检测
a.为什么实现
UDP本身不告诉你数据是否到达, 如果枪击指令的包丢了, 玩家会觉得自己开了空枪, 体验极差; 所以你必须自己能检测
到哪个包可能丢了
b.如何实现
- 确认应答(ACK), 接收方收到包后, 会发送一个ACK消息回去, 告诉发送方法我收到了X号包
- 否定确认(NACK), 接收方发现序列号不连续(比如收到了包1和包3, 没有收到包2), 会主动发送一个NACK的消息, 请求
发送方重传包2
- 超时计时器, 发送方为每个已发送的包启动一个计时器, 如果在一定的时间内没有收到对应的ACK, 就认为丢了, 触发
重传
3).重传机制
a.为什么必须
检测到丢包是为了补救, 重传是保证关键数据可靠的最终手段
b.关键: 选择性重传, 不是所有数据都值得重传
- 需要重传的: 关键的指令, 比如玩家释放技能
- 不需要重传的: 比如玩家的位置和朝向, 对于这种数据, 最新的数据永远比旧数据更有价值
4).拥塞控制(强烈建议实现)
a.为什么
如果我们的游戏不顾网络拥堵情况疯狂发送和重传数据, 会拖垮整个局域网或路由器的性能, 最终所有人的连接质量下降
b.我们不需要实现TCP那样复杂的拥塞控制算法, 可以实现一个轻量级, 对游戏友好的版本
- 监测往返时间(RTT)的变化, 如果RTT持续变大, 说明网络可能拥堵了, 应适当降低发送速率
- 监测丢包率, 丢包率上升也是网络拥堵的标志