java游戏服务端实现
java游戏服务端实现
一个多人在线的棋牌类网络游戏的项目临近尾声,我参与了该项目的整个设计流程,并且完成了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[i] = (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[i] & 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.java.html" target="_blank">ConnectionFactory
;
import
org.apache.commons.dbcp.java.html" target="_blank">BasicDataSource
;
import
org.apache.commons.dbcp.java.html" target="_blank">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); //得到连接工厂
java/sql/Connection.java.html" target="_blank">Connection
conn=fac.createConnection(); //从池中获得连接
conn.close(); //释放连接,回到池中
//销毁连接池
bds.close();
bds=null
;
fac=null
;
请自行处理操作中的各种异常。
四、扑克牌的生成
游戏中需要为用户生成随机的扑克牌,首先我们需要初始化一副牌,放到一个Hashmap中,每张牌以一个字节表示,高为代表花色,的为代表数字,生成整副牌:
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
java/lang/Integer.java.html" target="_blank">Integer
(i * 13 + m),new
java/lang/Byte.java.html" target="_blank">Byte
((byte
)tmp));
}
}
cards.put(new
java/lang/Integer.java.html" target="_blank">Integer
(53), new
java/lang/Byte.java.html" target="_blank">Byte
((byte
)0x4d)); //大王
cards.put(new
java/lang/Integer.java.html" target="_blank">Integer
(54), new
java/lang/Byte.java.html" target="_blank">Byte
((byte
)0x4e)); //小王
如何随机地得到其中的N张牌呢?我们的做法是生成一个0-55的随机数,用这个随机数作主键从Hashmap中获得对象,取得之后,把该对象从队列中删
除,以免重复取得。由于java中的随机数是根据时间生成的,所以有可能导致用户得到的牌不够散,每个用户都摸到一条龙岂不是笑话?所以在生成随机数的时
候我们加入了一个大素数来作运算:
long
cardId=new
java/lang/Long.java.html" target="_blank">Long
((java/lang/Math.java.html" target="_blank">Math
.round(java/lang/Math.java.html" target="_blank">Math
.random() * 87) % 55)).intvalue();
通过修改这个大素数,可以控制某个用户的牌比较好。
五、线程
实际上本系统并没有复杂的线程管理,但是我想提供一个控制台让管理员可以管理游戏主线程,可以让它停止、中段、恢复、重启动,本来的设计是管理员通过与
线程A打交道,通过A去管理主线程B,但是熟悉java线程的朋友都知道,线程互相管理基本上就是不实际的,举个最简单的例子,A如何销毁B?也许你会说
调用B的destroy()方法就好了,网上很多讲解java线程的资料也确实是这么说的,但是他们都是鬼扯的,自己去看看java源代码
吧,Thread.destroy()方法的实际代码如下:
public
void
destroy()
{
throw
new
java/lang/NoSuchMethodError.java.html" target="_blank">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的开发与部署,我有专门的文章描述(参见我的
blog:http://blog.csdn.net/bromon )。
七、启动与退出
为了让系统具备让
人满意的性能,应该尽量多的重用对象,减少创建新对象。比如上面提到的消息发送,我们的操作是提供一个静态类,在系统启动的时候就初始化,保持与JMS服
务器的连接,系统发送消息的时候,不用再去查询JNDI和生成QueueConnectionFactory,这样可以提高系统响应速度。
在数据库连接池的问题上,我们也采用同样的操作,启动的时候初始化N个连接。但是如果在关闭进程的时候不做任何操作,会导致JMS抛出socket异
常,虽然没什么大的影响,但总显得不专业,而且池中的连接不被释放的话,也可能导致问题。最好能够让系统像jboss等控制台程序一样,ctrl+c之后
能够执行操作,释放资源再退出。我们可以通过给进程/线程加上一个Hook来实现,windows程序员应该对这个非常熟悉。
Hook应该是一个线程方法,如下:
public
class
Hook extends
java/lang/Thread.java.html" target="_blank">Thread
{
public
void
run()
{
//释放数据库连接,销毁连接池
//关闭与JMS的连接
}
}
在主线程中加入:
java/lang/Runtime.java.html" target="_blank">Runtime
.getRuntime().addShutdownHook(new
Hook()) ;
那么进程/线程会在退出的时候执行Hook的run方法,清理资源。
java游戏服务端实现相关推荐
- Java游戏服务端开发系列文章
已经好久没有更新自己的学习状态.或许是工作太忙吧,生活被工作羁绊,无法抽身.最近因为某些事情的原因,工作上有点充裕的时间来分享自己的一点小小经验.因为毕竟我也是一个混迹职场几年了 虽然天资不咋地,但是 ...
- Java游戏服务端开发之开课了
从今天开始我将会重新的做一个专栏.这个专栏是专门为新手讲解游戏服务端开发的文章系列.同时也是将自己多年的经验做一个系统的分享. 通过这个系列大家可以掌握到一个游戏开发中的一些概率以及在线游戏服务器所包 ...
- java游戏服务端_JAVA游戏服务端的速度比得上c++吗?
看你要做什么游戏 有一类游戏叫益智休闲类游戏,就是用php做服务器都没问题,何况java 现在热门的吃鸡类游戏,官方钦定UE4,UE4自带服务器,跟客户端一样是C++ 另外就是各类mmo游戏,这类游戏 ...
- 推广下自己的JAVA开源游戏服务端框架
Carmelo是基于Java的游戏服务端框架,适合于页游和手游.它的主要特点是: 利用Netty实现高效的NIO通信,同时支持TCP/HTTP协议 完善的三层架构模型,易扩展 通用.完善的sessio ...
- 从Web后端(Java)转到游戏服务端的感受
九零后的男生几乎都是玩电子游戏长大的,做游戏开发几乎是每个九零后男生从小就有的梦想吧.我的大学时代,正好与移动Web高速发展的时代重合了,大学里几乎所有同学都是做Web方向的开发,Web前端,Java ...
- 畅玩mt3单机游戏服务器维护,【梦幻西游】MT3仿端手工游戏服务端源码[教程+授权物品后台]...
[梦幻西游]MT3仿端手工游戏服务端源码[教程+授权物品后台] 架设教程 系统:CentOS 6.8 64位 1.关闭防火墙 chkconfig iptables off service iptab ...
- 百万在线:大型游戏服务端开发
进入手游时代,服务端技术也在向前演进.现代游戏服务端既要承载数以万计的在线玩家,又要适应快速变化的市场需求,因此,如何设计合适的架构就成了重中之重.服务端技术并不简单,作为服务端新人,全面掌握服务端技 ...
- 谈谈一只菜鸟转行Erlang游戏服务端的经历(希望大佬指导,也希望我的经历能给一些还未毕业的同学或者正在迷茫自己工作内容的同学一些感触)
入行半年Erlang游戏服务端的心得体验 个人经历 转方向后的工作经历 工作中遇到的问题和处理 一些游戏服务端开发的体验和想法 最后谈谈自己现在面临的问题 第一次发文章,主要想谈谈自己近半年来的心路历 ...
- 关于游戏服务端的搭建
前一阵,一场突如其来的疫情,将我们都困在了家中,于是有了大把的时间,来做一些自己感兴趣的事情.当然,是打游戏咯. 不过只是打游戏,也太low了,完全不符合我们程序员的气质,我们作为网络世界中的 ...
最新文章
- 关于Jdk7与Jdk8对Collections进行分组的区别
- java之包装类与BigInteger、BigDecimal
- SSL/TLS 配置
- 第3篇:Flowable快速工作流脚手架Jsite_关闭演示模式
- bzoj3238 [Ahoi2013]差异 后缀自动机
- Python+django网页设计入门(4):用户登录与登录验证
- 【前端开发】前端引入公共部分footer header的几种方法,及iframe自适应高度js
- 基于libuv的TCP设计(二)
- undefined reference to `major‘
- 微信小程序:获取验证码
- swifty项目中pod的使用
- 苹果 iOS 15.5更新了哪些功能 苹果 iOS 15.5值得更新吗
- 【巡店软件系统开发】1、软件架构及基础功能的设定
- java-spring的JdbcTempldate对oracle 的CLob字段进行读和写
- java的HMACSHA1加密算法
- eclipse juno(4.2) 集成 maven 插件
- SGI STL的rb_tree浅析
- 基于MQ对登录系统核心流程进行异步化改造,提升系统性能-11
- powerdesigner CDM中联系理解
- PMP考试的分数线是怎么确定的(记分规则,成绩的演变)