网络游戏开发中的通讯杂谈

概述

目前主流游戏服务框架大部分使用tcp互相通讯,随着rudp方案的日趋成熟,客户端和服务器之间采用udp连接的方案也越来越多。本篇文章将会由浅入深地介绍一下服务端业务中采用的一些通讯方案和一些优化手段。
在这里插入图片描述

文章大致分为以下几块内容:

  • TCP在服务端的使用和优化
  • 使用基于UDP的协议对游戏延迟的优化。这块会主要介绍KCP的一些使用和对UDP参数的调整
  • 个人在开发中积累的一些关于网络连接的经验
  • 题外话:KCP在开源界的兴起

一些比较常见的基础知识在网上已经很多文章进行过介绍。本文由于篇幅限制就不再赘述了。

TCP在服务端的使用和优化

TCP为游戏服务端最常用的通讯手段,它的稳定性和可靠性毋庸置疑。从早期的局域网对战游戏红警、cs等到现在多到满地爬的各种类型的页游都少不了它的身影。很多开发小白可能会觉得,只要我学会socket的基本操作,再结合流行的epoll或者iocp模型,就可以跟别人吹嘘我做过服务器框架了,但是其实tcp的开发细节远不止这么简单。

TCP从三次握手到通讯期间的拥塞控制再到关闭过程的四次挥手,基本上每个阶段每个状态的切换都有各种参数供开发者调节(这里只针对linux下开发)。其实针对建立连接过程和中间通讯过程的优化,我们也主要是通过调一些内核参数来实现的。下面笔者展示一下debian下面可供调节的tcp参数。

linux下tcp相关系统内核参数调节

一些基础性的通用调优参数,建议记住它们是做什么用的,比如tcprmem/tcpwmem是调整tcp默认读写缓存,tcptwreuse重用time_wait连接等等。再复杂一些的参数可以google或者阅读linux的tcp源码。在shell下执行ls /proc/sys/net/ipv4/tcp_* 可以看到:
在这里插入图片描述
上面大致有几十项参数,没有接触过这块内容的同学可能觉得非常头大。 其实大部分的参数笔者也不知道是干什么用的,但是假如tcp在使用期间出现了各种各样的问题,就需要我们学会如何去查哪些参数能对应的解决问题,这就需要对tcp有更深入的了解。

一些基础性的通用调优参数,建议记住它们是做什么用的,比如tcprmem/tcpwmem是调整tcp默认读写缓存,tcptwreuse重用time_wait连接等等。再复杂一些的参数可以google或者阅读linux的tcp源码。

幸运的是,大部分的默认参数已经可以满足我们的各种需求,是不需要调整的。

对于线上运营的产品来说,一般的策略是如果没有问题就尽量不要改,不知道参数用途的不要瞎改,笔者只推荐开启一个参数:

net.core.defaultqdisc: fq
net.ipv4.tcpcongestion_control: bbr

Google的bbr算法是支持单边开启的,会替代原生的tcp拥塞策略,对于提升tcp通讯效率有帮助。

调整参数属于牵一发而动全身的行为,尽量先在测试机上做对比观察,然后线上做好AB测试,毕竟出了运营事故就麻烦了。

前面说到我们除了断开连接部分基本上都可以通过调整内核参数来优化,断开连接作为一个看似非常不起眼的操作,事实上里面的学问也多得很,下面针对断开连接做一下展开说明。

断开连接问题

先抛出一个问题:

服务端对某个客户端的socket,执行了send(socket, msg),然后 close(socket), 会发生什么?对方客户端能收到这个msg吗?这样的行为对服务端会有影响吗?

( 如果你能回答上述问题,可以直接跳过这一章节的内容。)

通常开发者使用close关闭socket,它默认行为是尽量在后台执行优雅关闭动作,但是本身这个行为并不算可控,socket虽然释放,程序失去了对这条连接的控制权,剩下的生命周期全权交给操作系统内核来维护,所以假设我们的网络处于非常拥堵的状态,系统可能会随时将连接回收掉。

这里我们可以看一下以下内容里的描述:

https://docs.microsoft.com/en-us/windows/win32/winsock/graceful-shutdown-linger-options-and-socket-closure-2

所以close之后我们并不清楚msg什么时候,或者到底有没有到达对端。close相当于我们告诉内核,这个socket我用完了,你来处理后续的工作,我不管了。

但是做为一个优秀的服务端框架开发者,我们应该把这个过程牢牢掌握在自己手里:我虽然关了你,但是我会尽量保证把没发出去的消息给你发完,假如我实在发不完,我就主动退出做清理工作释放连接。

TCP提供一个SOLINGER选项让开发者可以接管这个close行为,这个选项需要指定一个超时时间,系统会在指定时间内尽量发送未发送完的数据,当超过超时时间还没有传输完成系统会清理socket,这里网上流传的说法有的是清理sendbuff,但是笔者在wsl下测试的结果并不相同,表现上是tcp连接由内核接管依旧尝试发送完剩余包,然后走到timewait状态,但是不管如何,我们确定这里的超时行为依然是不确定的,而且这样设置会带来一个问题: close变成了阻塞调用(无论socket本身是阻塞还是非阻塞),会block线程。

那么还有没有更好的方式来控制这个关闭行为呢?

下面介绍一下行为可控的优雅关闭流程,如果大家看了上面的关于gracefull-shutdown的链接,链接中也介绍了这个流程:

由服务端发起shutdown(write)代

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值