服务器端开发:网络在线游戏开发心得体会(服务器端、Java)



  个多人在线棋牌类网络游戏项目临近尾声我参和了该项目整个设计流程并且完成了90%核心代码有关这个项目有很多地方值得聊本系列不打算把这个项目将得多么详细规范标准那是设计文档应该描述我打算只说说些值得注意地方

   这个项目个特别的处是客户端是手机用户通过移动网络和服务器通信和PC相比手机处理能力极弱而且网络流量费用昂贵除了要考虑普通网络游戏些问题的外这两点也需要在设计中充分考虑

   首先是开发语言选择由于服务器是Linux环境MS技术直接排除至于MONO嘛我实在不放心可供选择是C和JavaJava胜在网络能力强大开发周期短有众多框架和开源库支持要写出烂得不可接受代码也不容易;C则胜在速度快综合各方面原因C更容易把这个项目变成堆代码噩梦我们选择了Java


、网络

   网络游戏首先面临问题当然是如何进行网络通信首先考虑是HTTP协议所有J2ME手机都支持这个我们当然想尽可能兼容用户而且HTTP协议封装程度已经非常高了不用去考虑线程、同步、状态管理、连接池不过HTTP协议有两个不爽地方:

   ◇ 协议无状态这个问题已经困扰过很多人很多次了我曾考虑过解决办法是改造HTTP协议在数据传输完成的后不关闭但是这样做工作量非常大在项目周期中基本上就是Mission impossible不予考虑那么客户也就只能通过轮询方式向服务器请求数据

   ◇ 网络流量过大就这个项目来说网络间传递只是指令但是每次传递都要加上堆毫无用处HTTP Head再加上客户端需要做轮询这个流量对于手机来说简直恐怖经简单测试按照0.03元/KGPRS网络费用计算局牌居然要消耗1元多费用(每秒轮询)实在不可接受也许我们可以采用流量费包月资费方式不过这个话题和技术无关

   以上问题导致我们选择了Socket这意味着我们将没有个web环境很多东西都要靠自己去实现:线程管理、客户状态监控、对象池、控制台……….

   网络部分打算采用Java NIO来实现这是种新网络监听方式基于事件异步通信可以提高性能每个客户端连接的后会有个独立SocketChannel和它通信这个SocketChannel会在用户整个生存周期中存在用户如果断开连接服务器会得到-1并且会抛出Connection re异常通过捕获这两个特征可以在用户意外断开连接后清理相关资源由于NIO是异步通信所以没有复杂线程管理


2、通信协议

   这个项目并没有复杂通信指令命令数量很有限但是还是有个关键问题需要关注:流量为了尽量减小流量我们使用字节代替串来保存系统指令这样可以使流量减少比如使用个字节来保存张扑克牌字节高位表示花色字节低位表示数字如果0代表黑桃那么黑桃 3就应该是0x03这个需要靠位操作来实现:

m=0;
n=3;
card=()(m)<<4)|(()n; //m左移 4位然后和n左或操作

   游戏中需要传递用户积分这是个大整数使用 4个字节来保存比较保险将整数转换为 4个字节操作如下:

public translateLong(long mark)
{
b = [4];
for ( i = 0; i < 4; i)
{
b[i] = () (mark >>> (24 - i * 8));
}
b;
}

   将 4个字节转回来操作如下:

public long translateByte( b)
{
mask = 0xff;
temp = 0;
res = 0;
for ( i = 0; i < 4; i)
{
res <<= 8;
temp = b[i] & mask;
res |= temp;
}
res;
}


3、数据库连接池

   由于没有个web环境所以我们需要自己实现个数据库连接池apache有个项目叫做commons DBCP这是个基于apache自己对象池(apache commons pool)实现数据库连接池我们可以直接拿来使用apache软件Software未必是最好但是极大可能比我们自己写要好

Commons DBCP需要 3个.jar:

commons-collections-3.1.jar、commons-dbcp-1.2.1.jar、commons-pool-1.2.jar

这 3个文件都可以在apache – Jakarta – commons项目下下载加入到工程中即可

构造个数据库连接池代码如下:

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 BasicDataSource bds= BasicDataSource;
private ConnectionFactory fac=null;

//化连接池
bds.DriverClassName(“org.postgresql.Driver”); //数据库驱动
bds.Url(“jdbc:postgresql://localhost:5432/myDB”); //数据库url
bds.Username(“postgres”); //dba帐号
bds.Password(“XXXXXXXX”); //密码
bds.InitialSize(100); //化连接数量


bds.MaxIdle(10); //最大idle数
bds.MaxWait(1000*60); //超时回收时间

fac= DataSourceConnectionFactory(bds); //得到连接工厂
Connection conn=fac.createConnection; //从池中获得连接
conn.close; //释放连接回到池中

//销毁连接池
bds.close;
bds=null;
fac=null;

请自行处理操作中各种异常


4、扑克牌生成

   游戏中需要为用户生成随机扑克牌首先我们需要副牌放到个Hashmap中每张牌以个字节表示高为代表花色为代表数字生成整副牌:

private HashMap cards = HashMap;

tmp=0;
for ( i = 0; i <4; i) {
for ( m = 0; m < 13; m) {
tmp=(()(i)<<4)|(()m); //使用位操作构造张牌
cards.put( Integer(i * 13 + m), Byte(()tmp));
}
}

cards.put( Integer(53), Byte(()0x4d)); //大王
cards.put( Integer(54), Byte(()0x4e)); //小王

   如何随机地得到其中N张牌呢?我们做法是生成个0-55随机数用这个随机数作主键从Hashmap中获得对象取得的后把该对象从队列中删除以免重复取得由于java中随机数是根据时间生成所以有可能导致用户得到牌不够散每个用户都摸到条龙岂不是笑话?所以在生成随机数时候我们加入了个大素数来作运算:

long cardId= Long((Math.round(Math.random * 87) % 55)).value;

通过修改这个大素数可以控制某个用户牌比较好


5、线程

   实际上本系统并没有复杂线程管理但是我想提供个控制台让管理员可以管理游戏主线程可以让它停止、中段、恢复、重启动本来设计是管理员通过和线程A打交道通过A去管理主线程B但是熟悉java线程朋友都知道线程互相管理基本上就是不实际举个最简单例子A如何销毁B?也许你会说Bdestroy思路方法就好了网上很多讲解java线程资料也确实是这么说但是他们都是鬼扯自己去看看java源代码吧Thread.destroy思路方法实际代码如下:

public void destroy
{
throw NoSuchMethodError;
}

   事实真相是Thread.destroy思路方法自始至终就没有被实现过所有写文章教别人用这个思路方法销毁线程都去撞墙吧丢人丢大了

   最好办法是A负责生成个B并且启动它然后B自己管理生存周期A和B通过使用可共享思路方法来通信这是sun推荐做法


6、异步消息

   用户玩牌过程中有很多东西需要记录下来比如记录用户积分、等级变化记录玩牌日志供数据统计等当用户数量很多时候在数据库中记录这些信息会很耗费资源用户玩了局的后会可能会等待很长时间解决这个问题思路方法是利用J2EE消息bean来提供异步通信机制需要记录数据时候系统会封装个值对象发送给J2EE容器这个操作是很快完成的后就返回用户可以继续操作不用关心消息何时被处理

   J2EE消息框架具备如下特征:

◇ 消息定会被阅读而且只阅读JMS框架有自己算法把消息缓冲到硬盘就算J2EE服务器死掉消息也不会丢失

◇ 系统采用点对点Queue消息队列可以保证同等优先级消息先进先出

   在Jboss 4.0中部署消息Bean和Queue队列都比weblogic 8.1来容易只需要在jboss.xml中声明消息目如果jboss发现该目地不存在会自动建立实在很简单有关消息bean开发和部署我有专门文章描述(参见我blog:http://blog.csdn.net/bromon )


7、启动和退出

   为了让系统具备让人满意性能应该尽量多重用对象减少创建新对象比如上面提到消息发送我们操作是提供个静态类在系统启动时候就保持和JMS服务器连接系统发送消息时候不用再去查询JNDI和生成QueueConnectionFactory这样可以提高系统响应速度

   在数据库连接池问题上我们也采用同样操作启动时候化N个连接但是如果在关闭进程时候不做任何操作会导致JMS抛出异常虽然没什么大影响但总显得不专业而且池中连接不被释放也可能导致问题最好能够让系统像jboss等控制台ctrl+c的后能够执行操作释放资源再退出我们可以通过给进程/线程加上个Hook来实现windows员应该对这个非常熟悉

Hook应该是个线程思路方法如下:

public Hook extends Thread
{
public void run
{
//释放数据库连接销毁连接池
//关闭和JMS连接
}
}

在主线程中加入:

Runtime.getRuntime.addShutdownHook( Hook) ;

那么进程/线程会在退出时候执行Hookrun思路方法清理资源

Tags:  java应用服务器 javaweb服务器 java服务器 服务器端开发

延伸阅读

最新评论

发表评论