一个棋牌游戏的服务端开发1
一个多人在线的棋牌类网络游戏的项目临近尾声,我参与了该项目的整个设计流程,并且完成了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左或操作
游戏中需要传递用户的积分,这是一个大整数,使用四个字节来保存比较保险,将整数转换为四个字节的操作如下:
public static byte[] translateLong(long mark)
{
? ? byte[] b = new byte[4];
? ??for (int i = 0; i < 4; i++)
? ??{
? ?? ???b?= (byte) (mark >>> (24 - i * 8));
? ??}
? ??return b;
}
将四个字节转回来的操作如下:
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?& mask;
? ?? ???res |= temp;
? ??}
? ??return res;
}
三、数据库连接池
由于没有一个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项目下下载,加入到工程中即可。
构造一个数据库连接池的代码如下:
? ??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;
请自行处理操作中的各种异常。