最近想使用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方法。