`
seya
  • 浏览: 355842 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

基于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.

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
分享到:
评论

相关推荐

    JAVA上百实例源码以及开源项目

     基于JAVA的UDP服务器模型源代码,内含UDP服务器端模型和UDP客户端模型两个小程序,向JAVA初学者演示UDP C/S结构的原理。 简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 ...

    JAVA上百实例源码以及开源项目源代码

     基于JAVA的UDP服务器模型源代码,内含UDP服务器端模型和UDP客户端模型两个小程序,向JAVA初学者演示UDP C/S结构的原理。 简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 ...

    java开源包4

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包11

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包6

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包101

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包9

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包5

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包8

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包10

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包1

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包3

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包2

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    java开源包7

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    Java资源包01

    j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF...

    Java开发微信朋友圈PC版系统(架构1.0+Spring Boot2.X实战)

    本课程对应着系统架构1.0,即第一阶段,主要的目标在于实现一个完整的系统,可以学到的技术还是比较多的:Spring Boot2.X、Java基础、Java8、JUC、NIO、微服务、分布式、系统架构设计、SpringMVC、MySQL、Luc

Global site tag (gtag.js) - Google Analytics