實例:Netty 處理 TCP協議數據分包問題
一、Netty解決TCP協議數據分包問題思路
我們知道通過TCP協議發送接收數據時,如果數據過大,接收到的數據會是分包的,比如:
Netty服務端:定義一個LengthFieldBasedFrameDecoder(1024*1024*1024, 0, 4,0,4)),最大數據量是1GB,長度屬性位於數據包頭部,占4個字節,協議體調節值為0,跳過頭部協議長度四個字節
三、總結:客戶端和服務端定義消息格式必須一致
+-----+-----+-----+
發送數據是: | ABC | DEF | GHI |
+-----+-----+-----+
發送數據是: | ABC | DEF | GHI |
+-----+-----+-----+
而我們想接受到的數據是: | ABCDEFGHI |
該如何處理這種情況呢?Netty提供了一個專門處理TCP協議數據的Handler:LengthFieldBasedFrameDecoder ,它的原理是服務器端和客戶端約定一個協議格式:數據包=協議長度+協議體
--------------------------------數據包------------------------------
| 協議長度部分(接收數據長度) | 協議體部分(要接收的數據)|
舉個例子,假如我們的TCP客戶端發送了10MB字節的數據,如何讓Netty服務器一次就接收到這10MB數據呢?那就需要客戶端告訴服務端我發送的數據大小是多少,即在發送的數據中加入一個“數據包長度”即可,上麵提到的Handler就是用來和客戶端約定這個協議格式的,它有幾個參數,下麵我介紹一下它的參數意義:
int maxFrameLength:定義接收數據包的最大長度,如果發送的數據包超過此值,則拋出異常;
int lengthFieldOffset:長度屬性部分的偏移值,0表示長度屬性位於數據包頭部;
int lengthFieldLength:長度屬性的字節長度,如果設置為4,就是我們用4個字節存放數據包的長度;
int lengthAdjustment:協議體長度調節值,修正信息長度,如果設置為4,那麼解碼時再向後推4個字節;
int initialBytesToStrip:跳過字節數,如我們想跳過長度屬性部分。
二、實例-客戶端發送10MB字節的數據,Netty服務端一次接收到全部10MB數據
客戶端:定義一個消息體,用頭部四個字節存放數據包長度
public byte[] send(byte[] sendData) throws UnknownHostException, IOException {
Socket socket = new Socket(serverIp, serverPort);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
byte resultArray[] = null;
try {
// 定義一個發送消息協議格式:|--header:4 byte--|--content:10MB--|
// 獲取一個4字節長度的協議體頭
byte[] dataLength = intToByteArray(4, sendData.length);
// 和請求的數據組成一個請求數據包
byte[] requestMessage = combineByteArray(dataLength, sendData);
//發送數據-------------------------------
os.write(requestMessage);
os.flush();
//接收數據-------------------------------
resultArray = IOUtils.toByteArray(is);
} catch (Exception e) {
e.printStackTrace();
} finally {
os.close();
is.close();
socket.close();
}
return resultArray;
}
private static byte[] intToByteArray(int byteLength, int intValue) {
return ByteBuffer.allocate(byteLength).putInt(intValue).array();
}
private static byte[] combineByteArray(byte[] array1, byte[] array2) {
byte[] combined = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, combined, 0, array1.length);
System.arraycopy(array2, 0, combined, array1.length, array2.length);
return combined;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framedecoder",new LengthFieldBasedFrameDecoder(1024*1024*1024, 0, 4,0,4));
pipeline.addLast(new TCPServiceHandler());// 處理業務Handler
}
三、總結:客戶端和服務端定義消息格式必須一致
最後更新:2017-04-03 20:19:27