当前位置:网站首页>服务器正文18:UDP可靠传输的理解和思考(读云凤博客有感)
服务器正文18:UDP可靠传输的理解和思考(读云凤博客有感)
2022-06-28 06:47:00 【谢白羽】
一、什么时候用UDP比TCP好
需求
有人期望基于 UDP 协议通讯来获得更快的响应速度,而又想让通讯流像 TCP 一般可靠尝试
1)一条路是寄希望于业务逻辑上允许信息丢失:比如,在同步状态中,如果状态是有实效性的,那么过期的状态信息就是可丢失的。这需要每次或周期性的全量状态信息同步,每个新的全量状态信息都可以取代旧的信息。或者在同步玩家在场景中的位置时可以用这样的策略。不过在实际操作中,我发现一旦允许中间状态丢失,业务层将会特别难写。真正可以全量同步状态的场合也非常少。
2)不允许信息丢失,但允许包乱序会不会改善?一旦所有的包都一定能送达,即丢失的包会用某种机制重传,那么事实上你同样也可以保证次序。只需要和 TCP 一样在每个包中加个序号即可。唯一有优势的地方是:即使中间有包晚到了,业务层有可能先拿到后面的包处理。
3)允许消息丢失,包次序无关:
在网络状况不好的时候,我们可以看到有时采用短连接反而能获得比长连接更好的用户体验。不同的短连接互不影响,无所谓哪个回应先到达。如果某个请求超时,可以立刻重新建立一条新的短连接重发请求。这时,丢包重发其实是放在业务层来做了。而一问一答式的小数据量通讯,正是 TCP 的弱项:正常的 TCP 连接建立就需要三次交互,确定通讯完毕还需要四次交互。如果你建立一次通讯只为了传输很少量的一整块数据,那么明显是一种浪费。这也是为什么 google 的 QUIC 对传统的 http over TCP 有改善的空间
二、一个可靠的 UDP 通讯模块的 API 接口该如何设计
注意要点
最糟糕的地方是,通讯模块本身和 UDP 绑的太死,也就是这个模块本身负责了 UDP 包的收发。(整个模块应该只提供输入和输出数据包的接口,和网络通讯 api 无关。)代码示例
struct rudp_package {
struct rudp_package *next;
char *buffer;
int sz;
};
struct rudp * rudp_new(int send_delay, int expired_time);
void rudp_delete(struct rudp *);
// return the size of new package, 0 where no new package
// -1 corrupt connection
int rudp_recv(struct rudp *U, char buffer[MAX_PACKAGE]);
// send a new package out
void rudp_send(struct rudp *U, const char *buffer, int sz);
// should call every frame with the time tick, or a new package is coming.
// return the package should be send out.
struct rudp_package * rudp_update(struct rudp *U, const void * buffer, int sz, int tick);
- 游戏中的注意点
一般在网络游戏或其它需要低延迟的应用中,我们都需要定期保持心跳,以检查连接质量。所以必然会周期性的调用维持用的 api ,这和一般网络应该是不同的。
接口模块使用解析
1)业务模块按时间周期(时间片)调用udp更新信息
这里提供了一个 rudp_update 的 api 要求业务层按时间周期调用,当然也可以在同一时间片内调用多次,用传入的参数 tick 做区分。如果 tick 为 0 表示是在同一时间片内,不用急着处理数据,当 tick 大于 0 时,才表示时间流逝,这时可以合并上个时间周期内的数据集中处理
2)rudp_update 的每次调用均可以传入一个实际收到的 UDP 包(可以是一个完整的 UDP 包,也可以是一部分),这个包数据是一个黑盒子,业务层不必了解细节。它的编码依赖对端采用的相同的 rudp 模块。
3)每次调用都有可能输出一系列需要发送出去的 UDP 包。这些数据包是由过去的 rudp_send 调用压入的数据产生的,同时也包含了最近接收到的数据包中发现的,对端可能需要重传的数据,以及在没有通讯数据时插入的心跳包等。总的来说,rudp_update 内部做了所有的可靠化通讯需要的数据组织工作。使用的人传入从 UDP socket 上收到的数据(不包括数据加密或其它数据组织工作),并从中获取需要发送到 UDP socekt 的数据。总结
而业务层的数据收发只需要调用 rudp_send 和 rudp_recv 即可。其中,rudp_recv 保证数据包按次序输出;rudp_send 也并不真正发送这些数据包,而是堆积在 rudp 对象内,等待下一个时间片。(rudp_new 创建 rudp 对象时,有两个参数可配置。send delay 表示数据累积多少个时间周期 tick 数才打包在一起发送。expired time 表示已发送的包至少保留多少个时间周期。和 TCP 不同,我们既然使用 udp 通讯,就是希望高响应速度,所以即使数据抱迟迟没有送达,它们也不必保留太长时间,而只需要通知业务层异常即可)固定格式
0 心跳包
1 连接异常
2 请求包 (+2 id)
3 异常包 (+2 id)
后两种数据需要跟上两字节的序号(采用大端编码)
三、一个简单的实现
四、一些备注
1)tcp麻烦的是在wifi情况下的rto问题,fps,moba对延时很敏感,所以基本上都用udp,有过球类游戏用过tcp,统计发现比较糟糕。在国内最好udp包大小最好不超过576,我们踩过坑(国内一些设备制造商没有严格遵循rfc标准),576最安全,fps,moba,球,车类的上行包大多数不超过100字节,下行不超过300,所以大多数情形下不会因为分包造成有效payload减少
2)UDP可靠库:KCP、enet
3)资料索引
1.On Latency and Player Actions in Online Games
2.The effects of loss and latency on user performance in unreal tournament 2003
3.Matchmaking in multi-player on-line games studying user traces to improve the user experience
4)上述代码的问题
算法和UNREAL3中的是一致的,也是采用请求机制,无请求默认消息包对方已收到,发送方需要一个发送缓存区,一个缓存的数据包超过心跳时间就会清掉,如果缓存满了,就是网络出了问题,弹框异常了
5)在相同的网络环境下,特别是跨网(比如说电信与联通),UDP有可能比TCP块120倍。这是几年内严格测试得出的结论。
6)固定MTU。但是可以很负责的告诉你,很多路由特别是企业路由,仅允许每次通过512字节的UDP。所以QQ是会在登录期间测试这个值。
边栏推荐
- [interval DP] stone consolidation
- Linux Mysql 实现root用户不用密码登录
- 职场IT老鸟的几点小习惯
- 2 startup, interrupt and system call
- Paper recommendation: efficientnetv2 - get smaller models and faster training speed through NAS, scaling and fused mbconv
- ImportError: cannot import name 'ensure_dir_exists'的可解决办法
- Servlet value passing JSP
- 助力涨点 | YOLOv5结合Alpha-IoU
- Introduction to Qualcomm platform WiFi -- Wi Fi display software
- KMP string
猜你喜欢

js正则表达式系统讲解(全面的总结)

编译原理期末复习

AutoCAD C# 多段線小銳角檢測

JS regular expression system explanation (comprehensive summary)

The code is correct, and the rendering page does not display the reason

搭建你jmeter+jenkins+ant

Speech enhancement - spectrum mapping

Interpretation of Blog

VM332 WAService.js:2 Error: _vm.changeTabs is not a function报错

The custom cube UI pop-up dialog supports multiple and multiple types of input boxes
随机推荐
How to open UMD, KMD log and dump diagrams in CAMX architecture
VM332 WAService. js:2 Error: _ vm. Changetabs is not a function
Shell script one click deployment (MySQL)
MySQL (I) - Installation
调接口事件API常用事件方法
FPGA - 7系列 FPGA SelectIO -09- 高级逻辑资源之IO_FIFO
手把手教你用Ucos
Freeswitch sets the maximum call duration
[rust translation] implement rust asynchronous actuator from scratch
Iframe switching in Web Automation
[rust daily] published on rust 1.43.0 on April 23, 2020
fpm工具安装
pytorch RNN 学习笔记
OpenGL API learning (2008) client server client server
It will cost 700 yuan to realize this issue. Does anyone do it?
The custom cube UI pop-up dialog supports multiple and multiple types of input boxes
最后的二十九天
Configure redis from 0
Interpretation of Blog
Triode driven brushless motor