基于Java NIO的手机解题游戏开发

基于Java NIO的手机答题游戏开发
先上个游戏截图:
基于Java NIO的手机解题游戏开发
基于Java NIO的手机解题游戏开发
豌豆荚地址: http://apps.wandoujia.com/search?key=%E5%85%A8%E6%B0%91%E7%AD%94%E9%A2%98&source=index

本文概要介绍如何通过java nio来开发android客户端与服务器socket通信的过程。
1. 为什么要用java NIO
常规的socket通信为阻塞方式,服务器接收到消息并进行处理,处理完一个才能处理下一个。 如果要同时处理,就得自己开线程,为每一个客户端连接做一个线程,这样当连接数大的情况下,上万以上,服务器就难以承受了。 JAVA NIO是jdk1.4提供的API, 主要原理是以监听机制来处理客户端的连接。主要包含buffer和channel.
基于Java NIO的手机解题游戏开发
2. 服务器示例代码
package com.seya.onlineserver.socket.nio;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Vector;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.seya.onlineserver.jpush.JPushManager;
import com.seya.onlineserver.socket.dao.UserDAO;
import com.seya.onlineserver.socket.nio.Room.State;

/**
* NIO服务端
* @author Seyason
*/
public class NIOServer implements MsgProcessor{
//用户在线标示
public static final int STATUS_ONLINE = 1;
public static final int STATUS_OFFLINE = 0;

private static final int PORT = 2121;

//全部用户集合
private Vector<UserClient> allUserList = new Vector<UserClient>();

//房间数
private Vector<Room> oneVRoomList = new Vector<Room>();

private Vector<Room> twoVRoomList = new Vector<Room>();

private Vector<Room> threeVRoomList = new Vector<Room>();

public static Vector<UserClient> idleUserList = new Vector<UserClient>();

public Vector<UserClient> challegePendingList = new Vector<UserClient>();
//通道管理器
private Selector selector;

UserDAO uDao = new UserDAO();
/**
* 获得一个ServerSocket通道,并对该通道做一些初始化的工作
* @param port  绑定的端口号
* @throws IOException
*/
public void initServer(int port) throws IOException {
// 获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 设置通道为非阻塞
serverChannel.configureBlocking(false);
// 将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(new InetSocketAddress(port));
// 获得一个通道管理器
this.selector = Selector.open();
//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
//当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

}

public static void startUp() {
    NIOServer server = new NIOServer();
        try {
            server.initServer(PORT);
            server.listen();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
System.out.println("服务端启动成功!");
// 轮询访问selector
while (true) {
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 客户端请求连接事件
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key
.channel();
// 获得和客户端连接的通道
SocketChannel channel = server.accept();
// 设置成非阻塞
channel.configureBlocking(false);
UserClient uClient = new UserClient(channel, this);
//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ, uClient);
} else if (key.isReadable()) {
read(key);
}

}

}
}

public void addIdleMemb(UserClient uClient) {
idleUserList.add(uClient);
}

private int getOnlineMembCnt() {
        int oneVMembCount = 0;
        for(Room room : oneVRoomList) {
            oneVMembCount += room.getMembCount();
        }
       
        int twoVMembCount = 0;
        for(Room room : twoVRoomList) {
            twoVMembCount += room.getMembCount();
        }
       
       
        int threeVMembCount = 0;
        for(Room room : threeVRoomList) {
            threeVMembCount += room.getMembCount();
        }
       
        int idleCount = 0;
        for (UserClient userClient : idleUserList) {
            if (userClient.state == UserClient.STATE_IN_HALL) {
                if (userClient.getReqRoomSize() == 2) {
                    oneVMembCount++;
                } else if (userClient.getReqRoomSize() == 4) {
                    twoVMembCount++;
                } else if (userClient.getReqRoomSize() == 6) {
                    threeVMembCount++;
                }
            } else if (userClient.state == UserClient.STATE_IN_HOME) {
                idleCount++;
            }
        }
       
       
        //总在线人数
        int totalCount = idleCount + oneVMembCount + twoVMembCount + threeVMembCount;
       
        return totalCount;
}

private JSONObject getHomeJsonInfo() {
JSONObject json = new JSONObject();
int oneVMembCount = 0;
for(Room room : oneVRoomList) {
oneVMembCount += room.getMembCount();
}

int twoVMembCount = 0;
for(Room room : twoVRoomList) {
twoVMembCount += room.getMembCount();
}


int threeVMembCount = 0;
for(Room room : threeVRoomList) {
threeVMembCount += room.getMembCount();
}

int idleCount = 0;
for (UserClient userClient : idleUserList) {
if (userClient.state == UserClient.STATE_IN_HALL) {
if (userClient.getReqRoomSize() == 2) {
oneVMembCount++;
} else if (userClient.getReqRoomSize() == 4) {
twoVMembCount++;
} else if (userClient.getReqRoomSize() == 6) {
threeVMembCount++;
}
} else if (userClient.state == UserClient.STATE_IN_HOME) {
idleCount++;
}
}



if (oneVMembCount == 0) {
    Random r = new Random();
    oneVMembCount = r.nextInt(5);
}

json.put("oneVMembCount", oneVMembCount);
json.put("twoVMembCount", twoVMembCount);
json.put("threeVMembCount", threeVMembCount);

//总在线人数
int totalCount = idleCount + oneVMembCount + twoVMembCount + threeVMembCount;
// Random r = new Random();
// totalCount = r.nextInt(10000);

json.put("totalCount", totalCount);

return json;
}

/**
* 处理读取客户端发来的信息 的事件
* @param key
* @throws IOException
*/
public void read(SelectionKey key) {
// 服务器可读取消息:得到事件发生的Socket通道
UserClient uClient = (UserClient) key.attachment();
boolean isSuccess = uClient.onMessage();
if (!isSuccess) {
//发送失败,连接异常
removeFromIdle(uClient.getUserId());
}
}



@Override
public void process(String msg, UserClient userClient) {
    try {
        JSONObject json = JSON.parseObject(msg);
        String cmd = json.getString("cmd");
        if (Cmd.JOIN.equals(cmd)) {
        //加入房间
            json = json.getJSONObject("data");
            String type = json.getString("type");
            userClient.populateFromJson(json);
           
            Room room = null;
            if ("new".equals(type)) {
                room = createNewRoom(userClient);
                userClient.setFromHome(false);
            } else if ("fast".equals(type)) {
                room = fastJoin(userClient);
                if (json.containsKey("isFromHome")) {
                    userClient.setFromHome(json.getBooleanValue("isFromHome"));
                } else {
                    userClient.setFromHome(false);
                }
            } else if (Cmd.ENTERHALL.equals(cmd)) {
            json = json.getJSONObject("data");
            userClient.populateFromJson(json);
            userClient.state = UserClient.STATE_IN_HALL;
            notifyHomeInfo();
           
            Cmd enter = new Cmd(Cmd.ENTERHALL);
            JSONObject msgData = new JSONObject();
            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);
        } else if (Cmd.QUIT.equals(cmd)) {
            //退出房间大厅,回到主界面
            userClient.state = UserClient.STATE_IN_HOME;
            notifyHomeInfo();
        } else if (Cmd.UNPRESENCE.equals(cmd)) {
            //退出登录, 断开连接
            System.out.println("===退出登录===" + userClient.getUserId());
            idleUserList.remove(userClient);
            allUserList.remove(userClient);
           
            //从挑战者列表中清除
            for (int i=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();
        } else if (Cmd.ROOMLIST.equals(cmd)) {
            //客户端主动获取房间列表
            sendRoomList(userClient);
        } else if (Cmd.HOME_INFO.equals(cmd)) {
            //客户端主动获取主页数据
            sendHomeInfo(userClient);
        } else if (Cmd.PRESENCE.equals(cmd)) {
            Cmd alert = new Cmd(Cmd.ALERT);
            JSONObject data = new JSONObject();
            json = json.getJSONObject("data");
            userClient.populateFromJson(json);
            ***********
            alert.data = data;
            userClient.sendCmd(alert);
            notifyHomeInfo();
           
            //给不在多人对战房间的人发送消息
            sendToIdle(msg);
        }
    } catch (JSONException ex) {
        ex.printStackTrace();
    } catch (IOException e) {
e.printStackTrace();
}
}

private void sendToIdle(String msg) throws IOException {
for (UserClient idleUser : idleUserList) {
idleUser.sendJsonMsg(msg);
}
}

private void sendToIdle(Cmd msg) throws IOException {
for (UserClient idleUser : idleUserList) {
idleUser.sendCmd(msg);
}
}

/**
* 删除无效链接对象
*/
private void checkInvalidUserClient(int userId) {
    removeFromIdle(userId);
   
    for (Room room : oneVRoomList) {
        room.checkInvalidMember(userId);
    }
    for (Room room : twoVRoomList) {
            room.checkInvalidMember(userId);
        }
    for (Room room : threeVRoomList) {
            room.checkInvalidMember(userId);
        }
   
   
}


/**
* 给处在主页面的人发送人数变动信息
*/
private void notifyHomeInfo() {
for (UserClient uClient : idleUserList) {
if (uClient.state == UserClient.STATE_IN_HOME) {
sendHomeInfo(uClient);
}
}
}

/**
* 房间列表变化
* @param room
*/
public void refreshRoom(Room room) {
Cmd cmd = new Cmd(Cmd.REFRESH_ROOM);
cmd.data = room.toJsonSummary();

for (UserClient uClient : idleUserList) {
            if (uClient.state == UserClient.STATE_IN_HALL && uClient.getReqRoomSize() == room.capicity) {
                uClient.sendCmd(cmd);
            }
        }

notifyHomeInfo();
}



/*
* 创建新房间
*/
public Room createNewRoom(UserClient uClient) {
List<Room> roomList = getMatchRoomList(uClient.getReqRoomSize());
boolean find = false;
Room rRoom = null;
//查找是否有空房间可以使用
for (Room room : roomList) {
if (room.state == State.EMPTY) {
rRoom = room;
room.addMember(uClient);
find = true;
break;
}
}

//无空房间,重新创建
if (!find) {
Room room = new Room(uClient);
roomList.add(room);
rRoom = room;
}

return rRoom;
}



/**
* 用户加入房间
* @param uClient
* @param roomId : 房间id
*/
public Room joinRoom(UserClient uClient, int roomId) {
int capicity = uClient.getReqRoomSize();
List<Room> roomList = getMatchRoomList(capicity);

int size = roomList.size();
Room room = null;
for (int i = size - 1; i >= 0; i--) {
room = roomList.get(i);
if (room.id == roomId) {
room.addMember(uClient);
break;
}
}

return room;
}



}


3. android 客户端连接ServerConn
package com.seya.onlineanswer.logic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;

import org.json.JSONException;
import org.json.JSONObject;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;

import com.seya.onlineanswer.ui.Cmd;
import com.seya.onlineanswer.ui.StageActivity;
import com.seya.onlineanswer.util.GlobalVar;
import com.seya.onlineanswer.util.LogX;
import com.seya.onlineanswer.util.PreferencesUtil;
/**
* Server connection thread
* @author Administrator
*
*/
public class ServerConn implements Runnable {
    public static final int PING_DELAY = 2*1000; //2秒ping一次
    Timer timer;
    private BufferedReader in = null;
    private PrintWriter out = null;
    private Handler mHandler = null;
   
   
    public ServerConn(Socket server, Handler handler) throws IOException {
        /* obtain an input stream from the server */
        in = new BufferedReader(new InputStreamReader(
                        server.getInputStream()));
        out = new PrintWriter(server.getOutputStream(), true);
        mHandler = handler;
    }
   
    public void setHandler (Handler handler) {
    mHandler = handler;
    }
    
    private void sendMsg(String msg) {
    new AsyncTask<String, Object, Boolean>() {

    @Override
    protected Boolean doInBackground(String... params) {
      //防止消息连在一起发送
        synchronized (out) {
            out.println(params[0]);
                    out.flush();
        }
    return null;
    }
   
   
    }.execute(msg);
    }
   
    /**
     * 客户端Ping服务器,测试连接
     */
    public void ping() {
        timer = new Timer();
        TimerTask timerTask = new TimerTask() {
           
            @Override
            public void run() {
                sendMsg("");
            }
        };
       
        timer.schedule(timerTask, 0, PING_DELAY);
    }
   
    public void cancelPing() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
   
 
   
    public void sendUnPresenceMsg(String msg) {
    new AsyncTask<String, Object, Boolean>() {

    @Override
    protected Boolean doInBackground(String... params) {
    out.println(params[0]);
    return null;
    }

@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
mHandler.sendEmptyMessage(StageActivity.MSG_SHUTDOWN_CONN);
}
   
   
    }.execute(msg);
    }
   
    public void run() {
        String msg = null;
        try {
         /**
          * loop message from server and process
          */
            while ((msg = in.readLine()) != null) {
            LogX.print("服务器返回=="+msg);
                Message uiMsg = mHandler.obtainMessage(StageActivity.MSG_SERVER_PUSH);
                uiMsg.obj = msg;
                uiMsg.sendToTarget();
            }
        } catch (IOException e) {
            System.err.println(e);
        }
    }
   
    public void quit() {
    JSONObject quit = new JSONObject();
    try {
quit.put("cmd", Cmd.QUIT);
JSONObject data = new JSONObject();
        data.put("userid", GlobalVar.userId);
quit.put("data", data);
sendMsg(quit.toString());
} catch (JSONException e) {
e.printStackTrace();
}
    }
   
   
    //下线
    public void unPresence() {
    JSONObject quit = new JSONObject();
    try {
    LogX.print("===unPresence===");
quit.put("cmd", Cmd.UNPRESENCE);
JSONObject data = new JSONObject();
        data.put("userid", GlobalVar.userId);
quit.put("data", data);
sendUnPresenceMsg(quit.toString());
} catch (JSONException e) {
e.printStackTrace();
}
    }
   
   
    /**
     * 进入房间
     * @param roomSize
     */
    public void join(int roomSize, String type, int roomId, boolean isFromHome, int challegeUid) {
    JSONObject join = new JSONObject();
    try {
join.put("cmd", Cmd.JOIN);

//进入房间, 再次验证
JSONObject data = new JSONObject();
        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 (JSONException e) {
e.printStackTrace();
}
    }
   

   
    /**
     * 发送就绪命令
     */
    public void ready() {
    JSONObject ready = new JSONObject();
    try {
    ready.put("cmd", Cmd.READY);

//进入房间, 再次验证
JSONObject data = new JSONObject();
        data.put("userid", GlobalVar.userId);
        ready.put("data", data);
       
        LogX.print("發送就绪: "+ready.toString());
        sendMsg(ready.toString());
} catch (JSONException e) {
e.printStackTrace();
}
    }
  
}
欢迎了解学习交流
豌豆荚地址: http://apps.wandoujia.com/search?key=%E5%85%A8%E6%B0%91%E7%AD%94%E9%A2%98&source=index