开发准备:
上次介绍了NIO如何去写了一简单的服务器,没有实用性,真正自己要用到现实的需求中,会很麻烦,所以我们需要把一些关键的东西封装,如何去封装
先参考下面框架图(这是我个人的理解,如有建议欢迎指出):
accept线程:用来接收连接,把接收到的玩家数据包,解包后,添加到消息队列。
wrok线程:用来处理玩家的请求,并把玩家请求让游戏服分发给相应的游戏模块,相应的模块处理后,返回处理结果.
session与数据包
session:把解包、封包、消息队列封装到session。
数据包:由包ID,包长度,包内容组成
分发:由work线程调用游戏服去处理。
代码示例
代码比较多,下面贴主要的代码:
Session:
package network.session; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import network.packet.Msg; /** * * @author Jack Lei * @see readme.txt * @date 2016年1月18日 下午10:11:38 */ public class GameSession implements ISession, ISessionId { private int uid; private SocketChannel channel; private ByteBuffer reciveBuffer; private List<Msg> reciveMsgList; private List<Msg> writeMsgList; /** * @param uid */ public GameSession(SocketChannel channel) { super(); this.channel = channel; reciveBuffer = ByteBuffer.allocate(1024); reciveBuffer.order(ByteOrder.LITTLE_ENDIAN); reciveMsgList = new ArrayList<Msg>(); writeMsgList = new ArrayList<Msg>(); } public void registerId(int id) { this.uid = id; } public Iterator<Msg> iteratorReciveMsg() { return reciveMsgList.iterator(); } public void clearReciveMsg() { reciveMsgList.clear(); } /* (non-Javadoc) * @see packge1.ISessionId#getSessionId() */ @Override public int getSessionId() { // TODO Auto-generated method stub return uid; } public void addWriteMsg(Msg msg) { writeMsgList.add(msg); } public void read() throws IOException { // TODO Auto-generated method stub int readCount = channel.read(reciveBuffer); if (readCount > 0) { reciveBuffer.flip(); if (readCount >= Short.SIZE / 8) { short packetId = reciveBuffer.getShort(); if (packetId > 0) { int size = reciveBuffer.getInt(); byte[] body = new byte[size]; reciveBuffer.get(body); reciveMsgList.add(new Msg(packetId, size, body)); reciveBuffer.clear(); } } } } /* (non-Javadoc) * @see packge1.Session#write(java.nio.ByteBuffer) */ @Override public void write() throws IOException { // TODO Auto-generated method stub if (!writeMsgList.isEmpty()) { Iterator<Msg> iterator = writeMsgList.iterator(); while (iterator.hasNext()) { Msg msg = iterator.next(); ByteBuffer byteBuffer = ByteBuffer.allocate(msg.getMsgLen()); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); byteBuffer.putShort(msg.getPacketId()); byteBuffer.putInt(msg.getSize()); byteBuffer.put(msg.getBody()); byteBuffer.flip(); channel.write(byteBuffer); } writeMsgList.clear(); } } }
数据包;
package network.packet; /** * * @author Jack Lei * @see readme.txt * @date 2016年1月18日 下午9:58:17 */ public class Msg { private short packetId; private int size; private byte[] body = new byte[0]; /** * @param packetId * @param size * @param body */ public Msg(short packetId, int size, byte[] body) { super(); this.packetId = packetId; this.size = size; this.body = body; } /** * @return the packetId */ public short getPacketId() { return packetId; } /** * @param packetId * the packetId to set */ public void setPacketId(short packetId) { this.packetId = packetId; } /** * @return the size */ public int getSize() { return size; } /** * @param size * the size to set */ public void setSize(int size) { this.size = size; } /** * @return the body */ public byte[] getBody() { return body; } /** * @param body * the body to set */ public void setBody(byte[] body) { this.body = body; } public int getMsgLen() { return Short.SIZE / 8 + Integer.SIZE / 8 + body.length; } }
游戏世界:
package network; import java.util.logging.Logger; import network.packet.Msg; import network.packet.PacketId; import network.session.GameSession; /** * * @author Jack Lei * @see readme.txt * @date 2016年1月19日 下午4:50:35 * @desc */ public class GameWorld { private final static Logger logger = Logger.getLogger(GameWorld.class.getName()); private static GameWorld instance = new GameWorld(); private GameWorld() { } public static GameWorld getInstance() { return instance; } public void processNetMessage(GameSession session, Msg msg) { logger.info("recive data from client packetId = " + msg.getPacketId() + " size = " + msg.getSize()); switch (msg.getPacketId()) { case PacketId.REQ_PLAYER_LOGIN: // 这里简单的做个示例,是用其他模块返回这个MSG String body = "登录成功"; byte[] bytes = body.getBytes(); Msg m = new Msg(PacketId.RESP_PLAYER_LOGIN, bytes.length, bytes); session.addWriteMsg(m); break; default: logger.info("error packetId."); break; } } }
后续
服务端的就先写到这了,准备开始写客户端了~
JAVA NIO 服务器(三)
最新推荐文章于 2024-07-17 14:34:23 发布