/** *消息头 * Created by Administrator on 16-4-10. */ public final class Header implements Serializable{ private static final long serialVersionUID = 1L; private short msgId; private short msgLen; private int serialNo; private int sessionId; public Header(){ super(); } public Header(short msgId,short msgLen,short serialNo,short sessionId){ this.msgId=msgId; this.msgLen=msgLen; this.serialNo=serialNo; this.sessionId=sessionId; } public short getMsgId() { return msgId; } public void setMsgId(short msgId) { this.msgId = msgId; } public short getMsgLen() { return msgLen; } public void setMsgLen(short msgLen) { this.msgLen = msgLen; } public int getSerialNo() { return serialNo; } public void setSerialNo(int serialNo) { this.serialNo = serialNo; } public int getSessionId() { return sessionId; } public void setSessionId(int sessionId) { this.sessionId = sessionId; } @Override public String toString() { return "Header{" + "msgId=" + msgId + ", msgLen=" + msgLen + ", serialNo=" + serialNo + ", sessionId=" + sessionId + '}'; } }
/** * Created by Administrator on 16-4-10. */ public final class NettyMessage implements Serializable{ private static final long serialVersionUID = 1L; //消息头 private Header header; //消息体 private Object body; public NettyMessage(){ super(); } public NettyMessage(Header header,Object body){ this.header=header; this.body=body; } public Header getHeader() { return header; } public void setHeader(Header header) { this.header = header; } public Object getBody() { return body; } public void setBody(Object body) { this.body = body; } public void setMsgId(int msgId){ if(header==null){ header=new Header(); } header.setMsgId((short)msgId); } @Override public String toString() { return "NettyMessage{" + "header=" + header + ", body=" + body + '}'; } }
Netty编解码
/** * Created by Administrator on 16-4-10. */ public class MyMessageDecoder extends ByteToMessageDecoder { private static final Logger logger= LoggerFactory.getLogger(MyMessageDecoder.class); @Override protected void decode(ChannelHandlerContext chx, ByteBuf in, List<Object> out) throws Exception { if(in.readableBytes()>=12){ in.markReaderIndex(); NettyMessage message=new NettyMessage(); Header header=new Header(); short msgId=in.readShort(); short msgLen=in.readShort(); int serialNo=in.readInt(); int sessionId=in.readInt(); logger.debug("msgId={};msgLen={};SerialNo={};session={}", msgId, msgLen,serialNo, sessionId); header.setMsgId(msgId); header.setMsgLen(msgLen); header.setSerialNo(serialNo); header.setSessionId(sessionId); // 如果body没有接收完整 if(in.readableBytes() < header.getMsgLen()) { in.resetReaderIndex(); // ByteBuf回到标记位置 } else { byte[] bodyBytes = new byte[header.getMsgLen()]; in.readBytes(bodyBytes); message.setHeader(header); message.setBody(bodyBytes); out.add(message); } } /*System.out.println("#########"+in.readableBytes()); in.markReaderIndex();*/ } }
/** * Created by Administrator on 16-4-11. */ public class MyMessageEncoder extends MessageToByteEncoder<NettyMessage> { private static final Logger logger= LoggerFactory.getLogger(MyMessageEncoder.class); public MyMessageEncoder(){ super(); } @Override protected void encode(ChannelHandlerContext ctx, NettyMessage message, ByteBuf byteBuf) throws Exception { if(message.getHeader()==null){ logger.error("头信息为空"); throw new Exception("头信息为空"); } if(message.getHeader().getMsgId()==0){ logger.error("命令未设置"); throw new Exception("命令未设置"); } byte[] bytes=null; if (message.getBody() instanceof MessageLite) { bytes= ((MessageLite) message.getBody()).toByteArray(); }else if (message.getBody() instanceof MessageLite.Builder) { bytes= ((MessageLite.Builder) message.getBody()).build() .toByteArray(); }else if(message.getBody() instanceof byte[]){ bytes =(byte[]) message.getBody(); }else{ logger.error("body无法解析"); throw new Exception("body无法解析"); } if(bytes!=null){ byteBuf.writeShort(message.getHeader().getMsgId()); int len=bytes.length; byteBuf.writeShort(len); byteBuf.writeInt(message.getHeader().getSerialNo()); byteBuf.writeInt(message.getHeader().getSessionId()); byteBuf.writeBytes(bytes); logger.debug("msgId={};msgLen={};SerialNo={};session={}",message.getHeader().getMsgId(),len,message.getHeader().getSerialNo(),message.getHeader().getSessionId()); }else{ logger.error("数据体为空"); throw new Exception("数据体为空!!"); } } }
游戏服务
/** * Created by Administrator on 16-5-31. */ public class GameService implements Runnable{ private int port; public GameService() { } public GameService(int port) { this.port = port; } public void run() { EventLoopGroup bossGroup = new NioEventLoopGroup();//线程组 EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap();//server启动管理配置 b.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .option(ChannelOption.SO_BACKLOG, 1024)//最大客户端连接数为1024 .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler( new ChannelInitializer<SocketChannel>() { public void initChannel(SocketChannel ch) { ch.pipeline().addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass() .getClassLoader()))); ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast(new GameHandle()); } } ); ChannelFuture f = b.bind(port).sync(); if (f.isSuccess()) { System.out.println("ClientService starts success at port:" + port); } f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
/** * Created by Administrator on 16-5-31. */ public class GameHandle extends SimpleChannelInboundHandler<NettyMessage> { private static final Logger logger = LoggerFactory.getLogger(GameHandle.class); /*@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { NettyMessage nettyMessage = (NettyMessage) msg; switch (nettyMessage.getHeader().getMsgId()) { case 1: gameServerReg(ctx, nettyMessage); break; case 2: createGameRoom(ctx, nettyMessage); break; default: break; } ReferenceCountUtil.release(nettyMessage); }*/ /** * 连接断开执行的方法 * * @param ctx * @throws Exception */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override protected void messageReceived(ChannelHandlerContext ctx, NettyMessage nettyMessage) throws Exception { switch (nettyMessage.getHeader().getMsgId()) { case 1: gameServerReg(ctx, nettyMessage); break; case 2: createGameRoom(ctx, nettyMessage); break; case 3: sendPing(ctx, nettyMessage); break; case 4: addToRoomResult(ctx, nettyMessage); default: break; } ReferenceCountUtil.release(nettyMessage); } /** * 加入房间 回调 * @param ctx * @param nettyMessage */ private void addToRoomResult(ChannelHandlerContext ctx, NettyMessage nettyMessage) { AddRoomResultMsg resultMsg=(AddRoomResultMsg)nettyMessage.getBody(); int userChannelId=resultMsg.getUserChannelId(); UserChannel userChannel = ChannelMapUtil.getUser(userChannelId); if(resultMsg.getCode()==1){ userChannel.setRoomId(resultMsg.getRoomUUID()); userChannel.setGameChannelId(resultMsg.getServerChannelId()); } SocketChannel channel=userChannel.getSocketChannel(); if(channel!=null){ nettyMessage.setMsgId(LandlordsTable.PBAssignedSendRequest.MSG.ID_VALUE); LandlordsTable.PBAssignedResponse.Builder resp=LandlordsTable.PBAssignedResponse.newBuilder(); resp.setErrorStr(resultMsg.getErrMsg()); resp.setState(resultMsg.getCode()); nettyMessage.setBody(resp.build().toByteArray()); channel.writeAndFlush(nettyMessage); } } /** * 游戏服务器向消息服务器发起注册 * * @param ctx * @param msg * @throws Exception */ private void gameServerReg(ChannelHandlerContext ctx, NettyMessage msg) throws Exception { SeRegMsg m1 = (SeRegMsg) msg.getBody(); logger.debug("gameServerReg run msg={}", m1); String regKey = PropertyUtil.getContextProperty("msg.server.regkey"); SeRegReMsg seRegReMsg = new SeRegReMsg(); if (regKey.equals(m1.getRegKey())) { int channelId=ctx.channel().id().hashCode(); AuForMsgRoutingServer auForMsgRoutingServer = SpringBeanUtil.getBean("auForMsgRoutingServer"); //调用dubbo获取房间初始化配置信息 List<GameRoomInit> list = auForMsgRoutingServer.getGameRoomInit(); //回写游戏房间需要游戏管道ID,服务器ID,服务器名称 addServerInfo(list,channelId,m1); seRegReMsg.setResSuccess(true); seRegReMsg.setInitList(list); seRegReMsg.setMsgType(MsgType.SERVER_REGIN); seRegReMsg.setChannelId(channelId); msg.setBody(seRegReMsg); GameChannel gameChannel = new GameChannel(); gameChannel.setChannelId(channelId); gameChannel.setGameServerId(m1.getServerId()); gameChannel.setGameServerName(m1.getServerName()); gameChannel.setSocketChannel((SocketChannel)ctx.channel()); ChannelMapUtil.addGameChannel(gameChannel.getChannelId(), gameChannel); } else { seRegReMsg.setResSuccess(false); } ctx.writeAndFlush(msg); } /** * 回写游戏房间需要游戏管道ID,服务器ID,服务器名称 * @param channelId * @param m1 * @throws Exception */ public void addServerInfo(List<GameRoomInit> list,int channelId, SeRegMsg m1) throws Exception { for (int i = 0 ;i<list.size();i++){ GameRoomInit gameRoomInit = list.get(i); list.get(i).setChannelId(channelId); list.get(i).setServerId(m1.getServerId()); list.get(i).setServerName(new String(m1.getServerName().getBytes("utf-8"),"gbk")); } } /** * 向BUS回写游戏服务器创建好的房间 * @param ctx * @param msg * @throws Exception */ private void createGameRoom(ChannelHandlerContext ctx, NettyMessage msg) throws Exception { List<GameRoom> gameRoomList = (List<GameRoom>) msg.getBody(); if (gameRoomList != null && !gameRoomList.isEmpty()) { AuForMsgRoutingServer auForMsgRoutingServer = SpringBeanUtil.getBean("auForMsgRoutingServer"); auForMsgRoutingServer.saveGameRooms(gameRoomList); } } /** * 心跳检查 * @param ctx * @param msg * @return * @throws Exception */ private boolean sendPing(ChannelHandlerContext ctx, NettyMessage msg) throws Exception { GameChannel gameChannel = ChannelMapUtil.getGameChannel(ctx.channel().id().hashCode()); if (gameChannel != null) { SeRegMsg seRegMsg = (SeRegMsg) msg.getBody(); if (gameChannel.getGameServerId().equals(seRegMsg.getServerId()) && gameChannel.getGameServerName() .equals(seRegMsg.getServerName())) { //logger.debug("{}发起心跳", gameChannel.getChannelId()); AuForMsgRoutingServer server=SpringBeanUtil.getBean("auForMsgRoutingServer"); if(server!=null){ server.updateServerHeartBeat(gameChannel.getChannelId()); }else{ logger.error("心跳逻辑无法执行,注入失败!"); } } } return true; } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.debug("游戏服务器连接出错,错误信息:{}", cause.getMessage()); //ctx.close(); } }
/** * Created by T420 on 16-3-28. */ public class ClientService implements Runnable { private int port; public ClientService() { } public ClientService(int port) { this.port = port; } public void run() { EventLoopGroup bossGroup = new NioEventLoopGroup();//线程组 EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap();//server启动管理配置 b.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .option(ChannelOption.SO_BACKLOG, 1024)//最大客户端连接数为1024 .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler( new ChannelInitializer<SocketChannel>() { public void initChannel(SocketChannel ch) { ch.pipeline().addLast(new MyMessageDecoder()); ch.pipeline().addLast(new MyMessageEncoder()); ch.pipeline().addLast(new ClientHandle()); } } ); ChannelFuture f = b.bind(port).sync(); if (f.isSuccess()) { System.out.println("ClientService starts success at port:" + port); } f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
/** * Created by T420 on 16-3-28. */ public class ClientHandle extends SimpleChannelInboundHandler<NettyMessage> { private static final Logger logger = LoggerFactory.getLogger(ClientHandle.class); private OrderedQueuePoolExecutor recvExcutor = new OrderedQueuePoolExecutor( "客户端消息接收队列", 100, 10000); @Override //当客户端连上服务器的时候会触发此函数 public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.debug("channel id =={} hashCode=={}", ctx.channel().id(), ctx.channel().id().hashCode()); ChannelMapUtil channelMapUtil = new ChannelMapUtil(); channelMapUtil.addTempChannel((SocketChannel) ctx.channel()); } @Override //当客户端断开连接的时候触发函数 public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.debug("clinet:" + ctx.channel().id().hashCode() + " leave server"); AuForMsgRoutingServer auForMsgRoutingServer = SpringBeanUtil.getBean("auForMsgRoutingServer"); UserChannel uc = ChannelMapUtil.getUser(ctx.channel().id().hashCode()); if (uc != null) { auForMsgRoutingServer.updateOffLine(uc.getChannelId(), uc.getUserId()); } ChannelMapUtil.removeUserChannel(ctx.channel().id().hashCode()); //ctx.close(); } /** * @param ctx * @param msg * @throws Exception */ @Override protected void messageReceived(ChannelHandlerContext ctx, NettyMessage msg) throws Exception { recvExcutor.addTask(String.valueOf(msg.getHeader().getSessionId()), new MWork( msg, ctx)); ReferenceCountUtil.release(msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.debug("{}连接出错,错误信息:{}", ctx.channel().id().hashCode(), cause.getMessage()); //cause.printStackTrace(); } class MWork extends AbstractWork { /** * 消息 * */ private NettyMessage request; /** * 消息队列 * */ private ChannelHandlerContext ctx; public MWork(NettyMessage request, ChannelHandlerContext ctx) { this.request = request; this.ctx = ctx; } @Override public void run() { NettyMessage msg = processRequest(ctx, request); if (msg != null) { ctx.writeAndFlush(msg); } } } private NettyMessage processRequest(ChannelHandlerContext ctx, NettyMessage request) { NettyMessage msg = null; if(request.getHeader().getMsgId()<2000 ){ try { AbstractGameHandler handler = SpringBeanUtil.getBean("Handler" + request.getHeader().getMsgId()); if (handler == null) { logger.error("命令 {} 对应的处理器不存在", request.getHeader().getMsgId()); } else { msg = handler.execute(ctx, request); } } catch (Exception e) { logger.error("处理业务逻辑出错" + e.getMessage()); } } else{ /*String channelId=new String((byte[])request.getBody()); GameChannel gameChannel=ChannelMapUtil.getGameChannel(channelId); SocketChannel channel=gameChannel.getSocketChannel(); channel.writeAndFlush(request);*/ } return msg; } }
public interface AbstractGameHandler { public NettyMessage execute(ChannelHandlerContext ctx,NettyMessage msg) throws Exception; }
@Service("Handler1001") public class Handler1001 implements AbstractGameHandler { private static final Logger logger = LoggerFactory.getLogger(Handler1001.class); @Override public NettyMessage execute(ChannelHandlerContext ctx, NettyMessage msg) throws Exception { AuForMsgRoutingServer auForMsgRoutingServer = SpringBeanUtil.getBean("auForMsgRoutingServer"); InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress(); String address = insocket.getAddress().getHostAddress(); try{ auForMsgRoutingServer.saveOrLogin(msg, ctx.channel().id().hashCode(),address); }catch (Exception e){ logger.error("发生错误",e); LoginAndRegister.PBLoginAndRegisterResponse.Builder builder = LoginAndRegister.PBLoginAndRegisterResponse .newBuilder(); NettyMessage response = new NettyMessage(); response.setHeader(msg.getHeader()); builder.setIsSuccess(false); builder.setCauseOfFailure(e.getMessage()); response.setBody(builder); ctx.writeAndFlush(response); } return null; } }