1、TCP粘包拆包問題解決方案
《TCP粘包拆包一》拋出了粘包拆包的問題,那麼有哪些解決方案呢?
經常使用解決方案:
一、固定消息長度,例如每一個報文長度500字節,不夠該長度的用特殊字符補齊,如空格、\001等,這種 方式會增長一些無用字符的傳遞,浪費網絡流量。
二、包尾部追加特殊符號進行分割,這種方式若是傳輸的數據中有分割符號,還要進行轉義,增長編碼的 難度。
三、消息分爲消息頭和消息體兩部分;
四、開發複雜應用協議,如HTTP協議等java
2、示例
咱們實現第三種方案來解決粘包拆包問題, 一個報文分兩塊,消息頭咱們用4個字節來存儲消息實體的字節數,編碼實現以下:
一、客戶端代碼程序員
public class SocketClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1", 8088); OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(os); for (int i = 0; i < 100; i++) { byte[] head = intToBytes("A".getBytes().length); // 消息實體的長度是用消息頭和消息體組成 byte[] message = new byte[head.length + "A".getBytes().length]; System.arraycopy(head, 0, message, 0, head.length); System.arraycopy("A".getBytes(), 0, message, head.length, "A".getBytes().length); os.write(message); } pw.flush(); pw.close(); os.close(); socket.close(); } // int型轉字節數組,java裏int用4字節表示,因此返回的字節數組長度爲4 public static byte[] intToBytes(int value) { byte[] src = new byte[4]; src[0] = (byte) ((value >> 24) & 0xFF); src[1] = (byte) ((value >> 16) & 0xFF); src[2] = (byte) ((value >> 8) & 0xFF); src[3] = (byte) (value & 0xFF); return src; } }
二、服務端代碼編程
public class SocketServer { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8088); while (true) { Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); int i = 0; byte[] head = new byte[4];// 先讀取4個字節消息頭 while (is.read(head) != -1) { i++; int messageLenth = byteArrayToInt(head); byte message[] = new byte[messageLenth]; is.read(message);// 讀取消息體 System.out.println(new String(message)); } System.out.println("總數讀取次數" + i); is.close(); socket.close(); } } public static int byteArrayToInt(byte[] b) { return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24; } }
三、運行結果數組
這樣就實現一個報文一個報文的讀取了,基於這種實現也能夠解決RPC框架中的粘包拆包問題。網絡
3、問題
Java支持更高效的IO方式Nio,那麼也有一些高效Nio框架,如netty、mina,這些框架極大的提升了程序員網絡編程的效率,那麼這些框架提供哪些方法去解決拆包粘包問題呢? 請關注下一篇博文《TCP粘包拆包三》
框架
快樂源於分享。socket
此博客乃做者原創, 轉載請註明出處 編碼