Netty编解码过程代码

/**
 *消息头
 * 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;
    }


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值