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
此博客乃做者原创, 转载请注明出处 编码