`
seya
  • 浏览: 361379 次
  • 性别: 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经典问答 pdg格式

    Java作为一门广泛使用的编程语言,其经典问答涵盖了众多领域,包括基础语法、面向对象特性、集合框架、多线程、异常处理、IO流、网络编程、反射机制、JVM优化、设计模式等。这份"Java经典问答"资料,无疑是准备面试...

    java基于socket手写协议的在线考试系统

    在IT行业中,构建一个基于Java的在线考试系统是一项常见的任务,尤其当涉及到网络通信和数据交换时,Socket编程成为不可或缺的技术。本项目“java基于socket手写协议的在线考试系统”着重于使用Java语言实现自定义...

    java开源包4

    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资源包01

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

    答题系统V2.0.zip

    【标题】"答题系统V2.0.zip"是一款基于JAVA编程语言开发的答题应用程序,它提供了用户友好的界面和实用的功能。此系统的核心亮点在于它的计时和计分机制,使得在线测试或知识竞赛等活动更加公正且具有挑战性。然而,...

    java开源包2

    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开源包7

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

    Stackoverflow上人气最旺的10个Java问题J

    Stackoverflow作为一个全球知名的程序员问答社区,汇集了各种技术问题和解决方案,尤其对于Java这样的主流编程语言,其讨论热度更是不言而喻。这些热门问题反映了Java开发中常见的痛点、难点以及开发者需要掌握的...

    2021山东大学软件学院JAVA高程课设-考试平台的设计

    在本项目中,“2021山东大学软件学院JAVA高程课设-考试平台的设计”是一个基于Java技术的课程设计项目,旨在让学生掌握高级Java编程技能,并应用到实际的在线考试系统开发中。这个项目的核心目标是设计并实现一个...

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

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

    Java Jeopardy-开源

    Java Jeopardy是一款基于Java开发的开源问答游戏,灵感来源于美国的知名电视游戏节目"Jeopardy!"。这个游戏设计主要用于教育环境,特别是课堂互动,它允许教师或学生以一种有趣的方式学习和复习Java编程语言的知识点...

Global site tag (gtag.js) - Google Analytics