闲聊:最近想要做一个用在网络游戏领域的网络控制协议小玩具,大体聊聊构思的想法

本文主要是介绍闲聊:最近想要做一个用在网络游戏领域的网络控制协议小玩具,大体聊聊构思的想法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这个控制协议跟 “猛禽 UDP/IP Aggligator 宽频聚合器”,是不同的,因为预期该网络控制协议,预计侧重在网络游戏加速这块。

所以它与多数网络控制协议,会有一定的不同。

但要先分析具体的目标场景,基本现代所有的 UDP/IP 应用层网络协议,都具备丢包重传控制,当然有一些是不在乎丢包的。

所以,一般来说保证 UDP/IP 不丢包,是没有太大意义的,因为这只会出现以下两种情况:

1、UDP/IP 网络应用,可以丢包,它们会忽略这类问题,因为不重要。

2、UDP/IP 网络应用,自己带了,可靠CC(类似 KCP/TCP/QUIC 的控制协议)。

那么类似网络应用产品(有):

1、灵境奇谈 (封灵笔记)

2、CS:GO    (反恐精英:全球攻势/cs2)

3、Warframe(星际战甲)

回到正题,既然知晓了问题的所在,那么就来开始按照不同的应用场景来调优,每个不同的网络应用调优方法是不同的。

但,我们通常预期目标都是为了减少网络抖动、只要产生丢包,那么网络抖动就会非常严重,而且在现代网络之中,因为路由跃点之间的不同,客户端到服务器之间的网络路由是随时波动的,即便路由没有变动,也会因为路由本身的QOS管理策略,导致帧的先后顺序发生改变(即乱序问题)产生抖动问题,因为若客户端需要排序,那么在收到来自未来帧时,是先缓存在接收方队列之中,等待确认帧(包)的到达,在这个空窗期就会产生抖动问题。

所以对于游戏加速,为了质量最好,会有两个以下解决方案:

1、对于UDP/IP带控制协议的网络游戏

我们可以采用,当收到游戏UDP数据包之后立即发出到服务器,并且为每个数据包标记帧序号,接收方,可以根据在1 ~ 5个毫秒内跳过的帧号,来评估网络的丢包层度。

注意:是采用旁路观察的办法,即控制协议来决定,发送方应该发送多少个冗余包,即:当收到客户端A包之后,发出几个包到服务器,如果丢失了一个包、由于冗余多发,虽然占用了更大的带宽,但延迟、抖动这块是基本被抹平了。

比如:

我们可以通过测算出来的每个帧需要发送几次,可以评估为:10个包丢一个包,则冗余发送一个包,5个包丢一个包,则冗余发送两个包,虽然这可能浪费更多的网络带宽。

网络之中5个包丢一个包,放大到3倍,产生一个包都无法到达的可能性几乎是没有的,在保证RTT、抖动不变大的情况,这是最佳解决方案,即通过冗余传送来保证。

但接收方一般拿需要确保不会出现重复帧转发的问题,即可:接收方收到已经转发过的帧不能再次转发,但为了可靠性也可以再次转发过去,让目的服务器自己处理它,但前提是目的是服务器允许这样的行为,这个要靠人们自己去测算。

网络游戏加速一般都是定向分析,定向研究加速解决方案的。

另外一种是做一个带ARQ(自动请求重传)的控制协议,这种像TCP/IP流控协议的做法就没有那么好了。

我们可以如此这般设计并处理,但它仍旧会产生一个抖动问题。

1、帧带上序号跟确认号。

2、接收方收到帧后,如果是当前确认号就确认并且交付

     如果序号小于确认号,只要不是序号回绕问题,就丢弃帧。

     如果序号大于确认号,就预先缓存在接收方队列之中。

     当然如果缓存的数据包,已经占用了窗口大小的 50% 以上,就立即发出ACK+NAK包

     另外为了提高效率,当连续收到 “2 ~ 3” 个来自未来的包(跳过),那么可以立即发出ACK+NAK 包,但建议是放在跟下面 Delay 一毫秒后在处理。(可以理解为:快速重传机制)

     否则检查设定一个毫秒后ACK的计时器句柄,在一个毫秒之后检查,因为这一个毫秒过后可能网卡会达到无数个包,此时在判断那些包已经收到,那些包被跳过,在发出ACK+NAK数据包告诉服务器,可以一定减少昂贵的带宽资源浪费。

       

     接收方收到ACK+NAK包以后,更新发送方的窗口大小,并且删除自己的已经缓存的数据包(尽快释放内存资源占用)、并且NAK上面指示的没有收到包重新发送出去。

    注意:NAK为否定应答(即接收方发出没有收到的帧到发送方),对的网络游戏加速,比较适合按照帧来确认,帧填充的方式来传输,而不是流式传输。

   重传时间(测算):默认情况下单位为1秒,在第一个测量出来的RTT(往返时间)这个可以从链接握手时确定。

   即:客户端发送链接建立的帧、到服务器应答客户端应答帧的第一个RTT时间作为SRTT、RTT_VAL(RTT/2)初始值。

关于重传时间的时间可以采用下面的公式,TCP/IP(但需要魔改)

R = RTT

SRTT = 平滑RTT时间

RTTVAL = RTT因子

第一个RTT测量:

K = 4(常数)

SRTT = R

RTTVAR = R/2

RTO = SRTT + max(G,K*RTTVAR)

第二个RTT测量:(及之后)

注意:它需要通过 “NPU/SSE” 浮点数来运算

alpha=1/8

beta=1/4

RTTVAR = (1 - beta) * RTTVAR + beta * |SRTT - R|            

SRTT = (1 - alpha) * SRTT + alpha * R

RTO = SRTT + max (G, K*RTTVAR)

补充:

G为时间精度常数,在内核之中它被设定为200毫秒。

Linux TCP_RTO_MIN, TCP_RTO_MAX and the tcp_retries2 sysctl (pracucci.com)

#define TCP_RTO_MAX ((unsigned)(120*HZ))
#define TCP_RTO_MIN ((unsigned)(HZ/5))

下述为KCP控制协议算法的RTO重传时间计算公式:

      它与上面的公式区别不大,只是对系数微调的区别,即:KCP约为TCP/IP控制协议重传时间除以1.25~1.5倍左右。

static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt)
{IINT32 rto = 0;if (kcp->rx_srtt == 0) {kcp->rx_srtt = rtt;kcp->rx_rttval = rtt / 2;}	else {long delta = rtt - kcp->rx_srtt;if (delta < 0) delta = -delta;kcp->rx_rttval = (3 * kcp->rx_rttval + delta) / 4;kcp->rx_srtt = (7 * kcp->rx_srtt + rtt) / 8;if (kcp->rx_srtt < 1) kcp->rx_srtt = 1;}rto = kcp->rx_srtt + _imax_(kcp->interval, 4 * kcp->rx_rttval);kcp->rx_rto = _ibound_(kcp->rx_minrto, rto, IKCP_RTO_MAX);
}

     但同时,需要设置一个最小RTO时间的边界值(临界值),这个值根据实际生产需要,可以设置为40、100毫秒即可,但对于游戏加速这种场景设置为40 ~ 60毫秒,一般会比较合理,RTO不能太小,否则会浪费大量带宽的,我们应该通过快速重传的方式来处理,而不是依赖于设计的控制协议RTO自动重发来处理。

       

关于发送方,因为根据接收方传递过来的窗口大小来发送数据,为了提高效率:应该是对面接收方有足够接受能力收到足够大的字节数时,一次性把发送队列之中带传输的数据包输出过去,在根据接收方投递的ACK/NAK来实时更新窗口大小,以便评估是否需要继续发送数据到接收方。

但需要注意一点:接收方跟发送方之间,建议采用 “背负式确认” 的方式来处理,这可以减少没必要单独的 ACK 报文发出,节约两端的网络带宽资源,但这可能会带来一定对于网络控制协议栈编程的复杂度。

RTO 值得计算用KCP修改后的系数会比较好一些,当然人们仍旧可以大胆得重新调整系数,无外乎是牺牲更多带宽,还是更小网络带宽得问题,另外也可以选用 BBRplus(BBR+)得调效模式,尽量避免提高RTO重传时间,以避免网络吞吐效率的降低,但这会增加网络拥塞层度的问题。

补充:

简单说一说SWS问题(糊涂窗口综合症问题)

在控制协议之中,我们大家都是通过窗口来评估对方的接收能力,如果对方窗口堆满,我们就不能发送。

所以对方只要收取且交付数据就需要更新窗口大小,但不可能为了更新窗口大小,每次都去立即发出ACK报文。

我在上篇文中没有着重的探讨这个问题,而是说推迟确认(延迟1毫秒)都是为了解决,类似如这个SWS糊涂窗口问题。

但仍可以通过算法评估解决该问题的ACK发出时机。

可以窗口边缘滑动了:
1/4缓冲区 or 4个MSS大小

来评估是否立即发出ACK报文,迫使发送方更新接收方窗口大小,否则应在下个正常发出帧数据背负式确认。

这篇关于闲聊:最近想要做一个用在网络游戏领域的网络控制协议小玩具,大体聊聊构思的想法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/935618

相关文章

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

poj1330(LCA最近公共祖先)

题意:求最近公共祖先 思路:之前学习了树链剖分,然后我就用树链剖分的一小部分知识就可以解这个题目了,记录每个结点的fa和depth。然后查找时,每次将depth大的结点往上走直到x = y。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring>

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

poj 2112 网络流+二分

题意: k台挤奶机,c头牛,每台挤奶机可以挤m头牛。 现在给出每只牛到挤奶机的距离矩阵,求最小化牛的最大路程。 解析: 最大值最小化,最小值最大化,用二分来做。 先求出两点之间的最短距离。 然后二分匹配牛到挤奶机的最大路程,匹配中的判断是在这个最大路程下,是否牛的数量达到c只。 如何求牛的数量呢,用网络流来做。 从源点到牛引一条容量为1的边,然后挤奶机到汇点引一条容量为m的边

配置InfiniBand (IB) 和 RDMA over Converged Ethernet (RoCE) 网络

配置InfiniBand (IB) 和 RDMA over Converged Ethernet (RoCE) 网络 服务器端配置 在服务器端,你需要确保安装了必要的驱动程序和软件包,并且正确配置了网络接口。 安装 OFED 首先,安装 Open Fabrics Enterprise Distribution (OFED),它包含了 InfiniBand 所需的驱动程序和库。 sudo