1. 程式人生 > >netty解碼器LineBaseFrameDecoder、StringDecoder

netty解碼器LineBaseFrameDecoder、StringDecoder

1.介紹

什麼是粘包、拆包

TCP是個流協議,傳送的資料是連成一片的沒有分界線。TCP底層會根據緩衝區的實際情況進行包的劃分,所以在業務上面認為一個完整的包被TCP拆分後傳送即拆包,也可能將多個小的資料包整合成一個大的資料包傳送即粘包問題。

產生粘包、拆包問題的原因

應用程式write寫入的位元組大小大於socket傳送緩衝區的大小

進行了MSS大小的TCP分段

乙太網幀的payload大於MTU進行ip分片

解決方案

由於底層tcp不理解上層業務,所以底層無法保證不進行報的拆分和重組的只能在上層的應用協議棧設計解決方式。業界註解方案如下:

訊息定長

包尾增加回車換行符進行分割

訊息分為訊息體和訊息頭

更復雜的應用層協議

2.netty實現

netty提供了半包解碼器來解決粘包和拆包問題

(1)粘包問題演示

server

package com.tyf.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

@Sharable
public class TimeServerHandler extends ChannelHandlerAdapter {


	private int count;
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//獲取request的msg
		ByteBuf buf = (ByteBuf) msg;
		byte [] bytes = new byte[buf.readableBytes()];
		buf.readBytes(bytes);
		//去掉後面的回車換行符留下訊息體並轉換成string
		String body = new String(bytes, "UTF-8").
						  substring(0, bytes.length-System.getProperty("line.separator").length());
		System.out.println("客戶端msg訊息體:"+body+" , 收到訊息次數:"+ ++count);

	}


	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		super.exceptionCaught(ctx, cause);
	}

	
	//建立起步程式
	public static void  main(String [] argsStrings) throws Exception {
		//配置服務端NIO執行緒組(boss執行緒、worker執行緒)
		EventLoopGroup bGroup = new NioEventLoopGroup();
		EventLoopGroup wGroup = new NioEventLoopGroup();
		//建立啟動輔助類
		ServerBootstrap bootstrap = new ServerBootstrap();
		bootstrap.group(bGroup, wGroup)
				 .channel(NioServerSocketChannel.class)
				 .option(ChannelOption.SO_BACKLOG, 1024)
				 .childHandler(new TimeServerHandler());//繫結handker
		
		try {
			//監聽本地埠,同步等待監聽結果
			ChannelFuture future = bootstrap.bind(11111).sync();
			//等待服務端監聽埠關閉,優雅退出
			future.channel().closeFuture().sync();
		}finally {
			bGroup.shutdownGracefully();
			wGroup.shutdownGracefully();
		}
		
		
				 
	}
	
}

每收到一次訊息就列印一下第幾次收到訊息
 

client

package com.tyf.netty;


import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

public class TimeClientHandler extends ChannelHandlerAdapter {
	
		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			ByteBuf mes = null;
			//建立請求體,最後是回車換行符
			byte[] req = new String("client message"+System.getProperty("line.separator")).getBytes();
			//連續50次傳送資料
			for(int i=0;i<15;i++){
				mes = Unpooled.buffer(req.length);
				mes.writeBytes(req);
				ctx.writeAndFlush(mes);
			}
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
			super.exceptionCaught(ctx, cause);
		}

		//建立起步程式
		public static void  main(String [] argsStrings) throws Exception {
			//配置客戶端端NIO執行緒組
			EventLoopGroup bGroup = new NioEventLoopGroup();
			//建立客戶端啟動輔助類
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(bGroup).
					  channel(NioSocketChannel.class).
					  option(ChannelOption.TCP_NODELAY, true).
					  handler(new TimeClientHandler());//設定handler
			
			//發起非同步連線
			ChannelFuture future = bootstrap.connect("127.0.0.1", 11111).sync();
			try {
				//等待客戶端鏈路關閉
				future.channel().closeFuture().sync();
				} finally {
				//優雅退出,釋放資源
				bGroup.shutdownGracefully();
			}
			
			 
		}
	
}

迴圈15次傳送訊息,檢視server端輸出:


實際上客戶端傳送的15條資料被tcp整合粘包之後成一個數據包傳送,server顯示是一次性收到全部訊息。很顯然發生了資料粘包

(2)粘包問題解決

netty預設提供了多種解碼器解決半包讀寫問題。LinBasedFrameDecoder就是其中一種

server

package com.tyf.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

@Sharable
public class TimeServerHandler extends ChannelHandlerAdapter {


	private int count;
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//直接獲取request的msg
		String body = (String) msg;
		System.out.println("客戶端msg訊息體:"+body+" , 收到訊息次數:"+ ++count);

	}


	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		super.exceptionCaught(ctx, cause);
	}

	
	//建立起步程式
	public static void  main(String [] argsStrings) throws Exception {
		//配置服務端NIO執行緒組(boss執行緒、worker執行緒)
		EventLoopGroup bGroup = new NioEventLoopGroup();
		EventLoopGroup wGroup = new NioEventLoopGroup();
		//建立啟動輔助類
		ServerBootstrap bootstrap = new ServerBootstrap();
		bootstrap.group(bGroup, wGroup)
				 .channel(NioServerSocketChannel.class)
				 .option(ChannelOption.SO_BACKLOG, 1024)
				 .childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel channel) throws Exception {
						//新增netty解碼器,和使用者handler
						channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
						channel.pipeline().addLast(new StringDecoder());
						channel.pipeline().addLast(new TimeServerHandler());
					}
					 
				});
		
		try {
			//監聽本地埠,同步等待監聽結果
			ChannelFuture future = bootstrap.bind(11111).sync();
			//等待服務端監聽埠關閉,優雅退出
			future.channel().closeFuture().sync();
		}finally {
			bGroup.shutdownGracefully();
			wGroup.shutdownGracefully();
		}
		
		
				 
	}
	
}


新增兩個解碼器。其中linebaseframedecoder是以換行符為結束標記的解碼器

client

package com.tyf.netty;


import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeClientHandler extends ChannelHandlerAdapter {
	
		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			ByteBuf mes = null;
			//建立請求體,最後是回車換行符
			byte[] req = ("client message"+System.getProperty("line.separator")).getBytes();
			//連續50次傳送資料
			for(int i=0;i<15;i++){
				mes = Unpooled.buffer(req.length);
				mes.writeBytes(req);
				ctx.writeAndFlush(mes);
			}
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
			super.exceptionCaught(ctx, cause);
		}

		//建立起步程式
		public static void  main(String [] argsStrings) throws Exception {
			//配置客戶端端NIO執行緒組
			EventLoopGroup bGroup = new NioEventLoopGroup();
			//建立客戶端啟動輔助類
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(bGroup).
					  channel(NioSocketChannel.class).
					  option(ChannelOption.TCP_NODELAY, true).
					  handler(new ChannelInitializer<SocketChannel>() {
							@Override
							protected void initChannel(SocketChannel channel) throws Exception {
								//新增netty解碼器,和使用者handler
								channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
								channel.pipeline().addLast(new StringDecoder());
								channel.pipeline().addLast(new TimeClientHandler());
							}
							 
						});
			
			//發起非同步連線
			ChannelFuture future = bootstrap.connect("127.0.0.1", 11111).sync();
			try {
				//等待客戶端鏈路關閉
				future.channel().closeFuture().sync();
				} finally {
				//優雅退出,釋放資源
				bGroup.shutdownGracefully();
			}
			
			 
		}
	
}

也是新增兩個解碼器

檢視server端結果:

3.LineBaseFrameDecoder解碼器

LineBaseFrameDecoder是以換行符為結束標記的解碼器

StringDecoder解碼器就是將物件轉成字串,可以看到上面msg物件直接轉成string物件

相關推薦

netty解碼LineBaseFrameDecoderStringDecoder

1.介紹 什麼是粘包、拆包: TCP是個流協議,傳送的資料是連成一片的沒有分界線。TCP底層會根據緩衝區的實際情況進行包的劃分,所以在業務上面認為一個完整的包被TCP拆分後傳送即拆包,也可能將多個小的資料包整合成一個大的資料包傳送即粘包問題。 產生粘包、拆包問題的原因:

Netty 解碼抽象父類 ByteToMessageDecoder 源碼解析

cep 轉換 cached 運算 ont 直接 小細節 message TP 前言 Netty 的解碼器有很多種,比如基於長度的,基於分割符的,私有協議的。但是,總體的思路都是一致的。 拆包思路:當數據滿足了 解碼條件時,將其拆開。放到數組。然後發送到業務 handler

FFmpeg(8)-打開和配置音視頻解碼(avcodec_find_decoder()avcodec_alloc_context3())

解碼器 register oge 模塊 gist 函數 nbsp ble video 一.avcodec_find_decoder 獲取解碼器。在使用之前必須保證所用到的解碼器已經註冊,最簡單的就是調用avcodec_register_all() 函數,就像之前註冊解封裝

netty解碼詳解(小白也能看懂!)

什麼是編解碼器?   首先,我們回顧一下netty的元件設計:Netty的主要元件有Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe等。 ChannelHandler   ChannelHandler充當了處理入站和出站

FFmpeg(8)-開啟和配置音視訊解碼(avcodec_find_decoder()avcodec_alloc_context3())

一.avcodec_find_decoder 獲取解碼器。在使用之前必須保證所用到的解碼器已經註冊,最簡單的就是呼叫avcodec_register_all() 函式,就像之前註冊解封裝器的時候,也要註冊一下。。 AVCodec *avcodec_find_decoder(enum AVCodecID i

Netty-解碼架構與常用解碼

任何資料型別想在網路中進行傳輸,都得經過編解碼轉換成位元組流 在netty中,服務端和客戶端進行通訊的其實是下面這樣的 程式 ---編碼--> 網路 網路 ---解碼--> 程式 對應服務端: 入站資料, 經過解碼器解碼後給後續的handler使用 出站資料, 結果編碼器編碼成位元組流給在網路上

netty自定義簡單解碼處理粘包拆包

.so ack 繼承 記錄 line sync ann write 工程 tcp連接的粘包、拆包發生在長連接中,先了解一下長、短連接的概念 短連接:請求/響應之後,關閉已經建立的tcp連接,下次請求再建立新的連接 長連接:請求/響應之後,不關閉已經建立的tcp連接,多次請求

Deep Learning基礎--線性解碼卷積池化

sparse pca 過程 條件 連接 移動 .cn 計算過程 htm 本文主要是學習下Linear Decoder已經在大圖片中經常采用的技術convolution和pooling,分別參考網頁http://deeplearning.stanford.edu/wiki/i

Netty中LineBasedFrameDecoder解碼使用與分析:解決TCP粘包問題

ring public xpl cep ctx new 綁定端口 註意 相關 [toc] Netty中LineBasedFrameDecoder解碼器使用與分析:解決TCP粘包問題 上一篇文章《Netty中TCP粘包問題代碼示例與分析》演示了使用了時間服務器的例子演示了T

Netty中分隔符解碼代碼示例與分析

rac ride 通用 否則 eventloop connect href throw java [toc] Netty中分隔符解碼器代碼示例與分析 通過特別解碼器的使用,可以解決Netty中TCP的粘包問題,上一篇《Netty中LineBasedFrameDecoder

Netty中定長解碼的使用

ble 解碼器 erb cau option bootstra cef 成功 ios [toc] Netty中定長解碼器的使用 有了前面的基礎,定長解碼器的使用相對就比較簡單了,所以這裏只使用服務端的代碼,測試時,用telnet作為客戶客戶端,數據只作單向的發送,即從客戶

Netty入門(六)Decoder(解碼

err 器) repl 方法 pub HA override 異常 oid   Netty 提供了豐富的解碼器抽象基類,主要分為兩類: 解碼字節到消息(ByteToMessageDecoder 和 ReplayingDecoder) 解碼消息到消息(MessageTo

netty解碼和粘包拆包

exception med 增加 這就是 就會 邊界 ali 空格 封裝 Tcp是一個流的協議,一個完整的包可能會被Tcp拆成多個包進行發送,也可能把一個小的包封裝成一個大的數據包發送,這就是所謂的粘包和拆包問題 粘包、拆包出現的原因: 在流傳輸中出現,UDP不會出現粘包,

javacpp-FFmpeg系列之2:通用拉流解碼,支持視頻拉流解碼並轉換為YUVBGR24或RGB24等圖像像素數據

tope sca 封裝 ams 定義 throw tco 如何使用 都是 javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:視頻拉流解碼成YUVJ420P,並保存為jpg圖片 javacpp-FFmpeg系列之2:通用拉流解碼器,支持視頻拉流解碼並

netty使用msgpack自定義編解碼實現序列化操作

匯入依賴 <dependency> <groupId>org.msgpack</groupId> <artifactId>msgpack</artifactId>

Netty(預置的ChannelHandler和編解碼)

通過SSL/TLS保護Netty應用程式     為了支援SSL/TLS,Java提供了javax.net.ssl包,它的SSLContext和SSLEngine類使得實現解密和加密相當簡單直接。Netty通過一個名為SslHandler的ChannelHand

Netty(編解碼框架)

    每個網路應用程式都必須定義如何解析在兩個節點之間來回傳輸的原始位元組,以及如何將其和目標應用程式的資料格式做相互轉換。這種轉換邏輯由編解碼器處理,編解碼器  由編碼器和解碼器組成,它們每種都可以將位元組流從一種格式轉換為另一種格式。  

Netty 整合 MessagePack 序列化框架 + LengthFieldBasedFrameDecoder 自定義解碼

環境準備及說明  如果是匯入二進位制開發包,則如下所示: 需要開發包的可以參考《 MessagePack 開發入門詳解》。 如果是 Maven 專案,則新增如下依賴: <!-- https://mvnrepository.com/artifact/

Netty學習之路(六)-分隔符和定長解碼的應用

之前已經使用了LineBasedFrameDecoder解決TCP粘包問題,現在再學兩種解決TCP粘包的方法。 DelimiterBasedFrameDecoder:可以自動完成以分隔符做結束標誌的訊息的解碼,分隔符自定義。 FixedLengthFrameDecoder:

netty自定義編碼解碼(粘包處理)

這裡的實現方式是:將訊息分為兩部分,也就是訊息頭和訊息尾,訊息頭中寫入要傳送資料的總長度,通常是在訊息頭的第一個欄位使用int值來標識傳送資料的長度。 首先我們寫一個Encoder,我們繼承自Me