Netty中的DelimiterBasedFrameDecoder使用方法詳解
1 DelimiterBasedFrameDecoder介紹
前面介紹了LineBasedFrameDecoder,其以換行符\n或者\(yùn)r\n作為依據(jù),遇到\n或者\(yùn)r\n都認(rèn)為是一條完整的消息。
而DelimiterBasedFrameDecoder與LineBasedFrameDecoder類似,只不過(guò)更加通用,允許我們指定任意特殊字符作為分隔符。我們還可以同時(shí)指定多個(gè)分隔符,如果在請(qǐng)求中發(fā)的確有多個(gè)分隔符,將會(huì)選擇內(nèi)容最短的一個(gè)分隔符作為依據(jù)。 例如:
+--------------+ | ABC\nDEF\r\n | +--------------+
如果我們指定分隔符為\n,那么將會(huì)解碼出來(lái)2個(gè)消息
+-----+-----+ | ABC | DEF | +-----+-----+
如果我們指定\r\n作為分隔符,那么只會(huì)解碼出來(lái)一條消息
+----------+ | ABC\nDEF | +----------+
DelimiterBasedFrameDecoder提供了多個(gè)構(gòu)造方法,最終調(diào)用的都是以下構(gòu)造方法:
public DelimiterBasedFrameDecoder(
int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters)其中:
maxLength:
表示一行最大的長(zhǎng)度,如果超過(guò)這個(gè)長(zhǎng)度依然沒(méi)有檢測(cè)到\n或者\(yùn)r\n,將會(huì)拋出TooLongFrameException
failFast:
與maxLength聯(lián)合使用,表示超過(guò)maxLength后,拋出TooLongFrameException的時(shí)機(jī)。如果為true,則超出maxLength后立即拋出TooLongFrameException,不繼續(xù)進(jìn)行解碼;如果為false,則等到完整的消息被解碼后,再拋出TooLongFrameException異常。
stripDelimiter:
解碼后的消息是否去除分隔符。
delimiters:
分隔符。我們需要先將分割符,寫(xiě)入到ByteBuf中,然后當(dāng)做參數(shù)傳入。
需要注意的是,netty并沒(méi)有提供一個(gè)DelimiterBasedFrameDecoder對(duì)應(yīng)的編碼器實(shí)現(xiàn)(筆者沒(méi)有找到),因此在發(fā)送端需要自行編碼,添加分隔符。
2 Base64編解碼
對(duì)于以特殊字符作為報(bào)文分割條件的協(xié)議的解碼器,如:LineBasedFrameDecoder、DelimiterBasedFrameDecoder。
都存在一個(gè)典型的問(wèn)題,如果發(fā)送數(shù)據(jù)當(dāng)中本身就包含了分隔符,怎么辦?如:我們要發(fā)送的內(nèi)容為:
hello1\nhello2\nhello3\n
我們需要把這個(gè)內(nèi)容整體當(dāng)做一個(gè)有效報(bào)文來(lái)處理,而不是拆分成hello1、hello2、hello3。一些同學(xué)可能想到那可以換其他的特殊字符,但是如果內(nèi)容中又包含你想指定的其他特殊字符怎么辦呢?
因此我們通常需要發(fā)送的內(nèi)容進(jìn)行base64編碼,base64中總共只包含了64個(gè)字符。
我們可以指定這64個(gè)字符之外的其他字符作為特殊分割字符;而接收端對(duì)應(yīng)的進(jìn)行base64解碼,得到對(duì)應(yīng)的原始的二進(jìn)制流,然后進(jìn)行處理。Netty提供了Base64Encoder/Base64Decoder來(lái)幫我們處理這個(gè)問(wèn)題。需要注意的是,只需要對(duì)內(nèi)容進(jìn)行base64編碼,分隔符不需要編碼。
3 DelimiterBasedFrameDecoder結(jié)合Base64編解碼案例
Server端:DelimiterBasedFrameDecoderServer
public class DelimiterBasedFrameDecoderServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ByteBuf delemiter= Unpooled.buffer();
delemiter.writeBytes("&".getBytes());
//先使用DelimiterBasedFrameDecoder解碼,以&作為分割符
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, true, true,delemiter));
//之后使用Base64Decoder對(duì)數(shù)據(jù)進(jìn)行解碼,得到報(bào)文的原始的二進(jìn)制流
ch.pipeline().addLast(new Base64Decoder());
//對(duì)請(qǐng)求報(bào)文進(jìn)行處理
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
ByteBuf packet = (ByteBuf) msg;
System.out.println(
new Date().toLocaleString() + ":" + packet.toString(Charset.defaultCharset()));
}
}
});
}
});
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(8080).sync(); // (7)
System.out.println("DelimiterBasedFrameDecoderServer Started on 8080...");
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}Server接受到的數(shù)據(jù),首先我們要去除分隔符,然后才能進(jìn)行base64解碼。因此首選我們添加了DelimiterBasedFrameDecoder根據(jù)&處理粘包辦包問(wèn)題,之后使用Base64Decoder進(jìn)行解碼,最后通過(guò)一個(gè)自定義ChannelInboundHandler打印請(qǐng)求的數(shù)據(jù)。client端:DelimiterBasedFrameDecoderClient
public class DelimiterBasedFrameDecoderClient {
public static void main(String[] args) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
//在于server建立連接后,即發(fā)送請(qǐng)求報(bào)文
public void channelActive(ChannelHandlerContext ctx) {
//先對(duì)要發(fā)送的原始內(nèi)容進(jìn)行base64編碼
ByteBuf content = Base64.encode(Unpooled.buffer().writeBytes("hello&tianshouzhi&".getBytes
()));
//之后添加分隔符
ByteBuf req = Unpooled.copiedBuffer(content);
req.writeBytes("&".getBytes());
ctx.writeAndFlush(req);
}
});
}
});
// Start the client.
ChannelFuture f = b.connect("127.0.0.1",8080).sync(); // (5)
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}在編寫(xiě)client端代碼時(shí)我們先對(duì)原始內(nèi)容進(jìn)行base64編碼,然后添加分割符之后進(jìn)行輸出。
需要注意,雖然Netty提供了Base64Encoder進(jìn)行編碼,這里并沒(méi)有直接使用,如果直接使用Base64Encoder,那么會(huì)對(duì)我們輸出的所有內(nèi)容進(jìn)行編碼,意味著分隔符也會(huì)被編碼,這顯然不符合我們的預(yù)期,所以這里直接使用了Netty提供了Base64工具類來(lái)處理。
如果一定要使用Base64Encoder,那么代碼需要進(jìn)行相應(yīng)的修改,自定義的ChannelInboundHandler只輸出原始內(nèi)容,之后通過(guò)Base64Encoder進(jìn)行編碼,然后需要額外再定義一個(gè)ChannelOutboundHandler添加分隔符,如:
ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if(msg instanceof ByteBuf){
((ByteBuf) msg).writeBytes("&".getBytes());
}
}
});
ch.pipeline().addLast(new Base64Encoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
//在于server建立連接后,即發(fā)送請(qǐng)求報(bào)文
public void channelActive(ChannelHandlerContext ctx) {
ByteBuf req = Unpooled.buffer().writeBytes("hello&tianshouzhi&".getBytes());
ctx.writeAndFlush(req);
}
});上述兩種方案效果是等價(jià)的。
當(dāng)我們先后啟動(dòng)server和client后,會(huì)看到server端控制臺(tái)輸出:
DelimiterBasedFrameDecoderServer Started on 8080...
2018-9-8 13:56:32:hello&tianshouzhi&
說(shuō)明經(jīng)過(guò)base64編碼后,我們的請(qǐng)求中可以包含分隔符作為內(nèi)容。
如果我們將base64編解碼相關(guān)邏輯去掉,你將會(huì)看到的輸出是:
DelimiterBasedFrameDecoderServer Started on 8080...
2018-9-8 14:13:31:hello
2018-9-8 14:13:31:tianshouzhi
也就是說(shuō),原始內(nèi)容也被誤分割了,解碼失敗,讀者可以自行驗(yàn)證。
到此這篇關(guān)于Netty中的DelimiterBasedFrameDecoder使用方法詳解的文章就介紹到這了,更多相關(guān)DelimiterBasedFrameDecoder使用方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot 創(chuàng)建對(duì)象常見(jiàn)的幾種方式小結(jié)
Spring Boot中創(chuàng)建對(duì)象的幾種常見(jiàn)方式包括使用@Component、@Service、@Repository或@Controller注解,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2024-11-11
Java程序開(kāi)發(fā)中abstract 和 interface的區(qū)別詳解
abstract class和interface在Java語(yǔ)言中都是用來(lái)進(jìn)行抽象類。但是兩者有什么區(qū)別呢,接下來(lái)小編給大家?guī)?lái)了abstract 和 interface的區(qū)別詳解,感興趣的朋友一起學(xué)習(xí)吧2016-06-06
Java開(kāi)發(fā)如何把數(shù)據(jù)庫(kù)里的未付款訂單改成已付款
這篇文章主要介紹了Java開(kāi)發(fā)如何把數(shù)據(jù)庫(kù)里的未付款訂單改成已付款,先介紹MD5算法,簡(jiǎn)單的來(lái)說(shuō),MD5能把任意大小、長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換成固定長(zhǎng)度的一串字符,實(shí)現(xiàn)思路非常簡(jiǎn)單需要的朋友可以參考下2022-11-11
Springboot中useGeneratedKeys用法小結(jié)
本文主要介紹了Springboot中useGeneratedKeys用法小結(jié),useGeneratedKeys?是 MyBatis 框架中的一個(gè)參數(shù),用于指定是否允許 JDBC 支持自動(dòng)生成主鍵,感興趣的可以了解一下2024-09-09
三分鐘帶你了解SpringBoot真正的啟動(dòng)引導(dǎo)類
這篇文章主要介紹了三分鐘帶你了解SpringBoot真正的啟動(dòng)引導(dǎo)類,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
解決使用@ManyToMany查詢數(shù)據(jù)時(shí)的死循環(huán)問(wèn)題
這篇文章主要介紹了解決使用@ManyToMany查詢數(shù)據(jù)時(shí)的死循環(huán)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12

