首页 > 代码库 > Netty Protobuf C# 通信
Netty Protobuf C# 通信
2、简介
http://netty.io/ netty 官方
http://download.csdn.net/detail/zeus_9i/7648115 【netty in action 英文版】
https://code.google.com/p/protobuf/ 什么是 protobuf 这里略过
http://protobuf-dt.googlecode.com/git/update-site/ proto 文件 eclipse 编辑器很方便,但是不一定能打开,天朝给强了。
3、关键词
协议格式
协议格式一般都是 TLV(type, length, value),但是这篇文章中是: LTV (length, type, value) 历史遗留问题,不影响通信。
Decoder 把二进制数据变成对象的过程
Encoder 把对象变成二进制数据的过程
4、实现
怎么组织一个 ServerBootstrap 和 ClientBootstrap 这里就不详细说明了。
主要说两点:1、decoder、encoder,
2、协议分发,也就是decoder以后怎么把协议号匹配到对应的业务处理逻辑
核心代码清单
class MessageMappingManager 映射,存储协议号与 MessageLite对应关系,双向,id -> class, class -> id class GameMessageDecoder 解码,这里会使用 MessageMappingManager 中的对应关系,来寻找具体的proto解码类(MessageLite) class ProtobufCommonDecoder extends ProtobufDecoder 这个是使用了Netty 自带的Protobuf 数据体解码类,自己去看代码 class GameMessageEncoder 编码,同上,并且把数据最后整理成:lenght + type + value 的二进制格式
眼尖的人应该会发现,这里没有:ProtobufVarint32FrameDecoder 和 ProtobufVarint32LengthFiledPrepender 官方的Demo 里面都使用到了。这里我们不需要使用者两个类。
解码代码
public class ProtobufCommonDecoder extends ProtobufDecoder { public ProtobufCommonDecoder(MessageLite prototype) { super(prototype); } public MessageLite invokeDecode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { return (MessageLite) decode(ctx, channel, msg); } } /** * 协议ID映射管理 * 自动生成文件,切勿修改 */ public class MessageMappingManager { /** msgId <-> MessageLite Req请求映射 */ private Map<Integer, MessageLite> idClazzMap; /** MessageLiteClass <--> msgId Resp响应映射 */ private Map<Class<? extends MessageLite>, Integer> clazzIdMap; public void init() { idClazzMap = new HashMap<Integer, MessageLite>(); clazzIdMap = new HashMap<Class<? extends MessageLite>, Integer>(); idClazzMap.put(2, ErrorNoticeResp.getDefaultInstance()); clazzIdMap.put(ErrorNoticeResp.class, 2); idClazzMap.put(4, EnterSceneResp.getDefaultInstance()); clazzIdMap.put(EnterSceneResp.class, 4); } public MessageLite getMessage(int messageId) { return idClazzMap.get(messageId); } public int getMessageId(Class<?> clazz) { return clazzIdMap.get(clazz); } } public class GameMessageDecoder extends OneToOneDecoder { public static final Log LOG = Loggers.message; @Override protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (!(msg instanceof ChannelBuffer)) { return msg; } ChannelBuffer buf = (ChannelBuffer) msg; if( !buf.readable() ) { return null; } buf.markReaderIndex(); int messageId = buf.readShort(); if( LOG.isDebugEnabled() ) { LOG.debug("receive messageId: " + messageId); } MessageMappingManager mappingManager = Application.getBean(MessageMappingManager.class); MessageLite bodyLite = mappingManager.getMessage(messageId); if(bodyLite == null) { buf.resetReaderIndex(); LOG.error("Can't find proto message body decoder. messageId : " + messageId); return null; } ProtobufCommonDecoder decoder = new ProtobufCommonDecoder( mappingManager.getMessage(messageId) ); MessageLite dataLite = decoder.invokeDecode(ctx, channel, buf); GameMessage message = new GameMessage(); message.setId(messageId); message.setMessage(dataLite); return message; } }
GameMessage 是自定义对象,里面存储messageId + MessageLite,到这里就可以解码出消息对象了
注意:网络传输使用的 大端字节学,Java本身默认就是大端所以不需要处理,C#默认小端,所以C#发往Java的数据需要做处理