使用Netty進(jìn)行編解碼的操作過程詳解
前言
何為編解碼,通俗的來說,我們需要將一串文本信息從A發(fā)送到B并且將這段文本進(jìn)行加工處理,如:A將信息文本信息編碼為2進(jìn)制信息進(jìn)行傳輸。B接受到的消息是一串2進(jìn)制信息,需要將其解碼為文本信息才能正常進(jìn)行處理。
上章我們介紹的Netty如何解決拆包和粘包問題,就是運用了解碼的這一功能。
java默認(rèn)的序列化機(jī)制
使用Netty大多是java程序猿,我們基于一切都是對象的原則,經(jīng)常會將對象進(jìn)行網(wǎng)絡(luò)傳輸,那么對于序列化操作肯定大家都是非常熟悉的。
一個對象是不能直接進(jìn)行網(wǎng)絡(luò)I/O傳輸?shù)模琷dk默認(rèn)是將對象轉(zhuǎn)換為可存儲的字節(jié)數(shù)組來進(jìn)行網(wǎng)絡(luò)操作?;贘DK默認(rèn)的序列化機(jī)制可以避免操作底層的字節(jié)數(shù)組,從而提升開發(fā)效率。
jdk默認(rèn)的序列化機(jī)制雖然能給程序猿帶來極大的方便,但是它也帶來了許多問題:
- 無法跨語言。
- 序列化后的碼流太大,會給網(wǎng)絡(luò)傳輸帶來極大的開銷。
- 序列化的性能太低,對于高性能的網(wǎng)絡(luò)架構(gòu)是極其不友好的。
主流的編解碼框架
- Google的Protobuf。
- Facebok的Thrift。
- Jboss Marshalling
- MessagePack
這幾類編解碼框架都有各自的特點,有興趣的童鞋可以自己對其進(jìn)行研究。
我們這里主要對MessagePack進(jìn)行講解。
MessagePack簡介
MessagePack是一個高效的二進(jìn)制序列化框架,它像JSON一樣支持不同的語言間的數(shù)據(jù)交換,并且它的性能更快,序列化之后的碼流也更小。
它的特點如下:
- 編解碼高效,性能高
- 序列化之后的碼流小,利于網(wǎng)絡(luò)傳輸或存儲
- 支持跨語言
MessagePack Java Api的使用
首先導(dǎo)包
<!-- https://mvnrepository.com/artifact/org.msgpack/msgpack --> <dependency> <groupId>org.msgpack</groupId> <artifactId>msgpack</artifactId> <version>0.6.12</version> </dependency>
使用API進(jìn)行編碼和解碼
List<String> nameList = new ArrayList<String>();
nameList.add("Tom");
nameList.add("Jack");
MessagePack messagePack = new MessagePack();
//開始序列化
byte[] raw = messagePack.write(nameList);
//使用MessagePack的模版,來接序列化后的字節(jié)數(shù)組轉(zhuǎn)換為List
List<String> 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
編碼器的實現(xiàn)
public class MsgpackEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
MessagePack msgpack = new MessagePack();
//使用MessagePack對要發(fā)送的數(shù)據(jù)進(jìn)行序列化
byte[] raw = msgpack.write(msg);
out.writeBytes(raw);
}
}
解碼器的實現(xiàn)
public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
//從msg中獲取需要解碼的byte數(shù)組
final int length = msg.readableBytes();
byte[] b = new byte[length];
msg.getBytes(msg.readerIndex(), b,0,length);
//使用MessagePack的read方法將其反序列化成Object對象,并加入到解碼列表out中
MessagePack msgpack = new MessagePack();
out.add(msgpack.read(b));
}
}
實現(xiàn)該編碼器和解碼器的Netty服務(wù)端
public class NettyServer {
public void bind(int port) throws Exception {
EventLoopGroup bossGruop = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGruop, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// TODO Auto-generated method stub
socketChannel.pipeline()
//添加支持粘包、拆包解碼器,意義:從頭兩個字節(jié)解析出數(shù)據(jù)的長度,并且長度不超過1024個字節(jié)
.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1024, 0, 2,0,2))
//反序列化解碼器
.addLast("msgpack decoder",new MsgpackDecoder())
//添加支持粘包、拆包編碼器,發(fā)送的每個數(shù)據(jù)都在頭部增加兩個字節(jié)表消息長度
.addLast("frameEncoder",new LengthFieldPrepender(2))
//序列化編碼器
.addLast("msgpack encoder",new MsgpackEncoder()
//后續(xù)自己的業(yè)務(wù)邏輯
.addLast(new ServerHandler());
}
});
try {
ChannelFuture future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGruop.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
實現(xiàn)該編碼器和解碼器的Netty客戶端
public class NettyClient {
private void bind(int port, String host) {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// TODO Auto-generated method stub
socketChannel.pipeline()
.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2))
.addLast("msgpack decoder", new MsgpackDecoder())
.addLast("frameEncoder", new LengthFieldPrepender(2))
.addLast("msgpack encoder", new MsgpackEncoder())
.addLast(new ClientHandler());
}
});
try {
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
可以看出客戶端的代碼與服務(wù)端基本相同,所以啊,如果能熟練掌握Netty,今后在自己的項目中運用上定制化編解碼的傳輸,將會是一件十分簡單的活路。
總結(jié)
無論是之前解決粘包拆包問題,還是這里的使用序列化框架來進(jìn)行編解碼。我相信讀者學(xué)習(xí)到這里,對于Netty的使用都有了較為全面的了解。其實Netty幫我們解決了很多底層棘手問題,如客戶端斷連、句柄泄漏和消息丟失等等。所以我們才能十分簡單開發(fā)出一個穩(wěn)定的網(wǎng)絡(luò)通訊項目。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java實現(xiàn)圖的鄰接表存儲結(jié)構(gòu)的兩種方式及實例應(yīng)用詳解
這篇文章主要介紹了java實現(xiàn)圖的鄰接表存儲結(jié)構(gòu)的兩種方式及實例應(yīng)用詳解,鄰接表構(gòu)建圖是必須需要一個Graph對象,也就是圖對象!該對象包含屬性有:頂點數(shù)、邊數(shù)以及圖的頂點集合,需要的朋友可以參考下2019-06-06
java算法題解LeetCode35復(fù)雜鏈表的復(fù)制實例
這篇文章主要為大家介紹了java算法題解LeetCode35復(fù)雜鏈表的復(fù)制實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Java String.replace()方法"無效"的原因及解決方式
這篇文章主要介紹了Java String.replace()方法"無效"的原因及解決方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
springboot post接口接受json時,轉(zhuǎn)換為對象時,屬性都為null的解決
這篇文章主要介紹了springboot post接口接受json時,轉(zhuǎn)換為對象時,屬性都為null的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
java獲取文件擴(kuò)展名的方法小結(jié)【正則與字符串截取】
這篇文章主要介紹了java獲取文件擴(kuò)展名的方法,結(jié)合實例形式分析了使用正則與字符串截取兩種獲取擴(kuò)展名的操作技巧,需要的朋友可以參考下2017-01-01
Java中如何使用Gson將對象轉(zhuǎn)換為JSON字符串
這篇文章主要給大家介紹了關(guān)于Java中如何使用Gson將對象轉(zhuǎn)換為JSON字符串的相關(guān)資料,Gson是Google的一個開源項目,可以將Java對象轉(zhuǎn)換成JSON,也可能將JSON轉(zhuǎn)換成Java對象,需要的朋友可以參考下2023-11-11

