1. 程式人生 > >Netty之解決TCP粘包拆包(設定訊息邊界)

Netty之解決TCP粘包拆包(設定訊息邊界)

1、什麼是粘包/拆包

       一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,導致粘包的存在。處理粘包的唯一方法就是制定應用層的資料通訊協議,通過協議來規範現有接收的資料是否滿足訊息資料的需要。

2、解決辦法

     2.1、訊息定長,報文大小固定長度,不夠空格補全,傳送和接收方遵循相同的約定,這樣即使粘包了通過接收方程式設計實現獲取定長報文也能區分。

     2.2、包尾新增特殊分隔符,例如每條報文結束都添加回車換行符(例如FTP協議)或者指定特殊字元作為報文分隔符,接收方通過特殊分隔符切分報文區分。

     2.3、將訊息分為訊息頭和訊息體,訊息頭中包含表示資訊的總長度(或者訊息體長度)的欄位

3、Netty中提供了DelimiterBasedFrameDecoder解碼器可以幫助我們輕鬆實現第二種解決方案,包尾新增特殊分隔符。

4、加入DelimiterBasedFrameDecoder解碼器

      4.1 在服務端,加入DelimiterBasedFrameDecoder解碼器

      

      4.2 在客戶端,加入DelimiterBasedFrameDecoder解碼器

         

5、用到的分隔符的定義

public class ConstValue {
	public final static String FUHAO = "$_";
}
6、服務端的實現
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
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.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class Server {
	public void bind(int port) throws Exception {
		// 伺服器執行緒組 用於網路事件的處理 一個用於伺服器接收客戶端的連線
		// 另一個執行緒組用於處理SocketChannel的網路讀寫
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			// NIO伺服器端的輔助啟動類 降低伺服器開發難度
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			serverBootstrap.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)// 類似NIO中serverSocketChannel
					.option(ChannelOption.SO_BACKLOG, 1024)// 配置TCP引數
					.option(ChannelOption.SO_BACKLOG, 1024) // 設定tcp緩衝區
					.option(ChannelOption.SO_SNDBUF, 32 * 1024) // 設定傳送緩衝大小
					.option(ChannelOption.SO_RCVBUF, 32 * 1024) // 這是接收緩衝大小
					.option(ChannelOption.SO_KEEPALIVE, true) // 保持連線
					.childHandler(new ChildChannelHandler());// 最後繫結I/O事件的處理類
																// 處理網路IO事件

			// 伺服器啟動後 繫結監聽埠 同步等待成功 主要用於非同步操作的通知回撥 回撥處理用的ChildChannelHandler
			ChannelFuture f = serverBootstrap.bind(port).sync();
			System.out.println("Server啟動");
			// 等待服務端監聽埠關閉
			f.channel().closeFuture().sync();

		} finally {
			// 優雅退出 釋放執行緒池資源
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
			System.out.println("伺服器優雅的釋放了執行緒資源...");
		}

	}

	/**
	 * 網路事件處理器
	 */
	private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
		@Override
		protected void initChannel(SocketChannel ch) throws Exception {
			ch.pipeline().addLast(new StringEncoder());
			// 設定特殊分隔符
			ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
			ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
			// 設定字串形式的解碼
			ch.pipeline().addLast(new StringDecoder());
			ch.pipeline().addLast(new ServerHandler());
		}
	}

	public static void main(String[] args) throws Exception {
		int port = 9998;
		new Server().bind(port);
	}

}
7、服務端Handler的實現
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class ServerHandler extends ChannelHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		String request = (String) msg;
		System.out.println("Server :" + msg);
		String response = request;
		ctx.writeAndFlush(response + ConstValue.FUHAO);
	}

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

}
8、客戶端的實現
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
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.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class Client {

	/**
	 * 連線伺服器
	 * 
	 * @param port
	 * @param host
	 * @throws Exception
	 */
	public void connect(int port, String host) throws Exception {
		// 配置客戶端NIO執行緒組
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			// 客戶端輔助啟動類 對客戶端配置
			Bootstrap b = new Bootstrap();
			b.group(group)//
					.channel(NioSocketChannel.class)//
					.option(ChannelOption.TCP_NODELAY, true)//
					.handler(new MyChannelHandler());//
			// 非同步連結伺服器 同步等待連結成功
			ChannelFuture f = b.connect(host, port).sync();
			// 等待連結關閉
			f.channel().closeFuture().sync();

		} finally {
			group.shutdownGracefully();
			System.out.println("客戶端優雅的釋放了執行緒資源...");
		}

	}

	/**
	 * 網路事件處理器
	 */
	private class MyChannelHandler extends ChannelInitializer<SocketChannel> {
		@Override
		protected void initChannel(SocketChannel ch) throws Exception {
			ch.pipeline().addLast(new StringEncoder());
			// 設定特殊分隔符
			ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
			ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
			ch.pipeline().addLast(new StringDecoder());
			// 客戶端的處理器
			ch.pipeline().addLast(new ClientHandler());
		}

	}

	public static void main(String[] args) throws Exception {
		new Client().connect(9998, "127.0.0.1");

	}

}
9、客戶端Handler的實現
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class ClientHandler extends ChannelHandlerAdapter {

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		// 客戶端連線成功,給服務端傳送資料
		ctx.channel().writeAndFlush("aaaaabbbbb" + ConstValue.FUHAO);
		// 增加空格,達到定長
		ctx.channel().writeAndFlush("ccccccc   " + ConstValue.FUHAO);
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		String response = (String) msg;
		System.out.println("Client: " + response);
	}

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

}

相關推薦

【轉】Netty解決TCP(自定義協議)

https://www.cnblogs.com/sidesky/p/6913109.html 1、什麼是粘包/拆包        一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方

Netty解決TCP(設定訊息邊界)

1、什麼是粘包/拆包        一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,

Netty解決TCP(自定義協議)

1、什麼是粘包/拆包        一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,導

結合RPC框架通訊談 netty如何解決TCP問題

0.起因 因為自己造一個RPC框架的輪子時,需要解決TCP的粘包問題,特此記錄,希望方便他人。這是我寫的RPC框架的 GitHub地址 github.com/yangzhenkun… 歡迎star,fork。已經寫了多篇文章對這個框架的原理進行說明。對原理有興趣的歡迎交流。 1.什麼是粘包 1.1 什麼

netty權威指南學習筆記四——TCP/問題解決

方法 pan 對象 protect row 學習 ddl .get font   發生了粘包,我們需要將其清晰的進行拆包處理,這裏采用LineBasedFrameDecoder來解決 LineBasedFrameDecoder的工作原理是它依次遍歷ByteBuf中的可讀字節

Netty TCP基本解決方案

上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案 Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述的粘包和拆包的問題,歸根結底的解決方案就是傳送端給遠端端一個標記,告訴遠端端,

一起學Netty(七) TCP基本解決方案

上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案 Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述的粘包和拆包的問題,歸根結底的解決方案就是傳送端給遠端端一個標記,告訴遠端端,每個資訊的結束

Netty學習路(五)-TCP/問題

TCP是個“流協議”,所謂流,就是沒有界限的一串資料。TCP底層並不瞭解上層業務資料的具體含義,它會根據TCP緩衝區的實際情況進行包的劃分,所以一個完整的包可能會被TCP拆分成多個包進行傳送,也有可能吧多個小的包封裝成一個大的資料包傳送,這就是TCP粘包和拆包問題。 TCP粘包/拆包產生

Netty學習分隔符解決TCP

package com.phei.netty.s20160424; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.ne

Netty精粹TCP問題

粘包拆包問題是處於網路比較底層的問題,在資料鏈路層、網路層以及傳輸層都有可能發生。我們日常的網路應用開發大都在傳輸層進行,由於UDP有訊息保護邊界,不會發生這個問題,因此這篇文章只討論發生在傳輸層的TCP粘包拆包問題。 什麼是粘包、拆包? 對於什麼是粘包、拆包問題,

【Netty4.x】Netty TCP/問題的解決辦法(二)

一、什麼是TCP粘包/拆包  如圖所示,假如客戶端分別傳送兩個資料包D1和D2給服務端,由於服務端一次讀取到的位元組數是不確定的,故可能存在以下4中情況:第一種情況:Server端分別讀取到D1和D2,沒有產生粘包和拆包的情況。第二種情況:Server端一次接收到兩個資料包,

一起學Netty(六) TCP場景

TCP程式設計底層都有粘包和拆包機制,因為我們在C/S這種傳輸模型下,以TCP協議傳輸的時候,在網路中的byte其實就像是河水,TCP就像一個搬運工,將這流水從一端轉送到另一端,這時又分兩種情況: 1)如果客戶端的每次製造的水比較多,也就是我們常說的客戶端給的包比較大,TC

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

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

TCP基本解決方案

scu fonts println mar 是我 perf throws 自己 切割 上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案 Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述

netty學習筆記一:TCP

min -s 原因 兩個 image 分享 技術 ima 選項 什麽是TCP拆包粘包 假設客戶端發送了2條消息M1,M2。可能會出現以下幾種情況。 1、服務端正常接收到M1,M2這兩條消息。 2、服務端一次接收到了2個數據包,M1和M2粘合在一起,這時候就被稱為TCP粘包

Netty權威指南_札記04_TCP/問題解決

文章目錄 Netty權威指南_札記04_TCP粘包/拆包問題解決 1. TCP粘包/拆包 1.1 TCP粘包/拆包問題說明 1.2 TCP粘包/拆包發生的原因 1.3 粘包問題解決策略

Unity C# 自定義TCP傳輸協議以及封解決問題(網路應用層協議)

本文只是初步實現了一個簡單的基於TCP的自定協議,更為複雜的協議可以根據這種方式去擴充套件。 網路應用層協議,通俗一點的講,它是一種基於socket傳輸的由傳送方和接收方事先協商好的一種訊息包組成結構,主要由訊息頭和訊息體組成。  眾所周知,基於socket的資訊互動有兩

TCP/--利用DelimiterBasedFrameDecoder解決TCP問題

前面我們介紹了利用LineBasedFrameDecoder解決TCP的粘包/拆包的問題, 現在我們繼續介紹Netty的另外一種解碼器--DelemiterBasedFrameDecoder。 1. DelimiterBasedFrameDecoder服務端開發 EchoS

Netty中處理TCP

什麼是粘包和拆包 TCP是個”流”協議,流其實就是沒有界限的一串資料。 TCP底層中並不瞭解上層業務資料的具體含義,它會根據TCP緩衝區的實際情況進行包劃分,所以在TCP中就有可能一個完整地包會被TCP拆分成多個包,也有可能吧多個小的包封裝成一個大的資料包傳

Netty入門】解決TCP/分包的例項

回顧TCP粘包/分包問題的解決方法 1.訊息定長 2.在包尾都增加特殊字元進行分割 3.將訊息分為訊息頭和訊息體 針對這三種方法,下面我會分別舉例驗證 FixedLengthFrameDecoder類 對應第一種解決方法:訊息定長 (1)例1: