最近在使用netty和rabbitmq进行开发即时通讯,为什么我会考虑使用这两个呢?首先Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序,而rabbitmq可以实现一端的写入至订阅的队列,并且驱动订阅者消费队列的信息,以达到信息通讯的可靠,满足我的需求。
针对于我的坑,我简单地说下,本来我是打算制定自己的协议,然后客户端与服务器端都遵循相同的协议进行通讯。最重要的莫过于编码器与解码器,我继承了MessageToMessageCode这个类,形如myclass extends MessageToMessageCodec<ByteBuf,LuckMessage>,其中Message是我定义的规范
@ChannelHandler.Sharable
public class ProtoCodec extends MessageToMessageCodec<ByteBuf,LuckMessage> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, LuckMessage message, List<Object> list) throws Exception {
ByteBuf byteBuf= ByteBufAllocator.DEFAULT.buffer();
if(message.getContent()!=null){
byteBuf.writeInt(message.getLuckHeader().getVersion());
byteBuf.writeInt(message.getLuckHeader().getContentLength());
byteBuf.writeBytes(message.getLuckHeader().getSessionId().getBytes());
byteBuf.writeBytes(message.getContent());
}
channelHandlerContext.writeAndFlush(byteBuf);
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
LuckMessage message=new LuckMessage();
LuckHeader header=new LuckHeader();
header.setVersion(byteBuf.readInt());
header.setContentLength(byteBuf.readInt());
int length=header.getContentLength();
byte[] bb = new byte[length];
byteBuf.readBytes(bb);
header.setSessionId(new String(bb));
byte[] b=new byte[byteBuf.readableBytes()];
byteBuf.readBytes(b);
message.setContent(b);
message.setLuckHeader(header);
list.add(message);
}
}
这里很协议LuckMessage说到底就是类,然后我直接让它在我的解析器里面解析出来,就没必要Handler器中去解析
ByteBuf了,只要单纯地获取LuckMessage就搞定了,然而照着这样去做的时候会发现,当字节数小于1024的时候,解析很正常;但当字节数大于1024的时候为啥会有异常呢?我解析得好好的。别急,我们测试下,在解析器里我加了测试语句,然后发现居然会重复地打印,是由于如果字节数太长会重复地读取相同的字节吗?经过网上的资料查询以及测试结果表明,就是如此。有个做法就是在解析器里面读完一次然后拼接起来,不过netty官方有提供一个解析器解决了这个问题LengthFieldBasedFrameDecoder解决问题
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeInt("jpg".length());
byteBuf.writeBytes("jpg".getBytes());
byteBuf.writeInt(bt.length);
byteBuf.writeBytes(bt);
ch.writeAndFlush(byteBuf);
然后在handler解析的时候
public class HelloServerHandler extends SimpleChannelInboundHandler<Object> {
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
}
channels.add(ctx.channel());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
}
channels.remove(ctx.channel());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf)msg;
int type=byteBuf.readInt();
byte[] b=new byte[type];
byteBuf.readBytes(b);
String st=new String(b);
System.out.println("类型:"+st);
int length=byteBuf.readInt();
System.out.println("长度:"+length);
byte[] bt=new byte[length];
byteBuf.readBytes(bt);
FileAndByte.getFile(bt,"c:","456.jpg");
// 收到消息直接打印输出
System.out.println(ctx.channel().remoteAddress() + " Say : " + msg);
// 返回客户端消息 - 我已经接收到了你的消息
ctx.writeAndFlush("Received your message !\n");
}
/*
*
* 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候)
*
* channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述
* */
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !");
ctx.writeAndFlush( "Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n");
super.channelActive(ctx);
}
}
看完channelRead0中的解析,相信你已经可以解决问题了,然后不要忘记了对于ChannelInitializer中添加 LengthFieldBasedFrameDecoder的解析器,设置参数去百度下解决。
以下参考的网站http://www.jianshu.com/p/8fcca15ea0b9
以上是关于netty的大致介绍,哪里写的出错或者不好,请大牛指正。大家多多交流,下次将交流下rabbitmq以及在spring中使用rabbitmq