實例: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