近来跟人讨论一些关于实时性要求比较高的服务器设计的问题,这个问题纯粹是理论的,概念的,架构的,整理一下以供参考。
众所周知,UDP是无连接的,面向消息的数据传输协议,对于传统的TCP服务器来讲,有2个致命的缺点,一是数据包容易丢失,二是数据包无序。很多人对于这2个弱点做了很多工作,大致的方法就是模拟TCP,其实这是无意义的,与其说模拟TCP还不如直接使用TCP。但是UDP的这2个缺点正好是可以利用的,实际上在某些状况下,正是UDP在网络游戏通信上的最大优势。
我先说简单的容易理解的,关于UDP的数据包的无序。其实自从多用户多任务的操作系统流行以来,消息便成了程序与程序之间,程序与人之间联系的一个纽带,实际上这本身就是无序的,服务器根本不知道玩家操纵的角色什么时候向左什么时候向右什么时候打怪什么时候喝药,这些都是以消息的形式触发的,只有当这些消息触发的时候,服务器才去处理,即使是TCP连续流,我们必须根据自己定义的格式将流截断解开成一个一个的消息包,然后再去处理这些消息。比如说,玩家去攻击一个怪物,我们把它放大为2个消息,[走到怪物跟前] [攻击怪物]。如果采用TCP协议,那么消息是连续的,先做第一个动作[走到怪物跟前],再做第二个动作[攻击怪物]。如果是UDP协议,那么这个消息可能发生倒序,可能变成了先[攻击怪物],然后[走到怪物跟前],这样看来就有点怪怪的。这个问题如何解决呢,其实在处理包的时候有一个逻辑层,这个问题本来是一个网络传输层的错误,但是我们可以将它转化为一个逻辑层的错误,我们可以认为凡是在怪物一定范围之外的近身攻击都是无效的(我想大多数网游服务器段都是这样处理的,没有哪个网游的战士能拿武器老远打怪的),无效的消息如何处理?很简单,丢掉,呵呵。
可能很多人不同意我最后的处理办法,没有关系,我说过了,这只是一部分特定的网游可以这么做,另外一方面,大部分网游服务器都会丢掉一些逻辑错误的包,除非服务器自己想挂掉,明知数据有错还去处理,这样的服务器程序我是不会要的。
另外一个,很多人都会想,玩家可能不干了,我明明打怪了,为什么打怪的动作没有?我想当一个网络状况不好的情况下,即使是TCP也会出现延迟的情况,玩家不会在意你这一下两下打了没反应,我不知道大家玩过网上的CS什么感觉,你确信你的每一次没打中对方的子弹都是因为你不准吗?我看未必,很多包都被同步掉了,你射出去的子弹只有在满足一定同步条件下才会被处理的。当然我刚才举的例子也很特殊,无序不会夸张到那种程度,另外一个就是我下面要讲的应用。
关于UDP的丢包处理。UDP丢包很多人的想法就是设置标签然后重发,那么这个是没有必要的,这种没有必要是在一定的特殊条件下:运动同步。如果你想要做运动同步,那么TCP将是非常臃肿的,因为每隔一段时间(一半都在半秒之内),玩家和服务器之间就需要交换一次空间参数(什么位置坐标,速度,方向,时间等等),大量的频繁的小数据包交换使用TCP将是可怕的(如果你不关掉延迟发送算法,那么很多数据都失去了意义)。这个时候UDP的特点就体现出来了,速度快,封包体积小,基于消息的。那么丢掉的包怎么办?很简单,丢就丢了呗,反正每隔半秒就会有新的修正数据发过来,你怕什么,如果你重发,发而不合时宜了,因为同步数据都是有时效性的,你重发的包往往失去了时效,没有意义了,所以不用重发,直接等待下一个新包吧
好了,就这么多了,欢迎大家指点