一起做网游吧【9】:多服务器支持

兑现上周的承诺

代码的位置:

;

先说下编译的方法,满足某些人的好奇心。截图就不提了,之前的有。

由于这次引入了web的方式,所以编译的准备工作有些麻烦。我使用mochiweb来作为web的服务器端程序,extjs作为客户端的javascript程序库,这两个库均未包含在git的仓库中,但可以通过执行others目录中的get_others.sh来获取,这个简单的脚本将获取mochiweb和extjs,并放置到相应的位置中,同时还设置了几个链接。之所以设置链接是由于压缩成7z之后,原来的链接特性消失了,维护不方便。对于ubuntu 10.04和fedora13来说,还有一个问题是关于mochiweb的,貌似最新的mochiweb需要使用erl R14编译,但这两个发行版都是R13(其他的我没有试),编译的时候会出错的,最简单的就是将每个erl最后的那些个测试删掉就可以了。如果服务器的源代码编译不过去,则可能是中文注释的问题,之前有erlang的中文注释补丁的办法,这里不说了。

首先执行get_others.sh,然后到server下执行make,到client下面最里面的makefile目录下执行make,就完成了。(当然,环境变量什么的还是需要手动设置的。)

运行就是以前的start-dev.sh,没什么好说的。下面开始技术分析:

在记录中(schema.hrl)的client段,变成了下面的样子:

       -record(client, {server = none, 
                 player = none,
                 server_name = none,
                 server_port = none,
                 server_ip = none,
                 server_pid = none,
                 game=none}).
       

添加的变量类型主要是为了进行多服务器的支持,server_pid为port端口的pid值,这样客户端来区分当前在哪一个服务器上。其他的没什么说明的。

server数据库中的记录格式:

        -record(server, {
          id,
          socketpid = none,
          ip = none,
          port = none,
          name = none,
          enable = false,
          maxuser = 5000,
          curuser = 0,
          pid = none
          }).
       

pid暂时没考虑到如何用,socketpid的值为port端口的pid值,这样可以随时查看数据库来看到运行的进程id。curuser在数据库中并不能实时反映,其实添加也很简单,只是觉得这样有点影响效率,毕竟内存操作要比磁盘操作快些。

player数据库的记录格式:

       -record( player, {
           id,
           nick,
           password,
           login_errors = 0,
           pid = none,
           socket = none,
           disabled = false,
           online = false,
           ingame = false,
           now_play=0,
           server_pid = none,
           tag_server = none,
           tag_port = none,
           code = none,
           game=[]
          } ).
       

想比以前的规划,添加了server_pid, tag_server, tag_port,code等4个属性值,还是上面的话,为了多服务器支持,server_pid指名当前加入的服务器的pid值,该值在判断用户登录成功后但并未选择进入指定服务器时进行判断用。tag_server,tag_port,code这三个变量主要是为了服务器的负载平衡使用。在服务器接收到进入服务器的消息时,服务器会对服务器组中的每一个服务器按照当前用户进行一个排序,如果用户目前登录用的服务器是最少用户的话,则直接进入,如果不是的话,这三个变量就起作用了,服务器将返回一个消息,指示断开当前链接,然后使用code值来直接连接tag_server的tag_port端口。之所以使用code值,是为了避免用户登录,而这样的话,则可能出现安全问题(用户不用密码也登录进来了),这个时候,服务器需要用户提供这个code值来做检查,服务器首先从数据库中找到code为客户发过来的用户资料(同时还检查tag_server和tag_port是否为之前约定好的),如果可以找到,则调用该用户资料,然后就像断开服务器之前一样执行。因此这个code值非常的重要,在正式使用的时候,应该再增加一个时效性的检查(这里没有)。code值使用下面的算法生成:

       Code = erlang:phash2(Client#player.nick ++ float_to_list(random:uniform()), 1 bsl 32),
       

用户名+随机浮点数的hash值,由于用户名不可能重复,因此该hash值难以重复。理论上来说,很大程度上防止了用户利用不用输入密码连接服务器的漏洞。

服务器的规划方面,将服务器的启动和操作放到了serverstate.erl文件中,具体可以看文件中的代码,我本来想让该文件仅仅进行服务器状态的监视,没想到越弄越多,最后彻底相反了,server.erl变成了提供服务接口的函数文件,serverstate.erl成了主要干活的了。这里说下其中的几个内容就可以了:

发布者

rix

如果连自己都不爱自己,哪还有谁来爱你