游戏服务器是⽤UDP还是TCP
之前⾯试,问起UDP和TCP区别时,⾯试官多问了在编写⽹络游戏的时候,到底使⽤UDP还是TCP,我当时说⼀般⽤UDP,特别对于我经常玩的和平精英这种对延迟敏感的⽽⾔,但如果对⽅丢⼀个⼿雷,丢包了,是没丢出去好还是延迟丢出去好?这让我陷⼊了沉思,最后没有回答的很好。今天看到⼀篇⽂章,写的很好。
原⽂链接:
正⽂:
电脑主板跳线⼀般来说你会听到⼈们这样说:“除⾮你正在写⼀个动作类游戏,否则你就⽤TCP吧” 或者是 “你能够在MMO游戏中⽤TCP,因为魔兽世界就⽤的TCP!”
遗憾的是,这些观点都没有反映这个问题的复杂性。
背景
⾸先,说明⼀下,我之前主要是⽤TCP进⾏⽹络编程。我曾为⼀个流⾏的在线纸牌游戏编写服务器了好⼏年,在⾼峰期我们的每台服务器能够承受4000到10000个连接(同⼀台物理机器上有多个服务器进程在跑)都没有问题。在我来看,TCP是⼀种安全⽽且常见的选择。
尽管如此,我们最新的项⽬却是使⽤UDP协议,⽽且我们的项⽬⽆法通过任何⽅式在TCP下⼯作。事实上,项⽬⼀开始使⽤的TCP,但是后来发现我们使⽤TCP⽆法达到我们需求的连接数量时,我们只能换成UDP了。
在使⽤中TCP表现怎么样呢
从原理上,TCP的优势有:
1. 简单直接的长连接
2. 可靠的信息传输
3. 数据包的⼤⼩没有限制
任何⼀个和TCP打过交道的⼈都知道,要实现⼀个稳定的TCP⽹络连接,需要处理各种隐藏的坑,⽐如断线检测、慢速客户端响应阻塞数据包,对开放连接的各种dos攻击,阻塞和⾮阻塞IO模型等等。火车票网上提前几天
悲剧电影散场打一成语
除了上⾯列出的这些问题外,⼀个好的TCP模块确实不好编码实现。
但是,TCP最糟糕的特性是它对阻塞的控制。⼀般来说,TCP假定丢包是由于⽹络带宽不够造成的,所以发⽣这种情况的时候,TCP就会减少发包速度。
在3G或WiFi下,⼀个数据包丢失了,你希望的是⽴马重发这个数据包,然⽽TCP的阻塞机制却完全是采⽤相反的⽅式来处理!
⽽且没有任何办法能够绕过这个机制,因为这是TCP协议构建的基础。这就是为什么在3G或者WiFi环境下,ping值能够上升到1000多毫秒的原因。
为什么不⽤UDP
UDP相对TCP来说既简单⼜困难。
举个例⼦来说,UDP是基于数据包构建,这意味着在某些⽅⾯需要你完全颠覆在TCP下的观念。UDP只使⽤⼀个socket进⾏通信,不像TCP需要为每⼀个客户端建⽴⼀个socket连接。这些都是UDP⾮常不错的地⽅。
但是,⼤多数情况下你需要的仅仅是⼀些连接的概念罢了,⼀些基本的包序功能,以及所谓的连接可靠性。可惜的是,这些功能UDP都没有办法简单的提供给你,⽽你使⽤TCP却都可以免费得到。
这也是⼈们为什么经常推荐TCP的原因。在⽤TCP的时候你可以不考虑这些问题,直到你需要同步连接的数量级达到500以上的时候。
所以,是的,UDP没有提供所有的解决⽅法,但是就像你看到的那样,这也正是UDP好⽤的地⽅。在某种意义上来说,TCP对UDP就好⽐是Hibernate和⼿写SQL的区别。
使⽤TCP失败的地⽅
⼈们经常给你建议,让你去使⽤TCP,⽐如“TCP跟UDP⼀样快”或者“游戏X⽤TCP如此成功,所以TCP当然是⾸选”,然⽽,他们完全没有理解为什么在那个特定的游戏中TCP是有效的,为什么UDP不按照顺序发送数据包呢?
那么为什么魔兽世界采⽤TCP呢?⾸先我们需要解释这个问题。这个问题其实是“为什么魔兽世界有的时候1000毫秒以上的延迟还能够运⾏?”这是TCP的性质决定的,在发⽣丢包的时候,会产⽣巨⼤的延迟,因为TCP⾸先会去检测哪些包发⽣了丢失,然后重发所有丢失的包,直到他们都被接收到。
可靠的UDP也是有延迟的,但是由于它是在UDP的基础之上建⽴的通信协议,所以可以通过多种⽅式来减少延迟,不像TCP,所有的东西都要依赖于TCP协议本⾝⽽⽆法被更改。
就这⼀点来讲,⼀些⼈要开始提到Nagle算法了,实际上它是你在实现任意⼀个对延迟敏感的TCP模型时⾸先需要禁⽌使⽤的。
汪峰个人资料及简历
那么魔兽世界以及其他的⼀些游戏是怎么处理延迟问题的呢?
⽅法也很简单,他们能够隐藏掉延迟带来的影响。
在魔兽世界中,玩家和玩家是⽆法碰撞的:因为这类碰撞是⽆法通过⼀些预测来处理的,但是玩家和环境之间的碰撞却是可以通过预测来处理的,所以这⾥使⽤TCP是没有问题的。
我们来看⼀下魔兽世界的战⽃就会发现,玩家的攻击指令发送给服务器的操作是放在⽐如“attack_entity(entity_id)”或者”cast_spell(entity_id, spell_id)“的接⼝中来做的,换句话说,瞄准操作是独⽴于进⾏的。如此⼀来,⼀些类似发起攻击动作和释放技能特效就能够在没有收到服务器确认的情况下就直接执⾏,⽐如展现冰冻技能的效果就可以在服务器没有返回数据前在客户端就做出来。牛顿力学三定律
客户端直接开始进⾏计算⽽不等待服务端确认是⼀种典型的隐藏延迟的技术。
⼏年前,我为⼀个叫“Five Card Jazz”的纸牌游戏编写过客户端。它使⽤的是http协议,它⽐直接的TCP协议连接的延迟更加严重。
我们⽤简单的纸牌绘制和抽牌的动画来掩盖延迟的问题,所以延迟的问题只在⾮常糟糕的连接下才会被看出来。这种⽅法也⾮常的典型:发送请求的同时开始播放牌桌的动画,⼀直播放翻动最后⼀张牌直到接收到了服务端传回来的数据为⽌。魔兽世界的战⽃特效就是使⽤类似的原理。
这也意味着,我们到底是使⽤TCP还是UDP取决于我们能否隐藏延迟。
TCP在什么时候失效
⼀个采⽤TCP的游戏必须能够处理好突发的延迟问题(纸牌客户端就很典型,对突发性的⼀秒的延迟,玩家也不会产⽣什么抱怨)或者是拥有缓解延迟问题的好⽅法。
但是如果你运⾏的是⼀个⽆法使⽤任何减缓延迟措施的游戏呢?玩家对玩家的动作类游戏通常就属于这个范畴,但是这也不仅仅限于动作类游戏。
举个例⼦:
我⽬前正在写⼀个多⼈游戏(War Arcana)。
⼀种常见的操作是,你快速的移动你的⾓⾊通过⼀张充满战争迷雾的世界地图,但是⼀旦你探索过,迷雾就会被打开。
成都刺绣
为了确保游戏的规则,防⽌玩家作弊,服务器只能显⽰玩家当前位置附近的信息。这意味着不像魔兽世界,玩家⽆法在没有得到服务器响应的情况下,做出完整的动作。和Five Card Jazz相⽐,我们即使允许500毫秒的延迟,也已经⾮常困难了。
在实现了游戏的原型后,在局域⽹内⼀切都进⾏的⾮常顺利,但当我们在WiFi环境下测试时,操作会
间歇性的卡起来或者延迟⾼起来。写了⼀些测试程序之后发现,WiFi环境下偶尔会发⽣丢包⾏为,每当发⽣丢包的时候,服务器的响应速度就从100-150毫秒上升到1000-2000毫秒。
没有任何办法可以绕过TCP的这个设置来避开这个问题。
我们替换了TCP的代码,⽤了⾃定义的可靠的UDP来实现,把⼤量的丢包产⽣的延迟降到了仅仅只有50毫秒,甚⾄⽐以前TCP不丢包的情况⼀个来回的延迟还要⼩。当然,这只可能建⽴在UDP之上,这样我们才对可靠性拥有完全的掌控⼒。
困惑:可靠的UDP只是TCP的⼀种简单的实现?
你有没有听过这种说法:“可靠的UDP就像TCP⼀样,所以还是⽤TCP吧”。
问题是这种说法是错误的。可靠的UDP⼀点也不像TCP,要去实现⼀个特殊的阻塞控制。事实上,这也是你使⽤可靠UDP代替TCP的最⼤的原因,避免TCP的阻塞控制。
另⼀个重点是可靠的UDP的可靠性是如何保证的。这⾥有很多种⽅法去实现。我⾮常喜欢Quake3⽹络库代码⾥的⼀些想法,它们也激发了我在War Arcana中使⽤UDP协议。
你也可以使⽤许多⽀持可靠通信的UDP库,当然,这样在可靠性⽅⾯,相⽐⾃⼰⼿动实现全部的代码⽽⾔,可能会更加通⽤⽽失去了⼀些性能优势。
底线
那么到底是⽤UDP还是TCP呢?
如果是由客户端间歇性的发起⽆状态的查询,并且偶尔发⽣延迟是可以容忍,那么使⽤HTTP/HTTPS吧。
如果客户端和服务器都可以独⽴发包,但是偶尔发⽣延迟可以容忍(⽐如:在线的纸牌游戏,许多MMO类的游戏),那么使⽤TCP长连接吧。
如果客户端和服务器都可以独⽴发包,⽽且⽆法忍受延迟(⽐如:⼤多数的多⼈动作类游戏,⼀些MMO类游戏),那么使⽤UDP吧。
这些也应该考虑在内:你的MMO客户端也许⾸先使⽤HTTP去获取上⼀次的更新内容,然后使⽤UDP跟游戏服务器进⾏连接。
永远不要害怕去使⽤最佳的⼯具来解决问题。