这周,我们的cacheserver服务面临了很多的挑战。项目资源超过了30G,有几十个用户在同时使用。每天都有版本切换工作(导致重新上传下载30G的数据)。在这个过程中,我对cacheserver程序修修补补,终于没有太大的问题了。
总结一下,我认为cacheserver的协议设计,以及Unity客户端实现,均存在很大的问题。这些问题是无法通过改进服务器的实现彻底解决的,只能做一些缓解工作。真正的完善必须等Unity的客户端意识到这些问题并作出改进。
cacheserver的协议设计非常简陋。就是顺序的提交请求,然后每个请求会有序的得到一个回应。这些请求要么是获取GET文件,要么是上传PUT文件。其中PUT文件在协议上不必回应。
PUT实际上是个小问题,真正的问题是:这种依赖严格次序的协议,在面对两边数据量不对等、网络速度不对等的近况时,很难有一个健壮的实现。
如果不介意我可能有理解偏差,那么可以看看下面我对其原理做的一点中文复述:
今天有同事吐槽钉钉的windows客户端做第三方服务权限认证的流程,人机交互方面远没有qq好用。
我说,通过一个普通权限的本地程序做统一认证,其实是很容易出安全漏洞的,小心点比较好。一般来说,这个在操作系统层面支持会比较安全,就像windows的UAC。这种通常是第三方应用向服务器发一个认证请求,然后服务器下转发到本地客户端,然后客户端弹出一个确认窗口,经过用户确认以后,再经由第三方服务器下发给那个第三方客户端。
这里有个安全隐患就是,如果这个弹出窗口不是操作系统级别支持的话,在windows下很容易被普通权限的同级程序拦截。当然也不是完全没有办法。比如预留一个用户认可的信息展示,好像信用卡那样的安全识别码;我没用过qq,听说qq是用用户自己的头像做防伪确认的。
不过,这套流程做起来比较麻烦,开放个第三方使用的话,需要第三方客户端/服务器都遵循一定的协议来做。而且第一次需要做一次账号绑定,需要用户在第三方应用里输入一次自己的qq号,或在qq中输入一次第三方账号。windows下可以先用FindWindow找到qq客户端的窗口,然后用一个自定义消息把一个token或第三方的账号信息发过去,完成握手。
说到这里,同事说,qq的那套似乎没那么复杂,好像是走的本地端口。我先想说不至于吧,但是似乎每次遇到安全问题,我都会高估腾讯的产品设计人员的安全意识下限。腾讯系产品的用户权限大量被盗用似乎在黑产链上不足为奇。
我认为,大多数情况下,通讯的稳定性是大于带宽的需求的。那么,采用本文这种方法应该能去掉上面这些流量特征。
本文分三个部分:一,什么时候有可能采用UDP通讯而不是用TCP更好;二,一个可靠的UDP通讯模块的API接口该如何设计;三,一个简单的实现。
首先,我一直是非常反对在UDP协议上实现一个可靠传输协议的,即类似TCPoverUDP的东西。
TCP已经够复杂了,几乎不太可能重新设计的更好。如果用UDP再实现一个可靠传输协议,而表现的比TCP效果更好,那么多半只是在部分情况下的优势;或是霸道的占用了过量的资源,而TCP在设计时则是很友好的,以整个网络的通畅为更高准则的。
对于后者,我心里相当排斥。如果大家都想独占网络带宽,那么只会让每个人都无法获得高质量通讯。
在网络游戏,尤其是移动网络上的网络游戏制作圈里,不断的有人期望基于UDP协议通讯来获得更快的响应速度,而又想让通讯流像TCP一般可靠。我也时常思考这个问题,到底该怎么做这件事?
如果基于UDP可以做的比TCP更好,那么一定是放弃了点TCP需要做到的东西。
一条路是寄希望于业务逻辑上允许信息丢失:比如,在同步状态中,如果状态是有实效性的,那么过期的状态信息就是可丢失的。这需要每次或周期性的全量状态信息同步,每个新的全量状态信息都可以取代旧的信息。或者在同步玩家在场景中的位置时可以用这样的策略。不过在实际操作中,我发现一旦允许中间状态丢失,业务层将会特别难写。真正可以全量同步状态的场合也非常少。
那么,不允许信息丢失,但允许包乱序会不会改善一旦所有的包都一定能送达,即丢失的包会用某种机制重传,那么事实上你同样也可以保证次序。只需要和TCP一样在每个包中加个序号即可。唯一有优势的地方是,即使中间有包晚到了,业务层有可能先拿到后面的包处理。
什么情况下是包次序无关的呢?最常见的场合就是一问一答的请求回应。采用这种方式的,UDP在互联网上最为广泛的应用,就是DNS查询了。
我的思考结论就是:在UDP协议之上,实现一个带超时的请求回应机制,让业务层负责超时重发,有可能取得比TCP通讯更好的效果。但其前提是:单个请求或回应的包不应该过大,最好不要超过一个MTU,在互联网上大约是500多字节。
我这里就不提那些中招的所谓大厂了,如果经常听我扯淡的同学早就知道我的观点:即便是大厂,有安全常识的人还是少之又少、反而是所谓大厂因为管理更困难(好多大厂到管事的那级的人更跟不上知识更新),犯安全错误的机会就更大。
所以也曾经考虑过使用基于TCP连接的VPN,比如openVPN。在SA尝试做配置的时候,我突然有了个想法。既然PeerVPN是开源的,何不修改一下实现,让它支持多个ip间建立多条路径的通道呢。
从去年开始,我们的工作项目逐步迁往github的私有仓库里。github太好用了,自己搭建的git平台完全比不上。可是购买企业版本自己架服务器成本又太高(平均一个人一年要250刀),而我们项目并不多(只是人多)购买私有仓库绝对够用了,唯一的缺点就是github没有在国内开展业务,服务器都在境外,速度很慢。
之前我们无论是电信还是联通线路,从github上clone项目的速度一直没有超过200KB/s,而我们租用的国际线路本身带宽又很低(因为单价太高,所以只做部分翻墙用)。而且连接还不太稳定,有一定的丢包率。
考虑到我们办公室租有多条不同运营商的宽带,所以最近在考虑怎么把这些资源整合在一起使用。
这里的这个库定义的协议中,包长度是用big-endian的2个字节表示的,也就是说一个包的长度不得超过64K。这让很多人很为难。已经几次有同学建议,把长度放宽成4个字节,因为他们的应用环境中大部分包不会超过64K,但总有少量超过这个限制的。
如果业务逻辑基于短连接来实现,那么也就不必这么麻烦。但是缺点也是很明显的:
每对请求回应都是独立的,所以请求的次序是不保证的。
服务器向客户端推送变得很麻烦,往往需要客户端定期提起请求。
安全更难保证,往往需要用一个session串来鉴别身份,如果信道不加密,很容易被窃取。
即使有这些缺点,这种模式也被广泛使用。我打算在下个版本的skynet中提供一些支持。
所谓支持、想解决的核心问题其实是上述的第三点:身份验证问题;同时希望把复杂的登陆认证,以及在线状态管理模块可以更干净的实现出来。
我不打算基于HTTP协议来做,因为有专有客户端时,不必再使用浏览器协议。出于性能考虑,建立了一个TCP连接后,也可以在上面发送多个请求。仅在连接状态不健康时,才建议新建一个TCP连接。
目前如果要与腾讯合作运营游戏的话,是必须把服务器运行在腾讯的云平台上的。鉴于腾讯的强势地位,严禁自己托管机器,也禁止选择别家的云服务。至于腾讯开放平台,谁用谁知道,呵呵。既然都叫”开放“了,你就能想像是什么意思,所谓共和国还要在名字前加个“人民”呢:)
如果你把“必须运营在腾讯云平台”这条当成企鹅税来看待,也就了然了。无非是多花点钱用差点的硬件,增加点程序员人工为它写些代码适配,出点啥莫名其妙的问题,忍忍,多吐点槽就好了。要理解大公司部门多,各个部门都需要完成自己的KPI,刷点存在感。
当然也有可能你的程序无法获得需要的处理能力,或是你不堪忍受在云平台上的各种奇葩。那么还有一条路可以偷偷走。那就是想办法自己另外托管机器,然后写一个tunnel程序把服务器连起来。
这个东西完成了这样的功能:
我一直不想动手去做一个临时方案解决TCP断线重连问题,因为实现一个TCPoverTCP是没有太大意义的。移动网络发展迅速的今天,整个行业都在努力提高移动网络的稳定性,所以费力做这个事情很可能在两年之后就变得完全没有必要。
这两天,我们在自己的服务器上安装了支持MPTCP的新内核做了测试。发现:如有可能,设备会为新的IP地址建立新的通讯路径。如果连接两端各有两个IP,那么在初始的TCP连接建立后,通过协商,最终会建立4条TCP连接出来,交叉连接了所有的IP。任何一条通路有效都不影响通讯。btw,如果你的机房有网通,电信两个IP的话,如果客户端设备支持MPTCP,那么会自动同时使用两个通路同时维持一个逻辑上的连接。这对国内的网络环境非常有利,不需要使用bgp机房,也不需要在多线机房配置复杂的DNS了。
当你的手机从3G网络切换到新的wifi热点时,设备会自动利用新的wifi网络做数据传输;离开wifi热点后,又能无缝切换回3G;再次进入新的wifi热点范围,还可以重新利用新的wifi网络。这样,移动设备可以穿梭于多个网络之间而永不断开连接。
ps.经过这两天的测试,还发现MPTCP似乎只能利用第一次连接的通路做控制信息交换。当第一次连接的IP实效后,不能把后来的通路提升为主控连接。所以MPTCP看起来不能在只有一个网络设备上正常工作。(我原先预期它可以在同一个设备上切换IP还可以正常建立新的子流,看来是搞错了)
各种奇怪的现象似乎在说,我们被攻击了。
我们的防火墙规则还是非常严格的,系统安全方面也很注意。我觉得即使被人下了木马,也不大可能被大面积入侵。何况有几台机器从外网不可能进入,如果需要感染这些机器,必须先突破允许外网连接的机器才行。
因为这个项目,我们提前建立了平台开发团队。但许多东西开始的都很仓促,比如需要对接用户登陆认证系统。
这样,合作方的客户端可以较容易实现相应模块。
怪物公司同学周末调试客户端时,修改了自己机器的网关,增加了模拟延迟。奇怪的是,他的客户端在切换网关时并没有断开连接。可延迟也果真发生了。
我和他探讨了一下,觉得这个模拟延迟是单向的。当游戏服务器发送数据包回桌面时,由于服务器和他的桌面机在同一个网段,所以IP包被直接发回了。TCP连接也不因为修改了过去的通路和断开。
当然,这种模拟并不是我们想要的。正确的方法应该是在修改网关(指向延迟模拟机器)的同时,也修改桌面机的IP,或是给自己机器绑定两个IP,使用模拟环境网段的IP来重新建立TCP连接。或者在模拟网关上做一次NAT。反正方法有很多,不展开讨论了。只有正确的模拟双向延迟(或网络颠簸)才好得到接近现实情况的场景。
不过这次错误,引出我另一个思考。如果TCP上行和下行延迟差距较大,有没有什么特别糟糕的事情发生呢?我的第一反应是,网络对时不准了。
清明节放假,在家闲着无聊,就实现了一个试试。虽然写起来还是挺繁杂的,好在复杂度还在我的可控范围内,基本上也算是完成了。
设想这样一个需求:程序bind并listen一个端口,然后需要处理连接到这个端口上的所有TCP连接。当每个连接上要数据过来时,收取这些数据,识别出封包,发送给对应的逻辑层处理。如果数据不完整,则暂时挂起这些数据,直到数据收取完整再行处理。
这是一篇命题作文,源于今天在微薄上的一系列讨(好吧,也可以说是吵架)。其实方案没有太多好坏,就看你信不信这样做能好一些或坏一些。那么,整理成blog写出,也就是供大家开拓思路了。
问题是:如何管理这些缓冲区比较简洁明了,且性能高效。
其实这个有许多解决方案,比如为每个网络连接开一个单独的固定长度的buffer。或是用memorypool等改善内存使用率以及动态内存分配释放,等等。今天在微薄上吵架也正是在于这些方案细节上,到底好与不好,性能到底如何。既然单开一篇blog了,我不像再谈任何有争议的细节,仅仅说说,用RingBuffer如何解决这个问题。
过年了,人都走光了,结果一个人活也干不了。所以我便想找点东西玩玩。
今天想试一下libev写点代码。原本在我那台ubuntu机器上一点问题都没有,可在windows机上用mingw编译出来的库一个backend都没有,基本不可用。然后网上就有同学推荐我试一下libuv。
libuv是node.js作者做的一个封装库,在unix环境整合的libev,而在windows下用IOCP另实现了一套。看起来挺满足我的玩儿的需求的。所以就试了一下。
其实铁路订票系统面临的技术难点无非就是春运期间可能发生的海量并发业务请求。这个加上一个排队系统就可以轻易解决的。
本来我在weibo上闲扯两句,这么简单的方案,本以为大家一看就明白的。没想到还是许多人有疑问。好吧,写篇blog来解释一下。
简单说,我们设置几个网关服务器,用动态DNS的方式,把并发的订票请求分摊开。类比现实的话,就是把人分流到不同的购票大厅去。每个购票大厅都可以买到所有车次的票。OK,这一步的负载均衡怎么做我就不详细说了。
每个网关其实最重要的作用就是让订票的用户排队。其实整个系统也只用做排队,关于实际订票怎么操作,就算每个网关后坐一排售票员,在屏幕上看到有人来买票,输入到内部订票系统中出票,然后再把票号敲回去,这个系统都能无压力的正常工作。否则,以前春运是怎么把票卖出去的?
我们来说说排队系统是怎么做的:
写完了之后,我很好奇性能怎样,就写了一个非常简单的测试程序测了一下。当然这个测试不说明很多问题,因为测试用的数据实在是太简单了,等明天有空再弄个复杂点的来跑一下吧。我很奇怪,为什么google官方的C++版性能这么差。
我的lua测试代码大约是这样的:
localprotobuf=require"protobuf"addr=io.open("../../build/addressbook.pb","rb")buffer=addr:read"*a"addr:close()protobuf.register(buffer)fori=1,1000000dolocalperson={name="Alice",id=123,}localbuffer=protobuf.encode("tutorial.Person",person)localt=protobuf.decode("tutorial.Person",buffer)end100万次的编码和解码在我目前的机器上,耗时3.8s。
这种设计很难让人做动态语言的binding,而大多数动态语言往往又没有强类型检查,采用生成代码的方式并没有特别的好处,反而有很大的性能损失(和通常做一个bingding库的方式比较)。比如官方的Python库,完全可以在运行时,根据协议,把那些函数生成出来,而不必用离线的工具生成代码。
这次,我重新做项目,又碰到protobuf协议解析问题,想从头好好解决一下。上个月一开始,我想用luajit好好编写一个纯lua版。猜想,利用luajit和ffi可以达到不错的性能。但是做完以后,发现和C++版本依然有差距(大约只能达到C++版本的25%~33%左右的速度),比我去年写的C+Luabinding的方式要差。但是,去年写的那一份C代码和Lua代码结合太多。所以我萌生了重新写一份C实现的想法。
有那么几天,我们在惠灵顿的海边山顶租了个屋子,一切都很舒服,但是不能上网。甚至于附近连wifi信号都收不到,想"借用"一下别人的wifi热点都不成。我顶着海边的狂风在院子里竖起天线,捕捉着周围微弱的信号,最终未果。然后转战屋里的有线电视。我发现和国内的有线电视一样,机顶盒是接有网线的。也就是说,物理上,存在一条链路接入了互联网。但是我插上电脑后,发现ip包根本发不出去。不过,好似有个DNS服务是可以用的。
当时也没多想,只是觉得有办法可以利用一下。不过隔天就搬走了,没有深入下去。今天回味一下,感觉的确可以利用DNS服务和外部建立连接。当然,一开始就需要在外界把接应的程序程序搭建好。
最近Lua社区非常活跃。6月22日发布了Lua5.2.0(beta-rc2)。今天(6月24日)发布了LuaJIT-2.0.0-beta8。
虽然luajit和lua5.2还有点小矛盾,luajit没有完全支持lua5.2的迹象。不过,这些对Lua社区都是好消息啦。可能对于lua用户会有点小纠结,到底是追随官方的5.2版呢,还是去用性能更好的luajit2。我比较在意性能,暂时先投靠luajit了。反正和5.2区别也不大。更重要的是,luajit2提供的ffi库相当之好用,极大的减少了我们写C库的luabinding的负担。从某种角度可以看到另一个问题,为基础设施模块设计出良好的C接口(而不是C++的)是多么的重要。
信息加密技术已经很成熟了。不过想把加密信息伪装隐藏在看似明文的信息中的工具我还没有见到。
我的意思是,监听方完全察觉不到有密文在传输的情况下,把加密信息传输给对方。我记得有工具可以在图片中隐藏一些信息,即使图片经过扫描,隐藏在其中的密文信息依然可以读出。
什么是货币呢?货币就是商品(包括服务)交换的媒介。现在我们通行的货币是由有信誉的银行发行的,基本上是由其信誉来担保的。只要用的人都认可,那么我们就可以用它来交易。货币有一定的保值特性,我把我的劳动/服务/所有的商品换成货币后,银行担保我在日后的某一天,我还可以用它交换会差不多等值的东西。这个保证的前提是,银行不会滥发新的货币以及大家都信任这一点。
但是这个方案有很大的缺陷。大多数人机器都在防火墙背后,也没有权限可以在网关上设置NAT。很大可能是无法使用的。另外,架设server隐藏有一定安全上的风险。
而我的想法是这样的:
在需要并行化处理数据的时候,采用消息队列通讯的方式来协作,比采用共享状态的方式要好的多。Erlang,Go都使用这一手段来让并行任务之间协同工作。
ZeroMQ并不是一个对socket的封装,不能用它去实现已有的网络协议。它有自己的模式,不同于更底层的点对点通讯模式。它有比tcp协议更高一级的协议。(当然ZeroMQ不一定基于TCP协议,它也可以用于进程间和进程内通讯。)它改变了通讯都基于一对一的连接这个假设。
ZeroMQ把通讯的需求看成四类。其中一类是一对一结对通讯,用来支持传统的TCPsocket模型,但并不推荐使用。常用的通讯模式只有三类。
把网络游戏服务器分拆成多个进程,分开部署。这种设计的好处是模块自然分离,可以单独设计。分担负荷,可以提高整个系统的承载能力。
缺点在于,网络环境并不那么可靠。跨进程通讯有一定的不可预知性。服务器间通讯往往难以架设调试环境,并很容易把事情搅成一团糨糊。而且正确高效的管理多连接,对程序员来说也是一项挑战。
正如TCP协议解决了互联网上稳定可靠的点对点数据流通讯一样。游戏世界实际需要的是一个稳定可靠的在游戏系统内的点对点通讯需要。
我们可以在一条TCP连接之上做到这一点。一旦实现,可以给游戏服务的开发带来极大的方便。
AllowTcpForwardingno
嗯,估计是觉得好多人用它来做proxy,流量受不了,关掉了。
如果还想继续用怎么办呢?
我初看时,不以为然。不就是做个假证书做中间人攻击么?关键还在于用的人自己要有足够的安全意识。
周末。
玩了一下ActionScript。因为感觉做一些简单的需要长连接的互联网应用,flash是一个不错的选择。在大多数情况下,比要求用户安装一个客户端要人性。(当然,和要求用户为浏览器安装一个莫名其妙的ActiveX控件相比,让用户自己决定是否下载独立客户端要友好的多)
因为,虽然Flash大多数情况下作为一个浏览器插件(在Windows下是一个ActiveX控件)的形式存在,但其安全性比之许多绿霸之流的流氓软件还是值得信任的。
偶尔,我会在公众场合上网。但是不敢以自己的身份登陆任何网站。这年头,自己家的机器都不安全了(使用非Windows平台的除外),哪敢信任不知底细的机器啊。
话说,我是没买过没用过笔记本的,对这个东西比较抵触。之前已经极大的挖掘了Palm手机的功能,比如可以用ssh登陆远程自己的机器。可以用浏览器访问一些简单的控制界面等等。只要在自己放在公网上的服务器配置好了,问题都不大。记得有一天,我想看一个网站上的图片,但是手机浏览器对那个网站的页面罢工。我是先ssh登陆到自己服务器,用wget下载下页面,再用grep分析html,然后wget下图片。并用ImageMagick缩小,压缩转换格式。最后放到自己的webserver上,让手机浏览器可以顺利观看的。(顺便节约了许多GRPS流量)
这套方案用起来过于繁琐,也有很大的局限性。
昨天睡觉的时候胡思乱想。如果能弄一套通用的东西,或许就可以让我放心的去用别人的机器了。比如出差的时候突然需要上网,光靠手机又不够的时候,可以冲到网吧里暂时用一下。
最近在帮另一办公室的同事调试程序,有些东西远程弄起来比较麻烦,征得同意后,我希望直接连入对方的局域网来弄。但是申请VPN权限以及修改对方路由的程序比较繁琐,所以我想找个简单的方法。
首先我在我们办公室的网关上做了个NAT,让对方可以ssh到我的机器。
阅读ssh的手册我发现openssh支持一个-w参数,用来连接两端的tun设备。不过试了半天没有搞定:(所以又想了其它办法。
今天调公司里的VPN时,发现我的freebsd机器traceroute老是失败。
控制台报告
traceroute:sendto:Permissiondenied
(以上错误信息用来引导google同类问题的同学)
一开始是ping都不行,我查看了firewall的设置,允许icmp包通过。ping就可以用了,但是traceroute还是不行。
这让我很疑惑,后来用tcpdump查了一下,发现freebsd的traceroute默认是用udp协议做的。真是惯性思维害人啊。windows和linux下都是使用icmp的。
man了一下,发现freebsd的traceroute可以用-PICMP选择icmp协议。然后一切正常,不需要修改firewall的设置了。
今天想在自己管理的一台机器(安装的freebsd)上设置下防火墙,使用ipfw的时候,发现ipfw模块没有加载。
一时冲动就直接kldloadipfw,立刻就被防火墙踢了出来:(。无奈只好联系机房的同事帮忙按一下电源。
好吧,这次我知道freebsdipfw默认的配置是有一条65535denyipfromanytoany的规则了。一加载ipfw模块根本不给我机会通过ssh远程添加新的规则。
以往都是在自己办公室的机器上折腾,没什么好担心的,第一次操作千里之外的机器的firewall,一不小心就傻眼了。
重起之后谨慎多了。还是改/etc/rc.conf,然后用/etc/rc.d/ipfwstart的脚本启动好了。
保险起见,我添加了firewall_enable="yes"和firewall_type="open",并在机器的桌面机上测试了一下,感觉没有问题。就登陆上远程机器上操作。
可是当我输入/etc/rc.d/ipfwstart后,又被踢了,真是欲哭无泪啊。终于有明白的同事告诉我,因为ipfw添加规则时会有标准输出。但是加载完ipfw模块后,新的规则没来的及加上前,我的连接就被断开了。后续的输出失败会导致系统发信号让程序退出,后续规则就没有加上了。
再次麻烦机房同事重启系统,一切正常。
这次算长了点经验。远程开启ipfw,一定要重定向标准输出,更安全点是把标准错误输出也一并重定向。
/etc/rc.d/ipfwstart>/dev/null2>&1
去年,几个游戏组的同事给我描述了他们发现的针对游戏的中间人攻击(MITMattack)。有人做了一个代理服务,种植木马到用户机器上,使用户机器对游戏服务器的tcp连接都重定向到代理服务上。(这个步骤其实不一定在用户机器上做,网关和路由器上都理论上存在被做手脚的可能,这是用户自身无法保证的。
代理服务可以一直监听用户到服务器的通讯,直到认为认证过程结束,不再转发用户的数据,改由自己操作用户的帐号。这样就可以达到转移用户虚拟财产的目的。
今天,和几个同事讨论了这个问题和解决方案。
btw,在服务器上装php时,因为开始ports没有更新,出了好多问题。mysql一开始忘记装gbk的支持,困扰我老半天。鄙视一下公司购买的某著名php写的论坛系统,居然默认不是用utf-8编码的。
这篇blog的内容原本是去年10月写的,当时正在看《24小时反恐》,脑子里涌现出无数古怪的想法,觉得这个世界到处都是特工,什么通讯手段都不可靠。在我们还没有能力获得量子加密需要的硬件前,能有限依靠的恐怕只有数学能保证的加密技术。
当时那篇blog写了一大篇,自己都觉得太过于天马行空的乱扯,就没有公开。
不过今天开周会,我们又提到游戏client提交密码的安全性问题,指派了一个同事最近在这方面做些工作,这里也写点以前研究的东西,留点记录吧。
简单说,我们应该避免在一次登陆过程中从互联网连接中传递明文的用户名密码信息。这是一个起码的要求,但是我们以往的产品做的并不好。很多时候都是伪加密。就是client用个私有算法将密码信息编码后传送,再由server用相同的算法还原。
这个安全性极度依赖client的程序不被逆向工程。一旦有人完全逆向工程后,只需要他监听到通讯,就可以还原出用户的密码。
SSL就是干这个的,但是由于种种原因,暂时我们还不能在游戏client/server中推行使用。那么,现在是不是可以抽出一些东西来,自己先在程序中实现出来用着。其实,优先要做到的,无非是安全的让用户提交用户名密码而已。
我曾多次在Blog上讨论过增强游戏客户端的登陆安全问题。这个问题仅靠软件手段之所以难以从根本上解决,是因为现在国内的网络安全环境实在是太差,我们无法确保用户的机器上没有木马。
我们几乎做不到让client程序自检,确保自己没被动过手脚。
周末又一次想到这个问题时,突然想到,其实最不容易被伪造的client软件其实就是最容易被动手脚的浏览器。虽然像IE这样存在很多安全问题的浏览器,会被无缘无故的插上诸多恶意插件。但不可否认,木马想骗过浏览器截取到其中的特定传输内容还是颇不容易的:大多数恶意插件都会很快被检查出来。毕竟这个是现在反木马病毒软件全力去做的事情。而且想伪造一个浏览器上的登陆过程比伪造一个游戏Client的登陆过程技术上要困难的多。
网易的所有产品都使用网易通行证系统做用户身份认证,包括游戏产品。
这是降低新产品用户门槛的好方法。几乎所有的网络服务提供商都弄了个自己的统一用户认证系统,网易通行证也是干的这挡子事,内部我们把这套系统简称为URS。公司在URS系统上投入了很多人力和资源,但是其表现总是跟不上我们的需求。我个人从03年开始就不断的在提一些安全方面的改进建议。但是由于这些系统涉及面太广,想做出些实质性的改变举步维艰。
不断的写建议书,不断的参与北京URS部门的技术会议,让我充分理解了他们的困难,和其中许多非技术难点的难处。
网络游戏世界的构建有越来越大的趋势,游戏设计者希望更多的人可以发生互动。技术人员的工作就是满足这些越来越BT的需求。
我们目前这个项目由于是自己主导设计,而我本人又是技术方案的设计者。所以,技术解决不了的问题就不能乱发牢骚了。作为游戏设计者,我希望整个游戏世界的参于者可以在一个唯一的大世界中生存,他们永远有发生互动的可能。注意这里只是保留这种可能性,实际上,即使是现实社会,每个人的社交圈子都不大。即使是千军万马的战场上,无论是将军还是士兵,都不需要直接跟太多人互动。
我们的游戏的技术解决方案仍旧是将游戏大世界分成若干独立服务器组,人为的将人群切分成更小的独立单位。这里,技术上需要解决的是:服务器组间可以灵活的交换数据。
游戏服务器在设计时,会有许多组播的需求。比如一个NPC需要向周围的观察者播出它的状态信息。无出不在的各种聊天频道中需要向频道中的参于者广播聊天消息等。
通常,我们会为每个组播的请求维护一张列表,然后再把需要的信息包发送给指定列表上的多个连接。这个过程在很多地方都会遇到,一个设计的不太好的引擎,再这里有可能滋生诸多bug。尤其在多服务器的设计时尤其如此。
这两天,我试图寻找一种简洁统一的解决方案。
最近在考虑为一组游戏服务器配置多个连接入口。这个需求来至于我们的国情。作为大的游戏运营商,势必要考虑国内的网络状况——南北不通的现状。以往别的公司的代理游戏,由于不是自己开发,都选择了一个实际的方案:在北网通和南电信各放若干组服务器。北边来的在北边玩,南方住的安居在南方。
我们的游戏却不行,因为我需要一个完整的大世界,必须解决南北互通的问题。据我所知国内运营的游戏EVE是比较好解决了这个问题的。
我们自己的游戏大多也解决了,只是宣传上还是鼓励玩家登陆相应的服务器。我们的解决方案本质上很简单。建立有多个出口的机房,同时拥有电信和网通的线路。或是用自己的线路互联电信和网通的机器。这后者普通用户自己在家也可以做,只要你肯花钱,同时购买电信的ADSL于网通的宽带即可。目前许多城市两者都向大众提供服务。
当然,最终我们还是需要编写服务器的程序员做一些配合。
当游戏服务器群达到一定规模后,让用户只从一个入口连入会给这个入口带来很大的压力。这样,我们就需要让服务器群中的多台机器都允许用户直接连接。
当服务器开放给用户直接登陆后,必须面临的一个问题就是用户身份认证的问题。
大多数提供网络服务的公司都做了一套统一的用户认证系统,比如微软的passport,网易的通行证,等等。为了避免重复验证用户身份而给用户认证系统带来过大的负担,云风在这里给出一个参考解决方案。
我们在做mmo的服务器的时候,有些信息,数据量很大,却并非经常变动。比如物品的细节描述,工会/帮派信息,甚至还有好友列表等等。在IM软件中,cache住朋友列表到本地硬盘是很常用的手法,不过我参与的几个MMO项目都没有这样做。
其实,这些不易变的信息,在client需要获取的时候,只需要在请求协议中加入一个自己cache的信息包的校验值过去就可以了。server校对自己一方的校验值,当判断与client相同的时候,就不需要重发这些信息了。
很多信息都可以如法炮制,扩展开看,还可以是npc对话,任务描述等等。
这两天研究了一下lzw压缩算法,据说它专利已经过期了,那么应该可以随便用了吧。
这种基于字典的压缩算法,一个很大的优势就是,如果数据流中经常出现同一个词,那么会被极大的压缩掉。重复性的信息在网络游戏中经常碰到,不过是基于包和包之间的,而不是同一个包之内的。游戏又跟别的应用不太一样,我们往往希望更快的把数据交到对方,减少网络的延迟,所以大多数情况下,我们不会积攒很多数据一起发送。所以,不能简单的以每个数据包为单位调用压缩器。
最近有个小项目,很快的开始,似乎也能很快的完工。就一个不大不小的游戏,2d的,图象引擎是成熟的,然后我就这这段日子对lua的热情,用lua把原来写的C++图象引擎做了个封装。用起来感觉良好,UI部分封装的也不错。游戏逻辑用lua驱动貌似很方便。一度幻想着哪天把它开源出去,没准可以成为lua开发2d游戏的准标准。想想可能性不大,纯当意淫了。
小项目比较能锻炼队伍,所以我做为基本封装就跟新同事上课了。看着别人做程序心里痒痒的,做不出来急急的。恨不得什么都自己代劳。
其中一个同事的工作安排是网络协议的封装,争取用lua封装的好用一些。
我最近在构思一个用P2P网络同步文件的计划。用于以后网络游戏的client同步更新。现有的BT之类的软件很成熟了,不过我考虑到网络游戏有其特殊性,说不定可以做的更好。
网络游戏可以说有一个天然的稳定的p2p网络的基础,好象我们的梦幻西游,最高已经超过八十万玩家同时在线了。而所有玩家所需求的是同一份client。这跟BT网络上传输文件的需求和环境都不太相同。