基于Java NIO的手机答题游戏开发
先上个游戏截图:
豌豆荚地址:http://apps.wandoujia.com/search?key=%E5%85%A8%E6%B0%91%E7%AD%94%E9%A2%98&source=index
本文概要介绍如何通过javanio来开发android客户端与服务器socket通信的过程。
1.为什么要用javaNIO
常规的socket通信为阻塞方式,服务器接收到消息并进行处理,处理完一个才能处理下一个。如果要同时处理,就得自己开线程,为每一个客户端连接做一个线程,这样当连接数大的情况下,上万以上,服务器就难以承受了。JAVANIO是jdk1.4提供的API,主要原理是以监听机制来处理客户端的连接。主要包含buffer和channel.
2.服务器示例代码
packagecom.seya.onlineserver.socket.nio;
importjava.io.File;
importjava.io.IOException;
importjava.net.InetSocketAddress;
importjava.nio.channels.SelectionKey;
importjava.nio.channels.Selector;
importjava.nio.channels.ServerSocketChannel;
importjava.nio.channels.SocketChannel;
importjava.util.ArrayList;
importjava.util.Arrays;
importjava.util.Comparator;
importjava.util.Iterator;
importjava.util.List;
importjava.util.Random;
importjava.util.Vector;
importcom.alibaba.fastjson.JSON;
importcom.alibaba.fastjson.JSONArray;
importcom.alibaba.fastjson.JSONException;
importcom.alibaba.fastjson.JSONObject;
importcom.seya.onlineserver.jpush.JPushManager;
importcom.seya.onlineserver.socket.dao.UserDAO;
importcom.seya.onlineserver.socket.nio.Room.State;
/**
*NIO服务端
*@authorSeyason
*/
publicclassNIOServerimplementsMsgProcessor{
//用户在线标示
publicstaticfinalintSTATUS_ONLINE=1;
publicstaticfinalintSTATUS_OFFLINE=0;
privatestaticfinalintPORT=2121;
//全部用户集合
privateVector<UserClient>allUserList=newVector<UserClient>();
//房间数
privateVector<Room>oneVRoomList=newVector<Room>();
privateVector<Room>twoVRoomList=newVector<Room>();
privateVector<Room>threeVRoomList=newVector<Room>();
publicstaticVector<UserClient>idleUserList=newVector<UserClient>();
publicVector<UserClient>challegePendingList=newVector<UserClient>();
//通道管理器
privateSelectorselector;
UserDAOuDao=newUserDAO();
/**
*获得一个ServerSocket通道,并对该通道做一些初始化的工作
*@paramport绑定的端口号
*@throwsIOException
*/
publicvoidinitServer(intport)throwsIOException{
//获得一个ServerSocket通道
ServerSocketChannelserverChannel=ServerSocketChannel.open();
//设置通道为非阻塞
serverChannel.configureBlocking(false);
//将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(newInetSocketAddress(port));
//获得一个通道管理器
this.selector=Selector.open();
//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
//当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
serverChannel.register(selector,SelectionKey.OP_ACCEPT);
}
publicstaticvoidstartUp(){
NIOServerserver=newNIOServer();
try{
server.initServer(PORT);
server.listen();
}catch(IOExceptione){
e.printStackTrace();
}
}
/**
*采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
*@throwsIOException
*/
@SuppressWarnings("unchecked")
publicvoidlisten()throwsIOException{
System.out.println("服务端启动成功!");
//轮询访问selector
while(true){
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
//获得selector中选中的项的迭代器,选中的项为注册的事件
Iteratorite=this.selector.selectedKeys().iterator();
while(ite.hasNext()){
SelectionKeykey=(SelectionKey)ite.next();
//删除已选的key,以防重复处理
ite.remove();
//客户端请求连接事件
if(key.isAcceptable()){
ServerSocketChannelserver=(ServerSocketChannel)key
.channel();
//获得和客户端连接的通道
SocketChannelchannel=server.accept();
//设置成非阻塞
channel.configureBlocking(false);
UserClientuClient=newUserClient(channel,this);
//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector,SelectionKey.OP_READ,uClient);
}elseif(key.isReadable()){
read(key);
}
}
}
}
publicvoidaddIdleMemb(UserClientuClient){
idleUserList.add(uClient);
}
privateintgetOnlineMembCnt(){
intoneVMembCount=0;
for(Roomroom:oneVRoomList){
oneVMembCount+=room.getMembCount();
}
inttwoVMembCount=0;
for(Roomroom:twoVRoomList){
twoVMembCount+=room.getMembCount();
}
intthreeVMembCount=0;
for(Roomroom:threeVRoomList){
threeVMembCount+=room.getMembCount();
}
intidleCount=0;
for(UserClientuserClient:idleUserList){
if(userClient.state==UserClient.STATE_IN_HALL){
if(userClient.getReqRoomSize()==2){
oneVMembCount++;
}elseif(userClient.getReqRoomSize()==4){
twoVMembCount++;
}elseif(userClient.getReqRoomSize()==6){
threeVMembCount++;
}
}elseif(userClient.state==UserClient.STATE_IN_HOME){
idleCount++;
}
}
//总在线人数
inttotalCount=idleCount+oneVMembCount+twoVMembCount+threeVMembCount;
returntotalCount;
}
privateJSONObjectgetHomeJsonInfo(){
JSONObjectjson=newJSONObject();
intoneVMembCount=0;
for(Roomroom:oneVRoomList){
oneVMembCount+=room.getMembCount();
}
inttwoVMembCount=0;
for(Roomroom:twoVRoomList){
twoVMembCount+=room.getMembCount();
}
intthreeVMembCount=0;
for(Roomroom:threeVRoomList){
threeVMembCount+=room.getMembCount();
}
intidleCount=0;
for(UserClientuserClient:idleUserList){
if(userClient.state==UserClient.STATE_IN_HALL){
if(userClient.getReqRoomSize()==2){
oneVMembCount++;
}elseif(userClient.getReqRoomSize()==4){
twoVMembCount++;
}elseif(userClient.getReqRoomSize()==6){
threeVMembCount++;
}
}elseif(userClient.state==UserClient.STATE_IN_HOME){
idleCount++;
}
}
if(oneVMembCount==0){
Randomr=newRandom();
oneVMembCount=r.nextInt(5);
}
json.put("oneVMembCount",oneVMembCount);
json.put("twoVMembCount",twoVMembCount);
json.put("threeVMembCount",threeVMembCount);
//总在线人数
inttotalCount=idleCount+oneVMembCount+twoVMembCount+threeVMembCount;
//Randomr=newRandom();
//totalCount=r.nextInt(10000);
json.put("totalCount",totalCount);
returnjson;
}
/**
*处理读取客户端发来的信息的事件
*@paramkey
*@throwsIOException
*/
publicvoidread(SelectionKeykey){
//服务器可读取消息:得到事件发生的Socket通道
UserClientuClient=(UserClient)key.attachment();
booleanisSuccess=uClient.onMessage();
if(!isSuccess){
//发送失败,连接异常
removeFromIdle(uClient.getUserId());
}
}
@Override
publicvoidprocess(Stringmsg,UserClientuserClient){
try{
JSONObjectjson=JSON.parseObject(msg);
Stringcmd=json.getString("cmd");
if(Cmd.JOIN.equals(cmd)){
//加入房间
json=json.getJSONObject("data");
Stringtype=json.getString("type");
userClient.populateFromJson(json);
Roomroom=null;
if("new".equals(type)){
room=createNewRoom(userClient);
userClient.setFromHome(false);
}elseif("fast".equals(type)){
room=fastJoin(userClient);
if(json.containsKey("isFromHome")){
userClient.setFromHome(json.getBooleanValue("isFromHome"));
}else{
userClient.setFromHome(false);
}
}elseif(Cmd.ENTERHALL.equals(cmd)){
json=json.getJSONObject("data");
userClient.populateFromJson(json);
userClient.state=UserClient.STATE_IN_HALL;
notifyHomeInfo();
Cmdenter=newCmd(Cmd.ENTERHALL);
JSONObjectmsgData=newJSONObject();
msgData.put("userid",userClient.getUserId());
msgData.put("username",userClient.userInfo.getUsername());
msgData.put("nickname",userClient.userInfo.getNickname());
msgData.put("msg",userClient.userInfo.getNickname()+"刚刚走进了"+getHallName(userClient.getReqRoomSize())+"房间");
enter.data=msgData;
sendToIdle(enter);
}elseif(Cmd.QUIT.equals(cmd)){
//退出房间大厅,回到主界面
userClient.state=UserClient.STATE_IN_HOME;
notifyHomeInfo();
}elseif(Cmd.UNPRESENCE.equals(cmd)){
//退出登录,断开连接
System.out.println("===退出登录==="+userClient.getUserId());
idleUserList.remove(userClient);
allUserList.remove(userClient);
//从挑战者列表中清除
for(inti=0;i<challegePendingList.size();i++){
if(challegePendingList.get(i).getUserId()==userClient.getUserId()){
challegePendingList.remove(i);
i--;
}
}
uDao.setStatus(STATUS_OFFLINE,userClient.getUserId());
System.gc();
notifyHomeInfo();
}elseif(Cmd.ROOMLIST.equals(cmd)){
//客户端主动获取房间列表
sendRoomList(userClient);
}elseif(Cmd.HOME_INFO.equals(cmd)){
//客户端主动获取主页数据
sendHomeInfo(userClient);
}elseif(Cmd.PRESENCE.equals(cmd)){
Cmdalert=newCmd(Cmd.ALERT);
JSONObjectdata=newJSONObject();
json=json.getJSONObject("data");
userClient.populateFromJson(json);
***********
alert.data=data;
userClient.sendCmd(alert);
notifyHomeInfo();
//给不在多人对战房间的人发送消息
sendToIdle(msg);
}
}catch(JSONExceptionex){
ex.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}
privatevoidsendToIdle(Stringmsg)throwsIOException{
for(UserClientidleUser:idleUserList){
idleUser.sendJsonMsg(msg);
}
}
privatevoidsendToIdle(Cmdmsg)throwsIOException{
for(UserClientidleUser:idleUserList){
idleUser.sendCmd(msg);
}
}
/**
*删除无效链接对象
*/
privatevoidcheckInvalidUserClient(intuserId){
removeFromIdle(userId);
for(Roomroom:oneVRoomList){
room.checkInvalidMember(userId);
}
for(Roomroom:twoVRoomList){
room.checkInvalidMember(userId);
}
for(Roomroom:threeVRoomList){
room.checkInvalidMember(userId);
}
}
/**
*给处在主页面的人发送人数变动信息
*/
privatevoidnotifyHomeInfo(){
for(UserClientuClient:idleUserList){
if(uClient.state==UserClient.STATE_IN_HOME){
sendHomeInfo(uClient);
}
}
}
/**
*房间列表变化
*@paramroom
*/
publicvoidrefreshRoom(Roomroom){
Cmdcmd=newCmd(Cmd.REFRESH_ROOM);
cmd.data=room.toJsonSummary();
for(UserClientuClient:idleUserList){
if(uClient.state==UserClient.STATE_IN_HALL&&uClient.getReqRoomSize()==room.capicity){
uClient.sendCmd(cmd);
}
}
notifyHomeInfo();
}
/*
*创建新房间
*/
publicRoomcreateNewRoom(UserClientuClient){
List<Room>roomList=getMatchRoomList(uClient.getReqRoomSize());
booleanfind=false;
RoomrRoom=null;
//查找是否有空房间可以使用
for(Roomroom:roomList){
if(room.state==State.EMPTY){
rRoom=room;
room.addMember(uClient);
find=true;
break;
}
}
//无空房间,重新创建
if(!find){
Roomroom=newRoom(uClient);
roomList.add(room);
rRoom=room;
}
returnrRoom;
}
/**
*用户加入房间
*@paramuClient
*@paramroomId:房间id
*/
publicRoomjoinRoom(UserClientuClient,introomId){
intcapicity=uClient.getReqRoomSize();
List<Room>roomList=getMatchRoomList(capicity);
intsize=roomList.size();
Roomroom=null;
for(inti=size-1;i>=0;i--){
room=roomList.get(i);
if(room.id==roomId){
room.addMember(uClient);
break;
}
}
returnroom;
}
}
3.android客户端连接ServerConn
packagecom.seya.onlineanswer.logic;
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.PrintWriter;
importjava.net.Socket;
importjava.util.Timer;
importjava.util.TimerTask;
importorg.json.JSONException;
importorg.json.JSONObject;
importandroid.os.AsyncTask;
importandroid.os.Handler;
importandroid.os.Message;
importcom.seya.onlineanswer.ui.Cmd;
importcom.seya.onlineanswer.ui.StageActivity;
importcom.seya.onlineanswer.util.GlobalVar;
importcom.seya.onlineanswer.util.LogX;
importcom.seya.onlineanswer.util.PreferencesUtil;
/**
*Serverconnectionthread
*@authorAdministrator
*
*/
publicclassServerConnimplementsRunnable{
publicstaticfinalintPING_DELAY=2*1000;//2秒ping一次
Timertimer;
privateBufferedReaderin=null;
privatePrintWriterout=null;
privateHandlermHandler=null;
publicServerConn(Socketserver,Handlerhandler)throwsIOException{
/*obtainaninputstreamfromtheserver*/
in=newBufferedReader(newInputStreamReader(
server.getInputStream()));
out=newPrintWriter(server.getOutputStream(),true);
mHandler=handler;
}
publicvoidsetHandler(Handlerhandler){
mHandler=handler;
}
privatevoidsendMsg(Stringmsg){
newAsyncTask<String,Object,Boolean>(){
@Override
protectedBooleandoInBackground(String...params){
//防止消息连在一起发送
synchronized(out){
out.println(params[0]);
out.flush();
}
returnnull;
}
}.execute(msg);
}
/**
*客户端Ping服务器,测试连接
*/
publicvoidping(){
timer=newTimer();
TimerTasktimerTask=newTimerTask(){
@Override
publicvoidrun(){
sendMsg("");
}
};
timer.schedule(timerTask,0,PING_DELAY);
}
publicvoidcancelPing(){
if(timer!=null){
timer.cancel();
timer=null;
}
}
publicvoidsendUnPresenceMsg(Stringmsg){
newAsyncTask<String,Object,Boolean>(){
@Override
protectedBooleandoInBackground(String...params){
out.println(params[0]);
returnnull;
}
@Override
protectedvoidonPostExecute(Booleanresult){
super.onPostExecute(result);
mHandler.sendEmptyMessage(StageActivity.MSG_SHUTDOWN_CONN);
}
}.execute(msg);
}
publicvoidrun(){
Stringmsg=null;
try{
/**
*loopmessagefromserverandprocess
*/
while((msg=in.readLine())!=null){
LogX.print("服务器返回=="+msg);
MessageuiMsg=mHandler.obtainMessage(StageActivity.MSG_SERVER_PUSH);
uiMsg.obj=msg;
uiMsg.sendToTarget();
}
}catch(IOExceptione){
System.err.println(e);
}
}
publicvoidquit(){
JSONObjectquit=newJSONObject();
try{
quit.put("cmd",Cmd.QUIT);
JSONObjectdata=newJSONObject();
data.put("userid",GlobalVar.userId);
quit.put("data",data);
sendMsg(quit.toString());
}catch(JSONExceptione){
e.printStackTrace();
}
}
//下线
publicvoidunPresence(){
JSONObjectquit=newJSONObject();
try{
LogX.print("===unPresence===");
quit.put("cmd",Cmd.UNPRESENCE);
JSONObjectdata=newJSONObject();
data.put("userid",GlobalVar.userId);
quit.put("data",data);
sendUnPresenceMsg(quit.toString());
}catch(JSONExceptione){
e.printStackTrace();
}
}
/**
*进入房间
*@paramroomSize
*/
publicvoidjoin(introomSize,Stringtype,introomId,booleanisFromHome,intchallegeUid){
JSONObjectjoin=newJSONObject();
try{
join.put("cmd",Cmd.JOIN);
//进入房间,再次验证
JSONObjectdata=newJSONObject();
data.put("userid",GlobalVar.userId);
data.put("roomsize",roomSize);
data.put("type",type);
data.put("roomid",roomId);
data.put("isFromHome",isFromHome);
data.put("chaUid",challegeUid);
join.put("data",data);
LogX.print("發送登錄:"+join.toString());
sendMsg(join.toString());
}catch(JSONExceptione){
e.printStackTrace();
}
}
/**
*发送就绪命令
*/
publicvoidready(){
JSONObjectready=newJSONObject();
try{
ready.put("cmd",Cmd.READY);
//进入房间,再次验证
JSONObjectdata=newJSONObject();
data.put("userid",GlobalVar.userId);
ready.put("data",data);
LogX.print("發送就绪:"+ready.toString());
sendMsg(ready.toString());
}catch(JSONExceptione){
e.printStackTrace();
}
}
}
欢迎了解学习交流
豌豆荚地址:http://apps.wandoujia.com/search?key=%E5%85%A8%E6%B0%91%E7%AD%94%E9%A2%98&source=index