上市后问题的发生和解决的攻防战
第一次向市场推出ProudNet之前觉得11年时间的游戏服务器开发经验应该足够了。直到使用我的服务器引擎的游戏在东南亚市场的发布。
基于ProudNet的一款游戏在新加坡推出,并由马来西亚等周边国家的玩家接入了游戏。但出现了部分玩家中途不能继续玩的状况。 这个问题短时间内未能解决。最终我乘坐飞机去了新加坡。 而在现场我找到了问题的原因。这是部分游戏用户的路由器造成的原因。
在开发ProudNet时我曾调查过用于P2P hole punching的技术。 路由器大体分为4种,其中最难实现hole punching的机种是Symmetric NAT 路由器。
只通过普通的方法无法在Symmetric NAT中实现hole punching。 所以需要端口预测技巧。只要使用端口号预测方法,虽然达不到100%,但可以实现hole puching。
但问题是这个端口预测技巧会引起副作用。在下图中假设两个客户端都在Symmetric NAT后端,为了预测两个客户端之间的端口号,需要多次尝试hole punching。
在这过程中NAT路由器端将产生很多端口映射条目。但由于安全上 的考虑,几个共享端是禁止生成数千个以上的端口映射条目的。
但我还是解决了这个问题,就是降低了端口映射条目的生成数量。针对Symmetric NAT的端口预测技术成了等待数秒之后也未进行hole punching时的最终尝试手段。
还有一些路由器中可能会出现hole punching消失的状况。是在部分路由器中发生的问题。客户端1位于路由器后端,这时它与客户端2进行hole punching,还跟客户端3,4,5,6也进行hole punching。
在大部分计算机中这些都不成问题。 但在部分路由器中只要进入这一阶段客户端1和2之间的通信就会断开。
告诉你们一个有趣的现象,在中国的网吧未出现过这种现象,而发生在部分韩国路由器中。
Hole punching的本质其实是在路由器内添加端口映射条目。
由端点和外部端点构成的tuple集合叫做端口映射条目。
在出现问题的路由器中,不允许内部端点相同而外部端点不同的情形,这就是原因。
我们是这样解决的。 在客户端1与客户端2,3,4,5通信时使用了不同的内部端点。客户端1与 客户端2,3,4,5通信时使用相互不同的UDP socket。
基于ProudNet的PC MMORPG ‘Closers’
攻防战
在游戏服务器中使用的消息传递大部分可以使用TCP。TCP是一个非常成熟的协议,几乎在所有的状况下都运行稳定。 在丢包率1%以下或延迟100微妙以内时可以使用TCP。
但因特网环境不好时结果就会不一样。 发生TCP丢包时会发生很长的延时。如果在线游戏只使用TCP,那么在因特网条件不佳的环境中游戏人物的移动就会卡顿,这种现象叫做stuttering。
考虑到这些问题,我们的服务器引擎会同时使用TCP和UDP。 游戏开发人员在所有情形下都使用TCP。 但针对游戏人物的移动或机关枪扫射等环节使用UDP更有效。
UDP比 TCP更容易编程。UDP只要知道对方的端点就可以通过传输函数和接收函数简单地收发数据。 TCP的情形是,即便知道对方端点,也要处理连接、监听、接收的过程。 为此我编写了如下的代码。虽然没几行,但与epoll或 IOCP 等混合使用就会变得非常复杂。
但在库服务器中的情形正好相反。在多种网络环境中UDP反而比TCP更加难以操作。当TCP要发送超过网络性能负荷的数据时就会根据线路状态降低发送速度。而UPD在这种状况中会出现更严重的问题。
我在中国经历过这个问题, 而解决这个问题我花了半个月的时间。最终找到了原因并解决了问题。 那么看一下我是怎么做的。
在中国第一次接触到这个问题时TCP socket产生的错误代码为ECONNABORTED。我在相关文档查看了此错误代码表示的意思 。
英语文章表示 “data transmission time-out”。如果想了解这个错误代码,就要知道TCP是如何运行的。
TCP默认包含 ARQ算法。ARQ 算法是,在一端发送消息时对方会回复“已收到”,我们 把这个称之为Acknowledgement ,即 ACK。如果发送方未接到ACK,就会在一段间隔之后再一次发送数据,一直反复到经过20秒后系统断开TCP连接为止。这时TCP socket将返回ECONNABORTED错误。
这个问题可以通过如下方法再现。
设备 A,B位于同一个局域网。 C在远处。在此进行如下实验。
A向 C建立 TCP连接之后收发少量数据。
在此状态下没有出现 ,那么增加项目。
B和 C通过 UDP收发大量数据。
增加这个项目的几秒后A和C之间的TCP连接被断开。之后返回ECONNABORTED错误代码 。
这种现象也能发生在网吧。
例如,假设(如图)网吧中有很多设备。 左侧的两台设备通过UDP收发大量数据。假设左侧两台设备是用于文件传输的。这时就会发生有趣的现象,其他设备会经常与服务器断开连接。 即便是与文件传输无关的应用,如messenger或游戏也一样会断开连接。
为什么只有UDP才会出现这种问题呢?这需要通过拥塞控制算法说明。
组建因特网的网络设备其实是计算机。 每个网络设备都有各自可处理的最大数据上限。一旦接收到超出这个量的数据,网络设备就会丢掉多余的数据。 这就导致了数据包的丢失。
基于ProudNet的体育游戏“chagu chagu”
如果 UDP通信量庞大,那么网络设备就要尽全力处理UDP数据。 那么就会丢掉TCP数据的处理机会。 这时TCP就会重新发送数据,而这一过程持续20秒就会断开TCP连接。
单纯使用TCP时不会出现这种问题,因为TCP本身拥有拥塞控制算法,即TFRC(TCP friendly rate control)。
UDP本身没有拥塞控制算法。所以通过UDP编程时需要考虑拥塞控制,解决这个问题我花费了很多时间,但最终还是做到了
使用Proudnet的手机游戏七骑士