FPS游戏漫谈弱网环境时延优化

游戏在弱网情况下会变得体验很差,玩家的直观感受就是我的操作怎么没有反应,整个游戏世界都是一卡一顿的。这个就是因为网络问题导致了游戏体验变差。

那什么是弱网环境?弱网环境就是指网络不好的环境,尤其是移动网络下,因为移动网络下,我们经常会进出电梯或者去到一些信号不好的区域,又或者wifi和移动网络处于切换过程中,导致网络突然变坏,此时网络的时延和丢包率会增大。但是当走出这块区域后,网络又正常起来,这就是移动网络面临的问题,这个也是手游网络传输需要解决的技术难题。

从TCP的时延谈起
网络丢包有随机性丢包和相关性丢包两种。

相关性丢包:

链路阻塞
路由器负载过高
无线信号衰减
基站,场景切换
随机性丢包:原因是二进制信道噪声,无规律随机出现,89%的丢包都属于这种。

连续K次丢包的概率是很低的,因此重传是能解决丢包问题,如果2次重传不够,那就发3次。

重传机制
通信的可靠性始终是最需要考虑的指标,而这又是TCP的优点。不少游戏也是在用TCP做游戏通信。在网络良好的情况下,TCP的通信速度其实很快,但到了网络不好的情况下,网络时延会因为TCP的一些机制导致时延进一步增大。比如TCP的三次握手产生的延迟,当网络不佳时,TCP的长链接有可能断开,此时需要多次进行三次握手,增加了不少时间延迟。

TCP的哪些机制还导致了通信时延进一步增大呢?其实是TCP的重传机制。

当发生网络发生丢包时,重传机制是保证传输可靠性的重要手段,但是重传的机制会引入大量的延迟。

重传方式分为三类:定时重传,请求重传和FEC选择重传。

定时重传
发送端如果在发出数据包后一个RTO之后还未收到这个数据包的ACK消息,那么就重传这个数据包。这种方式依赖于接收端的ACK和RTO,容易产生误判,主要有两种情况:

对方收到了数据包,但是ACK发送途中丢失。
ACK在途中,但是发送端的时间已经超过了一个RTO。
所以超时重传的方式主要集中在RTO的计算上,如果你的场景是一个对延迟敏感但对流量成本要求不高的场景,就可以将RTO的计算设计比较小,这样能尽最大可能保证你的延时足够小,但缺点就是很可能对方ACK没来得及到达发送方时,发送方已经开始重传了,使得冗余发送了。例如:实时操作类网游、教育领域的书写同步,是典型的用expense换latency和Quality的场景,适合用于小带宽低延迟传输。如果是大带宽实时传输,定时重传对带宽的消耗是很大的,极端情况会用20%的重复重传率,所以在大带宽模式下一般会采用请求重传模式。

上面提到了RTO,什么是RTO呢?

RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传, RTO协议实现值最小1s。

一般而言,RTO时间是需要大于RTT的,RTT又是什么意思呢?

RTT(Round Trip Time):一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值。

在这里插入图片描述

由于网络波动的不确定性,每个RTT都是动态变化的,所以 RTO 也应随着 RTT 动态变化。

当 RTO < RTT 时, 将会触发大量的重传, 当 RTO > RTT 时候, 如果频繁出现丢包, 重传不及时, 又会造成网络的反应慢, 最好的结果是 RTO 略大于 RTT.

请求重传
请求重传就是接收端在发送ACK的时候携带自己丢失报文的信息反馈,发送端接收到ACK信息时根据丢包反馈进行报文重传。

在这里插入图片描述

这个反馈过程最关键的步骤就是回送ACK的时候应该携带哪些丢失报文的信息。请求重传这种方式比定时重传方式的延迟会大,一般适合于带宽较大的传输场景,例如:视频、文件传输、数据同步等。

FEC选择重传
FEC(Forward Error Correction)是一种前向纠错技术,一般是通过XOR类似的算法来实现,也有多层的EC算法和raptor涌泉码技术,其实是一个解方程的过程,核心思想是冗余发送来减少重传概率。

在这里插入图片描述

在发送方发送报文的时候,会根据FEC方式把几个报文进行FEC分组,通过XOR的方式得到若干个冗余包,然后一起发往接收端,如果接收端发现丢包但能通过FEC分组算法还原,就不向发送端请求重传,如果分组内包是不能进行FEC恢复的,就请求想发送端请求原始的数据包。FEC分组方式适合解决要求延时敏感且随机丢包的传输场景,在一个带宽不是很充裕的传输条件下,FEC会增加多余的冗余包,可能会使得网络更加不好。FEC方式不仅可以配合请求重传模式,也可以配合定时重传模式。

TCP重传机制带来的时延
回顾TCP的重传机制,TCP依靠ARQ进行重传。ARQ协议(Automatic Repeat-reQuest),即自动重传请求,是传输层的错误纠正协议之一,它通过使用确认和超时两个机制,在不可靠的网络上实现可靠的信息传输。

ARQ协议有3种模式:

1.停等ARQ协议

发送M1包后没有收到ACK,那就等到超时才重发M1,然后才能发M2。缺点就是带宽没有利用好,过于空闲了,而且等待时间有点长,包的发送依赖于上一个数据包的成功发送。

在这里插入图片描述

2.连续ARQ协议:可以连续发送好几个数据包,M3的包没有收到ack,因此触发超时重传,此时重传的是M3开始的M3,M4,M5,接收方前面正确收到的M4,M5都会被丢弃(M3以后的包都被丢弃),等待重传的M4,M5。缺点是冗余发送了,丢的是M3,却要重传M3后的包,网络带宽的利用变低了(浪费)。此外,因为网络不好的情况下,重传的M4,M5还是有可能发送丢包,也就是触发了更多重试的可能,也就是网络时延增大了。在出现丢包的时候,一般是网络拥塞,大量的重传又可能进一步加剧拥塞。

在这里插入图片描述

3.SACK:Selective-Ack,选择重传。连续ARQ协议暴露的缺点是,哪些包成功送达、哪些包丢了对于发送端而言并不知情,因此一个思路是在给发送端回ack包时,是否可以把【哪些包成功收到】的信息带回给发送端呢?收到这个信息后的发送端,就知道该重传具体哪一个包了。比如还是上面的例子,M3丢失,但M4,M5成功送达接收端。M4,M5会被缓存起来,发送端只需对M3重传,不必重传M4,M5了。SACK是需要额外开启的。

在这里插入图片描述

上面的问题是由于单纯以时间驱动来进行重传的,都必须等待一个超时时间,不能快速对当前网络状况做出响应,如果加入以数据驱动呢?TCP引入了一种叫Fast Retransmit(快速重传 )的算法,就是在连续收到3次相同确认号的ACK,那么就进行重传。这个算法基于这么一个假设,连续收到3个相同的ACK,那么说明当前的网络状况变好了,可以重传丢失的包了,这就大大减少了重传等待时间。

了解到TCP的重传机制带来的时延增大的原因后,有几个方法来减少时延:

RTO不翻倍。TCP是基于ARQ协议实现的可靠性,TCP每发生一次重传,RTO就加倍,即RTO=2*RTO,该方法称为指数退避。所以当丢包严重时,重传等待时间会越来越长。那么为了减少时延,我们可以减少这个RTO为1.5RTO,假设超时计算是RTO1.5,也就是说假如连续丢包3次,TCP是RTO8,那么新的机制RTO3.375,意味着可以更快地重新传输数据。
使用SACK机制,也就是只重传真正丢失的那个包。
使用快速重传,在连续收到3次相同确认号的ACK,那么就进行重传。
TCP分段、IP分组带来的问题
以太网的链路层对数据帧的长度会有一个限制,其最大值默认是1500字节,链路层的这个特性称为MTU,即最大传输单元。

数据链路层的有效数据,最小46byte,最大一般1500byte,这里的最大就是MTU,MTU表示网络层必须将发给网卡API的包 <= 1500byte,否则调用失败,所以在IP层就必须对大包先分好组,保证传递到数据链路层的数据不会超过一个1500字节。tcp是可靠传输协议,TCP在报文段中发送MSS选项的终端利用该选项来对端TCP实体通告本端点在一个报文段中所能够接受的最大数据长度。若没有指定这个选项意味着本终端能够接受任何长度的报文段。值得注意的是,MSS是为了约束对方发送数据的大小,而不是自己,所以假设自己发送大于MSS的数据,将会在IP层进行分片。如果要传输的数据大于 1500- 20(ip头部) - 20(tcp头部) =1460Byte时,那MSS(最大报文段长度)就是1460字节,当应用层传输的数据超过MSS时,就会触发分片。在ip层会被分片,而 ip层分片会导致,如果其中的某一个分片丢失,因为tcp层不知道哪个ip数据片丢失,所以 就需要重传整个数据段,这样就造成了很大空间和时间资源的浪费。所以一个解决时延的思路,就是控制发送的数据要小于一个MSS,这样就避免了数据分片的发生。

Nagle算法和delay ack机制带来的问题
Nagle算法和delay ack机制是减少发送端和接收端包量的两个机制,可以有效减少网络包量,避免拥塞。但是,在特定场景下,Nagle算法要求网络中只有一个未确认的包, 而delay ack机制需要等待更多的数据包, 再发送ACK回包, 导致发送和接收端等待对方发送数据, 造成死锁, 只有当delay ack超时或者发送方等待超时后才能解开死锁,进而导致应用侧对外的延时高。

这里简单介绍下Nagle算法和delay ack出现的背景:考虑发送一个字节的的情景,每次发送一个字节的有用数据,就会产生41个字节长的分组,20个字节的IP Header 和 20个字节的TCP Header,这就导致了1个字节的有用信息要浪费掉40个字节的头部信息,这是一笔巨大的字节开销,而且这种Small packet在广域网上会增加拥塞的出现。Nagle算法可以在发送端收集小包后一次发送,delay ack就是在接收端等待一会儿对多个ack包一次发送。

在默认的情况下,Nagle算法和延迟ACK是默认开启,也就是说TCP_NODELAY和TCP_QUICKACK默认关闭。如果服务器开启延迟ACK、客户端开启Nagle算法 ,就很容易导致网络延迟增大。所以为了减少网络时延,可以开启TCP_NODELAY 和TCP_QUICKACK。

可靠UDP
对于游戏开发,尤其是MOBA(多人在线竞技)游戏,延迟是需要控制的。但是对于传统的TCP,并不利于包的实时性传输,因为他的超时重传和拥塞控制都是网络友好,对于我们包的实时性或者在弱网下的传输,没有优势。所以一般都是需要基于UDP去实现一套自己的网络协议,保证包的实时,以及可靠,这种协议我们统称为RUDP(Reliable-UDP)。本质上就是以空间(更多的带宽)换时间(更小的时延)。

RUDP技术要点
结合TCP可靠传输的策略,再根据我们时延的需求大于带宽的需求,RUDP的实现要点有以下几点:

1.使用定时重传和选择重传的机制来对抗丢包,这个重传策略可以在应用层实现,底层传输层用的是UDP。

2.UDP传输是无序的,需要在应用层利用序列号标记每个包,实现包的保序。

3.定时重传的RTO不再选择TCP那种二进制退避的RTO增长策略,而是采用更短的RTO来进行重传,比如1.5RTO,即缺点是冗余重传,浪费了带宽,好处是减少了重传时间,优化了网络时延。

4.使用选择重传,即在接收方发起ACK时,需要带上丢包的序列号,发送端知道具体丢的包后,只重传丢失的包,其他的包不再重传,因为接收端成功接收到的包都被缓存起来了。重传的包越少,丢包的概率就越低。

5.快速重传,模拟TCP快重传的策略,发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1, 3, 4, 5,当收到ACK3时,发送端知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。

6.冗余发送,比如一次发送可以携带着多个之前还未收到确认的包一起发送,即使还未超时。比如发送端需要发送1,2,3,4,5几个包,那冗余发送的策略就是:1,12, 23, 34, 45。通过冗余包,来避免重传。

7.UDP分组优化:IP包过大会分组,IP分片越多,UDP包丢包概率就越大。策略就是将一个UDP大包分为几个UDP小包来发送。以太网:1500-20-8=1472;英特网:576-20-8=548。因为还存在各种子网,所以经验总结最佳包大小应限制在470字节以下。

在这里插入图片描述

7.流量控制可以采取与TCP的滑动窗口机制一样;拥塞控制也采取TCP的拥塞控制机制(慢开始、拥塞避免),但这个需要做成可关闭的选项,因为在追求极致的时延时,此时会非常自私的进行发送,不再考虑网络拥塞的情况。

在这里插入图片描述

TCP和RUDP在网络良好和网络丢包严重下的性能对比:

在这里插入图片描述

KCP
这里介绍一个游戏界常用的基于UDP的可靠传输协议KCP,在一些强调实时性的竞技游戏,多数采用KCP的来做网络传输。

KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。TCP信道是一条流速很慢,但每秒流量很大的大运河,而KCP是水流湍急的小激流。KCP有正常模式和快速模式两种,通过以下策略达到提高流速的结果:

RTO翻倍vs不翻倍: TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖,而KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度。
选择性重传 vs 全部重传: TCP丢包时会全部重传从丢的那个包开始以后的数据,KCP是选择性重传,只重传真正丢失的数据包。
快速重传: 发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1, 3, 4, 5,当收到ACK3时,KCP知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/399718.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java毕业设计-基于springboot的人才招聘管理系统-第68期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于springboot的人才招聘管理系统&#xff1a;前端jquery、easyui&#xff0c;后端 maven、springmvc、spring、jpa、hibernate&#xff0c;集成职位浏览、我的简历、投…

web前端宝藏题库,适用于0-3年前端工程师...

前些天意外认识了一个前端大佬&#xff0c;某大厂在职&#xff0c;我怕打扰就没敢多聊&#xff0c;没想到大佬挺平易近人&#xff0c;很亲切的给我出谋划策&#xff0c;还给了我一套面试题&#xff0c;难度不是很高&#xff0c;但是胜在全面十分契合我现在的状况&#xff0c;所…

在VsCode中通过Cookie登录LeetCode

在vscode中配置好leetcode之后&#xff0c;一般最常用的就是通过cookie登录leetcode ; 首先点击sign in &#xff0c; 然后选择最下面的 &#xff0c; LeetCode Cookie ! 然后输入username(也就是你的lc用户名) 或者 你leetcode绑定的邮箱 ; 输入完成之后 ; 就是要你输入你的l…

UE5 C++ 创建可缩放的相机

一.要将相机设置在Pawn类里 1.在MyPawn头文件里&#xff0c;加上摇臂和相机组件 #include "GameFramework/SpringArmComponent.h" #include "Camera/CameraComponent.h" 2.在Pawm里声明SceneComponet&#xff0c;SpringArmComponent,CameraComponent组件…

6.【架构师成长之路】职场新人:维护一张能力图谱

文章目录 导言一、能力图谱就是技能体系1、时刻提醒你&#xff0c;你有一个目标2、你可以知道靠近目标的具体方式3、你会变得更加自信 二、周期性review自己的能力图谱1、review能力项进度2、review能力项完整度3、固定周期review 本文总结说明 导言 上两篇文章我们讲了&#…

C语言中关于#include的一些小知识

写代码的过程中&#xff0c;因为手误&#xff0c;重复包含了头文件 可以看到没有报错 如果是你自己编写的头文件&#xff0c;那么如果没加唯一包含标识的话&#xff0c;那么编译器会编译报错的。如果是系统自带的头文件&#xff0c;由于其每个头文件都加了特殊标识&#xff0c…

【动态规划】【矩阵快速幂】LeetCode2851. 字符串转换

作者推荐 【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II 涉及知识点 【矩阵快速幂】封装类及测试用例及样例 LeetCode 2851. 字符串转换 给你两个长度都为 n 的字符串 s 和 t 。你可以对字符串 s 执行以下操作&#xff1a; 将 s 长度为 l &#xff08;0 <…

安达发|APS排产软件的机台产线任务甘特图功能详解

在现代制造业中&#xff0c;高级计划与排产是制造业运营的关键环节。为了提高生产效率、降低成本并确保产品质量&#xff0c;企业需要对生产过程进行精细化管理。APS&#xff08;高级计划与排产&#xff09;系统作为一种先进的生产计划和调度工具&#xff0c;可以帮助企业实现这…

在计算机上设置和使用 KVM

为了使用 gem5 的 KVMCPU 来快进你的模拟&#xff0c;你必须有一个 KVM 兼容的处理器并且在你的机器上安装了 KVM。本页将引导您完成在计算机上启用 KVM 并将其与 gem5 一起使用的过程。 注意&#xff1a;以下教程假设 X86 Linux 主机。本教程的各个部分可能不适用于其他体系结…

IBM V5000存储更换控制器及电源模块

LED故障状态 后面板故障状态 系统内电源模块报错信息(可安全卸下状态为"是"&#xff0c;此时可直接热拔插) 控制器报错信息&#xff08;当前已是脱机状态可直接拔插&#xff0c;该型号控制器不需要更换缓存可直接热拔插更换&#xff09; 更换故障备件应先核对新旧备件…

CTFHub技能树web之RCE(二)

第五题&#xff1a;远程包含 根据题目&#xff0c;使用远程包含进行 打开phpinfo&#xff0c;可以看到allow_url_fopen和allow_url_include都是On&#xff0c;因此可以使用php://input&#xff0c;由于代码会检查file中的内容&#xff0c;因此不能够使用php://filter包含文件&a…

C++正则表达式笔记

最近翻了翻正则表达式的一些资料&#xff0c;做个记录。 1、微软官方 <regex> 函数 | Microsoft Learn 2、正则表达式语法简介 正则表达式语法简介 - 简书 3、正则表达式基础语法大全 正则表达式基础语法大全_正则表达式语法大全-CSDN博客 4、练习 &#xff08;1…

ffmpeg TS复用代码详解——mpegtsenc.c

一、mpegtsenc.c 整体架构 二、主要函数 mpegts_write_pes(AVFormatContext *s, AVStream *st, const uint8_t *payload, int payload_size, int64_t pts, int64_t dts)这个函数就是TS打包的主函数了&#xff0c;这个函数主要功能就是把一帧数据拆分成188字节的TS包&#xff0…

openai DALL-E 3 从文本描述生成图像原理通俗解释

序言 在数字时代&#xff0c;图像生成技术正日益成为人工智能领域的热点。 本讨论将重点聚焦于两个备受瞩目的模型&#xff1a;DALL-E和其他主流AI绘图方法。 我们将探讨它们的优势、局限性以及未来的发展方向。通过比较分析&#xff0c;我们期望能够更全面地了解这些技术&a…

Datawhale零基础入门金融风控Task1 赛题理解

Task1 赛题理解 Tip:本次新人赛是Datawhale与天池联合发起的0基础入门系列赛事第四场 —— 零基础入门金融风控之贷款违约预测挑战赛。 赛题以金融风控中的个人信贷为背景&#xff0c;要求选手根据贷款申请人的数据信息预测其是否有违约的可能&#xff0c;以此判断是否通过此项…

jenkins的nmp install命令无法下载包

问题&#xff1a;在jenkin的流水线脚本中执行到&#xff1a;npm install命令后无法下载前端依赖包 1、进到jenkins的工作目录&#xff0c;一般在底层为/var/lib/jenkins/workspace/任务名称 cd /var/lib/jenkins/workspace/xkc处理方式&#xff1a; # 查看镜像源 npm config …

​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】

欢迎来CILMY23的博客喔&#xff0c;本期系列为​【C语言】长篇详解&#xff0c;字符系列篇3-----strstr&#xff0c;strtok&#xff0c;strerror字符串函数的使用【图文详解​】&#xff0c;图文讲解各种字符串函数&#xff0c;带大家更深刻理解C语言中各种字符串函数的应用&am…

日常的一些异常

Column ‘id’ in where clause is ambiguous 这个错误in where clause is ambiguous多半是因为多表查询的时候几个表中同时出现了某个相同的列名&#xff0c;而在查询条件WHERE后面又没有指定是那个表&#xff0c;而引起的,又或者是查询结果里面有两个相同的列名&#xff0c;…

[notice] A new release of pip is available: 23.2.1 -> 24.0

翻译之后&#xff1a;〔通知〕新版本的pip可用&#xff1a;23.2.1->24.0 就是说&#xff0c;你的pip版本需要从当前的 23.2.1 升级到最新版本 24.0&#xff0c;执行如下命令&#xff1a; cmd命令以管理员身份进入目录 ${Python}\Python3.12.1\Scripts下&#xff0c;执行 p…

好用便签:如何利用备忘录高效处理待办事项?

在快节奏的现代生活中&#xff0c;我们需要处理各种各样的待办事项&#xff0c;从个人生活琐事到工作任务。如何利用备忘录高效处理待办事项&#xff0c;成为了提升效率和生活质量的关键。一个合理的待办事项规划不仅能帮助我们明确目标&#xff0c;还能让我们更加有条不紊地应…