一个多人在线的棋牌类网络游戏的项目临近尾声,我参与了该项目的整个设计流程,并且完成了90%的核心代码。关于这个项目,有很多地方值得聊一聊。本系列不打算把这个项目将得多么详细规范,那是设计文档应该描述的,我打算只说说一些值得注意的地方。这个项目的一个特别之处是,客户端是手机,用户通过移动网络与服务器通信。和PC相比,手机的处理能力极弱,而且网络流量费用昂贵。因为除了要考虑普通网络游戏的一些问题之外,这两点也需要在设计中充分考虑。首先是开发语言的选择,由于服务器是Linux的环境,MS的技术直接排除,至于MONO嘛,我实在不放心。可供选择的是C++和Java,Java胜在网络能力强大,开发周期短,有众多框架和开源库的支持,要写出烂得不可接受的代码也不容易;C++则胜在速度快。综合各方面因素,C++更容易把这个项目变成一堆代码噩梦,我们选择了Java。
  一、网络
  网络游戏,首先面临的问题当然是如何进行网络通信。首先考虑的是HTTP协议,因为所有的J2ME手机都支持这个,我们当然想尽可能的兼容用户。而且HTTP协议封装程度已经非常高了,不用去考虑线程、同步、状态管理、连接池,不过HTTP协议有两个不爽的地方:
  ◇ 协议无状态,这个问题已经困扰过很多人很多次了。我曾考虑过的解决办法是改造HTTP协议,在数据传输完成之后不关闭socket,但是这样做工作量非常大,在项目周期中,基本上就是Mission impossible,不予考虑。那么客户也就只能通过轮询的方式向服务器请求数据。
  ◇ 网络流量过大。就这个项目来说,网络间传递的只是指令,但是每次传递都要加上一堆毫无用处的HTTP Head,再加上客户端需要做轮询,这个流量对于手机来说简直恐怖,经简单测试,按照0.03元/K的GPRS网络费用计算,一局牌居然要消耗1元多的费用(每秒轮询),实在不可接受。也许我们可以采用流量费包月的资费方式,不过这个话题与技术无关。
  以上问题导致我们选择了Socket,这意味着我们将没有一个web环境,很多东西都要靠自己去实现:线程管理、客户状态监控、对象池、控制台……….网络部分打算采用Java NIO来实现,这是一种新的网络监听方式,基于事件的异步通信,可以提高性能。每个客户端连接之后,会有一个独立的SocketChannel与它通信,这个SocketChannel会在用户的整个生存周期中存在。用户如果断开连接,服务器会得到-1,并且会抛出Connection reset异常,通过捕获这两个特征,可以在用户意外断开连接后清理相关的资源。由于NIO是异步通信的,所以没有复杂的线程管理。
  二、通信协议
  这个项目并没有复杂的通信指令,命令数量很有限,但是还是有个关键问题需要关注:流量。为了尽量减小流量,我们使用字节代替字符串来保存系统指令,这样可以使流量减少一半,比如使用一个字节来保存一张扑克牌,字节高位表示花色,字节低位表示数字,如果0代表黑桃,那么黑桃三就应该是0x03,这个需要靠位操作来实现:
  int m=0;
  int n=3;
  byte card=(byte)(m)<<4)|((byte)n; //m左移四位,然后与n左或操作
  游戏中需要传递用户的积分,这是一个大整数,使用四个字节来保存比较保险,将整数转换为四个字节的操作如下:
  package org.bromon.games;
  public static byte[] translateLong(long mark)
  {
  byte[] b = new byte[4];
  for (int i = 0; i < 4; i++)
  {
  b[i] = (byte) (mark >>> (24 - i * 8));
  }
 三、数据库连接池
  由于没有一个web环境,所以我们需要自己实现一个数据库连接池,apache有一个项目叫做commons DBCP,这是一个基于apache自己的对象池(apache commons pool)实现的数据库连接池,我们可以直接拿来使用,apache的软件未必是最好的,但是极大可能比我们自己写的要好。Commons DBCP需要三个.jar:commons-collections-3.1.jar、commons-dbcp-1.2.1.jar、commons-pool-1.2.jar这三个文件都可以在apache – Jakarta – commons项目下下载,加入到工程中即可。构造一个数据库连接池的代码如下:
  package org.bromon.games;
  import java.sql.*;
  import com.gwnet.games.antiLord.util.*;
  import org.apache.commons.dbcp.ConnectionFactory;
  import org.apache.commons.dbcp.BasicDataSource;
  import org.apache.commons.dbcp.DataSourceConnectionFactory;
  private static BasicDataSource bds=new BasicDataSource();
  private static ConnectionFactory fac=null;
  //初始化连接池
  bds.setDriverClassName(“org.postgresql.Driver”); //数据库驱动程序
  bds.setUrl(“jdbc:postgresql://localhost:5432/myDB”); //数据库url
  bds.setUsername(“postgres”); //dba帐号
  bds.setPassword(“XXXXXXXX”); //密码
  bds.setInitialSize(100); //初始化连接数量
  bds.setMaxIdle(10); //最大idle数
  bds.setMaxWait(1000*60); //超时回收时间
  fac=new DataSourceConnectionFactory(bds); //得到连接工厂
  Connection conn=fac.createConnection(); //从池中获得连接
  conn.close(); //释放连接,回到池中
  //销毁连接池
  bds.close();
  bds=null;
  fac=null;
  请自行处理操作中的各种异常。
  四、扑克牌的生成
  游戏中需要为用户生成随机的扑克牌,首先我们需要初始化一副牌,放到一个Hashmap中,每张牌以一个字节表示,高为代表花色,的为代表数字,生成整副牌:
  package org.bromon.games;
  private static HashMap cards = new HashMap();
  int tmp=0;
  for (int i = 0; i <4; i++) {
  for (int m = 0; m < 13; m++) {
  tmp=((byte)(i)<<4)|((byte)m); //使用位操作构造一张牌
  cards.put(new Integer(i * 13 + m),new Byte((byte)tmp));
  }
  }
  cards.put(new Integer(53), new Byte((byte)0x4d)); //大王
  cards.put(new Integer(54), new Byte((byte)0x4e)); //小王
  如何随机地得到其中的N张牌呢?我们的做法是生成一个0-55的随机数,用这个随机数作主键从Hashmap中获得对象,取得之后,把该对象从队列中删除,以免重复取得。由于java中的随机数是根据时间生成的,所以有可能导致用户得到的牌不够散,每个用户都摸到一条龙岂不是笑话?所以在生成随机数的时候我们加入了一个大素数来作运算:
  long cardId=new Long((Math.round(Math.random() * 87) % 55)).intValue();通过修改这个大素数,可以控制某个用户的牌比较好。
  return b;
  五、线程
  实际上本系统并没有复杂的线程管理,但是我想提供一个控制台让管理员可以管理游戏主线程,可以让它停止、中段、恢复、重启动,本来的设计是管理员通过与线程A打交道,通过A去管理主线程B,但是熟悉java线程的朋友都知道,线程互相管理基本上就是不实际的,举个最简单的例子,A如何销毁B?也许你会说调用B的destroy()方法就好了,网上很多讲解java线程的资料也确实是这么说的,但是他们都是鬼扯的,自己去看看java源代码吧,Thread.destroy()方法的实际代码如下:
  package org.bromon.games;
  public void destroy()
  {
  throw new NoSuchMethodError();
  }
  事实真相是,Thread.destroy()方法自始至终就没有被实现过。所有写文章,教别人用这个方法销毁线程的人,都去撞墙吧,丢人丢大了。最好的办法是A负责生成一个B并且启动它,然后B自己管理生存周期,A和B通过使用可共享的方法来通信,这是sun推荐的做法。
  六、异步消息
  用户玩牌的过程中,有很多东西需要记录下来,比如记录用户的积分、等级变化,记录玩牌日志供数据统计等,当用户数量很多的时候,在数据库中记录这些信息会很耗费资源,用户玩了一局之后会可能会等待很长时间。解决这个问题的方法是利用J2EE的消息bean来提供异步通信的机制,需要记录数据的时候,系统会封装一个值对象,发送给J2EE容器,这个操作是很快的,完成之后就返回,用户可以继续操作,不用关心消息何时被处理。J2EE的消息框架具备如下特征:
  ◇消息一定会被阅读,而且只阅读一次。JMS框架有自己的算法,把消息缓冲到硬盘,就算J2EE服务器死掉,消息也不会丢失。
  ◇系统采用点对点的Queue消息队列,可以保证同等优先级的消息先进先出。
  在Jboss 4.0中,部署消息Bean和Queue队列,都比weblogic 8.1来的容易,只需要在jboss.xml中声明消息目的地,如果jboss发现该目的地不存在的话,会自动建立一个,实在很简单。关于消息bean的开发与部署,我有专门的文章描述。
  七、启动与退出
  为了让系统具备让人满意的性能,应该尽量多的重用对象,减少创建新对象。比如上面提到的消息发送,我们的操作是提供一个静态类,在系统启动的时候就初始化,保持与JMS服务器的连接,系统发送消息的时候,不用再去查询JNDI和生成QueueConnectionFactory,这样可以提高系统响应速度。
  在数据库连接池的问题上,我们也采用同样的操作,启动的时候初始化N个连接。但是如果在关闭进程的时候不做任何操作,会导致JMS抛出socket异常,虽然没什么大的影响,但总显得不专业,而且池中的连接不被释放的话,也可能导致问题。最好能够让系统像jboss等控制台程序一样,ctrl+c之后能够执行操作,释放资源再退出。我们可以通过给进程/线程加上一个Hook来实现,windows程序员应该对这个非常熟悉。
  Hook应该是一个线程方法,如下:
  package org.bromon.games;
  public class Hook extends Thread
  {
  public void run()
  {
  //释放数据库连接,销毁连接池
  //关闭与JMS的连接
  }
  }
  在主线程中加入:Runtime.getRuntime().addShutdownHook(new Hook()) ;那么进程/线程会在退出的时候执行Hook的run方法,清理资源。
  }
  将四个字节转回来的操作如下:
  package org.bromon.games;
  public static long translateByte(byte[] b)
  {
  int mask = 0xff;
  int temp = 0;
  int res = 0;
  for (int i = 0; i < 4; i++)
  {
  res <<= 8;
  temp = b[i] & mask;
  res |= temp;
  }
  return res;
  }

多人在线游戏服务器端开发心得相关推荐

  1. 多人在线游戏服务器端开发心得(转)

    http://edu.21cn.com/youxi/g_141_548352-1.htm 一个多人在线的棋牌类网络游戏的项目临近尾声,我参与了该项目的整个设计流程,并且完成了90%的核心代码.关于这个 ...

  2. 大型多人在线游戏的开发中,如何做到每个玩家动作的实时同步的?

    游戏开发中,有两种主要的同步模式,一种是状态同步, 一种是帧同步, 这两种同步模式是完全不一样的. 接下来我们来分别的分析一下他们同步的原理. 先看状态同步,服务器上跑所有的游戏逻辑(没有图像,动画, ...

  3. KBEngine和Unity3d搭建多人在线游戏(1)

    kbengine环境的搭建 Unity3d及kbengine多人在线游戏的开发 今天和大家一起学习如何使用kbengine及Unity3d搭建一款多人在线游戏,内容设计多个游戏玩家游戏的登录,打怪系统 ...

  4. 多人在线游戏开发 Unity3D 序言

    序言 这本书打算给你以步骤的方式,通过概念和使用unity游戏引擎使用中间插件创建多人游戏.我一直是一个痴迷于多人游戏的的粉丝.他们有方法轻触我们最基本的欲望,如充满了竞争,合作,以及和我们周围的人类 ...

  5. 使用FLEX3开发大型多人在线游戏

    使用FLEX3开发大型多人在线游戏 2009-09-02 10:07 使用FLEX3开发大型多人在线游戏 收藏 使用FLEX3开发大型多人在线游戏 大型多人在线游戏(MMO)技术已经涉足到各种软件形式 ...

  6. 使用Adobe Flex 3开发大型多人在线游戏

    使用Adobe Flex 3开发大型多人在线游戏 2011年03月28日 大型多人在线游戏(MMO) 技术已经涉足到各种软件形式中了. 当我们还在思考MMO 时, 多人游戏已经使很多玩家能够实时连接进 ...

  7. BigWorld Pty. Ltd.是一家全球领先的大型多人在线游戏(MMOG)开发解决方案供应商...

    BigWorld Pty. Ltd.是一家全球领先的大型多人在线游戏(MMOG)开发解决方案供应商.其开发套件为网络游戏提供了一整套解决方案,可大幅度提高游戏产品质量,并使用户大大降低游戏开发成本,从 ...

  8. 游戏开发中的问题-----摘自《大型多人在线游戏开发》

    原文: 虽然在大型多人在线游戏开发过程中会遇到很多问题和挑战,但最关键的一点就是有能力编写稳健的代码.这意味着我们必须进行良好的设计而不是随意地堆砌代码,这意味着我们必须在代码中加入足够的注释以方便支 ...

  9. php 游戏开发swoole,Swoole+websocket多人在线游戏示例

    利用Swoole,可以使用PHP来开发Websocket服务器,实现多人在线游戏.本文通过一个最简单的摇骰子游戏来介绍Server端(由PHP+Swoole实现)和Client端(由H5+Websoc ...

最新文章

  1. 前端如何获取后台通过map封装的值_如何舒服的写api接口?
  2. 在.net中运用HTMLParser解析网页的原理和方法
  3. 支付宝接口开发-手机网站支付-沙箱测试
  4. vba可以放服务器上处理文档,vba 用服务器运行 内存溢出
  5. linux轻量级进程LWP
  6. Bitmap的内存占用
  7. HTTPS-客户端与服务器三次握手过程(含wireshark分析)
  8. 寒气重吃什么可以最容易排出来?
  9. @开发者,这个 GitHub 项目可以褥羊毛!
  10. 微信雄踞,头条社交梦破碎?| 畅言
  11. Spring Aop 报错 ...but was actually of type 'com.sun.proxy.$Proxy**'的解决方案
  12. Coursera课程自然语言处理(NLP) 借助概率模型做自然语言处理 deeplearning.ai
  13. 30岁开始实现我的程序员梦
  14. WireShark抓Intel网卡的802.1q包
  15. 如何将PDF转成PPT?为什么转换后不能编辑
  16. 阿里又上美国“售假黑名单” 是被故意抹黑了吗
  17. 教你如何安慰失戀人?
  18. H264视频传输、编解码----FFmpeg软解码
  19. 图书馆管理系统课程设计
  20. Android Qcom USB Driver学习(一)

热门文章

  1. 目标检测YOLO实战应用案例100讲-基于改进的YOLOv4柑橘目标检测算法
  2. PowerDesigner 实用手册
  3. windows下git使用出错:git push -u origin master fatal: unable to access ‘https://github.com
  4. html页面怎么改title,js修改页面标题 title
  5. vant 时间选择控件只显示年份,解决只能返回1970年
  6. B2B企业如何打造自己的私域流量池?
  7. 访问Harbor报502 Bad Gateway
  8. 关于ArcSDE版本压缩(Compress)的再研究
  9. 基于朴素贝叶斯和预训练Bert模型的中文句子情感分类实践
  10. 爱签电子合同成为国家区块链服务网络BSN指定合格开发者