使用Netty进行编解码的操作过程详解
前言
何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输。B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常进行处理。
上章我们介绍的Netty如何解决拆包和粘包问题,就是运用了解码的这一功能。
java默认的序列化机制
使用Netty大多是java程序猿,我们基于一切都是对象的原则,经常会将对象进行网络传输,那么对于序列化操作肯定大家都是非常熟悉的。
一个对象是不能直接进行网络I/O传输的,jdk默认是将对象转换为可存储的字节数组来进行网络操作。基于JDK默认的序列化机制可以避免操作底层的字节数组,从而提升开发效率。
jdk默认的序列化机制虽然能给程序猿带来极大的方便,但是它也带来了许多问题:
- 无法跨语言。
- 序列化后的码流太大,会给网络传输带来极大的开销。
- 序列化的性能太低,对于高性能的网络架构是极其不友好的。
主流的编解码框架
- Google的Protobuf。
- Facebok的Thrift。
- JbossMarshalling
- MessagePack
这几类编解码框架都有各自的特点,有兴趣的童鞋可以自己对其进行研究。
我们这里主要对MessagePack进行讲解。
MessagePack简介
MessagePack是一个高效的二进制序列化框架,它像JSON一样支持不同的语言间的数据交换,并且它的性能更快,序列化之后的码流也更小。
它的特点如下:
- 编解码高效,性能高
- 序列化之后的码流小,利于网络传输或存储
- 支持跨语言
MessagePackJavaApi的使用
首先导包
org.msgpack msgpack 0.6.12
使用API进行编码和解码
ListnameList=newArrayList (); nameList.add("Tom"); nameList.add("Jack"); MessagePackmessagePack=newMessagePack(); //开始序列化 byte[]raw=messagePack.write(nameList); //使用MessagePack的模版,来接序列化后的字节数组转换为List List deNameList=messagePack.read(raw,Templates.tList(Templates.TString)); System.out.println(deNameList.get(0)); System.out.println(deNameList.get(1)); System.out.println(deNameList.get(2));
Netty中如何使用MessagePack
编码器的实现
publicclassMsgpackEncoderextendsMessageToByteEncoder{ @Override protectedvoidencode(ChannelHandlerContextctx,Objectmsg,ByteBufout)throwsException{ MessagePackmsgpack=newMessagePack(); //使用MessagePack对要发送的数据进行序列化 byte[]raw=msgpack.write(msg); out.writeBytes(raw); } }
解码器的实现
publicclassMsgpackDecoderextendsMessageToMessageDecoder{ @Override protectedvoiddecode(ChannelHandlerContextctx,ByteBufmsg,List
实现该编码器和解码器的Netty服务端
publicclassNettyServer{ publicvoidbind(intport)throwsException{ EventLoopGroupbossGruop=newNioEventLoopGroup(); EventLoopGroupworkGroup=newNioEventLoopGroup(); ServerBootstrapbootstrap=newServerBootstrap(); bootstrap.group(bossGruop,workGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,1024) .childHandler(newChannelInitializer(){ @Override protectedvoidinitChannel(SocketChannelsocketChannel)throwsException{ //TODOAuto-generatedmethodstub socketChannel.pipeline() //添加支持粘包、拆包解码器,意义:从头两个字节解析出数据的长度,并且长度不超过1024个字节 .addLast("frameDecoder",newLengthFieldBasedFrameDecoder(1024,0,2,0,2)) //反序列化解码器 .addLast("msgpackdecoder",newMsgpackDecoder()) //添加支持粘包、拆包编码器,发送的每个数据都在头部增加两个字节表消息长度 .addLast("frameEncoder",newLengthFieldPrepender(2)) //序列化编码器 .addLast("msgpackencoder",newMsgpackEncoder() //后续自己的业务逻辑 .addLast(newServerHandler()); } }); try{ ChannelFuturefuture=bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); }catch(Exceptione){ e.printStackTrace(); }finally{ bossGruop.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
实现该编码器和解码器的Netty客户端
publicclassNettyClient{ privatevoidbind(intport,Stringhost){ EventLoopGroupgroup=newNioEventLoopGroup(); Bootstrapb=newBootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY,true) .handler(newChannelInitializer(){ @Override protectedvoidinitChannel(SocketChannelsocketChannel)throwsException{ //TODOAuto-generatedmethodstub socketChannel.pipeline() .addLast("frameDecoder",newLengthFieldBasedFrameDecoder(1024,0,2,0,2)) .addLast("msgpackdecoder",newMsgpackDecoder()) .addLast("frameEncoder",newLengthFieldPrepender(2)) .addLast("msgpackencoder",newMsgpackEncoder()) .addLast(newClientHandler()); } }); try{ ChannelFuturef=b.connect(host,port).sync(); f.channel().closeFuture().sync(); }catch(Exceptione){ e.printStackTrace(); }finally{ group.shutdownGracefully(); } } }
可以看出客户端的代码与服务端基本相同,所以啊,如果能熟练掌握Netty,今后在自己的项目中运用上定制化编解码的传输,将会是一件十分简单的活路。
总结
无论是之前解决粘包拆包问题,还是这里的使用序列化框架来进行编解码。我相信读者学习到这里,对于Netty的使用都有了较为全面的了解。其实Netty帮我们解决了很多底层棘手问题,如客户端断连、句柄泄漏和消息丢失等等。所以我们才能十分简单开发出一个稳定的网络通讯项目。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。