netty5+protobuf

最近想使用netty和protobuf搞个游戏的协议层框架,找了好多文章,解码器分发这部分的例子很少,抽了时间设计了一个,大神来给提提意见。

服务器

package server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.util.logging.Level;
import java.util.logging.Logger;

import server.coder.SelfDecoder;
import server.coder.SelfEncoder;
import server.handler.ProtoBufServerHandler;

public class NettyServer {

    private static final int PORT = 1588;

    private static Logger logger = Logger.getLogger(NettyServer.class.getName());

    public void start(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup);
            b.channel(NioServerSocketChannel.class);
            b.childHandler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    //decoded
                    ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4));
                    ch.pipeline().addLast(new SelfDecoder());
                    //encoded
                    ch.pipeline().addLast(new SelfEncoder());
                    // 注册handler
                    ch.pipeline().addLast(new ProtoBufServerHandler());
                }
            });
            b.option(ChannelOption.SO_BACKLOG, 128);
            b.childOption(ChannelOption.SO_KEEPALIVE, true);
            //绑定端口 同步等待成功
            ChannelFuture f = b.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        logger.log(Level.INFO, "NettyServer start...");
        new NettyServer().start(PORT);
    }

}

解码器

package server.coder;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

import message.MessageVO;

/**
 * 消息解码
 * 
 */
public class SelfDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext paramChannelHandlerContext, ByteBuf in, List<Object> paramList) throws Exception {
        short header = in.readShort();
        short errorCode = in.readShort();
        int length = in.readInt();
        byte[] data = new byte[length];
        in.readBytes(data);

        MessageVO msgVo = new MessageVO();
        msgVo.setHeader(header);
        msgVo.setErrorCode(errorCode);
        msgVo.setBody(data);
        paramList.add(msgVo);
    }

}

package server.coder;

import message.MessageVO;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * 消息封装成字节流
 * 
 */
public class SelfEncoder extends MessageToByteEncoder<MessageVO> {

    @Override
    protected void encode(ChannelHandlerContext cc, MessageVO msgVo, ByteBuf out) throws Exception {

        out.writeShort(msgVo.getHeader());
        out.writeShort(msgVo.getErrorCode());
        out.writeInt(msgVo.getBody().length);
        out.writeBytes(msgVo.getBody());

    }

}

消息分发处理

package server.handler;

import handlers.Handler1000;
import handlers.Handler1001;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import message.MessageVO;
import util.ChannalUtil;

public class ProtoBufServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ChannalUtil.setCtx(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //需要放到单独的分发器中处理
        //不同类型处理器,应该在服务器启动的时候就加载好对应关系,可以存储在map中
        MessageVO msgVo = (MessageVO) msg;
        switch (msgVo.getHeader()) {
            case 1000: {
                new Handler1000().handleMsg(ctx, msgVo);
                break;
            }
            case 1001: {
                new Handler1001().handleMsg(ctx, msgVo);
                break;
            }
            default:
                System.err.println("没有找到消息处理器!!");
                break;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

}

处理器抽象类

package handlers;

import io.netty.channel.ChannelHandlerContext;
import message.MessageVO;

public abstract class AbstractHandler {

    public void handleMsg(ChannelHandlerContext ctx, MessageVO messageVo) {
        try {
            Object object = handle(messageVo);
            if (object instanceof byte[]) {
                messageVo.setBody((byte[]) object);
                ctx.channel().writeAndFlush(messageVo);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理消息
     * 
     * @param messageVo
     */
    public abstract Object handle(MessageVO messageVo) throws Exception;

}

处理器实例

package handlers;

import com.google.protobuf.InvalidProtocolBufferException;

import proto.Msg1000;
import message.MessageVO;

public class Handler1000 extends AbstractHandler {

    @Override
    public Object handle(MessageVO messageVo) throws InvalidProtocolBufferException {
        Msg1000.Receive receiveMsg = Msg1000.Receive.parseFrom(messageVo.getBody());

        System.err.println("1000消息接收成功,我要返回消息了----" + receiveMsg.getUser() + " " + receiveMsg.getPswd());

        Msg1000.Send.Builder sendMsg = Msg1000.Send.newBuilder();
        sendMsg.setUser(receiveMsg.getUser());
        sendMsg.setPswd(receiveMsg.getPswd());
        return sendMsg.build().toByteArray();
    }

}

自定义消息封装类

package message;

import com.google.protobuf.MessageLite;

public class MessageVO {

    private short header;
    private short errorCode;
    private byte[] body;

    public MessageVO() {
    }

    public MessageVO(short header, MessageLite msg) {
        this.header = header;
        this.body = msg.toByteArray();
    }

    public short getHeader() {
        return header;
    }

    public void setHeader(short header) {
        this.header = header;
    }

    public short getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(short errorCode) {
        this.errorCode = errorCode;
    }

    public byte[] getBody() {
        return body;
    }

    public void setBody(byte[] body) {
        this.body = body;
    }

}

下面就是客户端了

package client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import server.coder.SelfDecoder;
import server.coder.SelfEncoder;

public class NettyClient {

    private static final int PORT = 1588;
    private static final String HOST = "127.0.0.1";

    public void connect(String host, int port) throws Exception {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    //decoded
                    ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4));
                    ch.pipeline().addLast(new SelfDecoder());
                    //encoded
                    ch.pipeline().addLast(new SelfEncoder());
                    // 注册handler
                    ch.pipeline().addLast(new ProtoBufClientHandler());
                }
            });
            ChannelFuture f = b.connect(host, port).sync();
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new NettyClient().connect(HOST, PORT);
    }
}
package client;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

import message.MessageVO;
import proto.Msg1000;
import proto.Msg1001;

public class ProtoBufClientHandler extends ChannelHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Msg1000.Receive request = Msg1000.Receive.newBuilder().setUser("010203").setPswd("abcde").build();
        Msg1001.Receive request1 = Msg1001.Receive.newBuilder().setFriendId(11111).build();
        ctx.writeAndFlush(new MessageVO((short) 1000, request));
        ctx.writeAndFlush(new MessageVO((short) 1001, request1));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.err.println("接收到服务器的消息了:" + ((MessageVO) msg).getHeader());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

}

之前实现了客户端的handler里面的read方法,导致客户端接收消息在read方法中,不要重写read方法就可以了,重写channelRead方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值