一、TCP粘包拆包问题解决方案
《TCP粘包拆包一》抛出了粘包拆包的问题,那么有哪些解决方案呢?
常用解决方案:
1、固定消息长度,例如每个报文长度500字节,不够该长度的用特殊字符补齐,如空格、\001等,这种 方式会增加一些无用字符的传递,浪费网络流量。
2、包尾部追加特殊符号进行分割,这种方式如果传输的数据中有分割符号,还要进行转义,增加编码的 难度。
3、消息分为消息头和消息体两部分;
4、开发复杂应用协议,如HTTP协议等
二、示例
我们实现第三种方案来解决粘包拆包问题, 一个报文分两块,消息头我们用4个字节来存储消息实体的字节数,编码实现如下:
1、客户端代码
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;
}
}
2、服务端代码
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;
}
}
3、运行结果
这样就实现一个报文一个报文的读取了,基于这种实现也可以解决RPC框架中的粘包拆包问题。
三、问题
Java支持更高效的IO方式Nio,那么也有一些高效Nio框架,如netty、mina,这些框架极大的提高了程序员网络编程的效率,那么这些框架提供哪些方法去解决拆包粘包问题呢?
快乐源于分享。
此博客乃作者原创, 转载请注明出处