TCP沾包问题
Posted 一位懒得写博客的小学生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP沾包问题相关的知识,希望对你有一定的参考价值。
目录
面向字节流
创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;
- 调用write时, 数据会先写入发送缓冲区中;
- 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
- 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
- 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
- 然后应用程序可以调用read从接收缓冲区拿数据;
沾包问题
- 粘包问题中的 “包” , 是指的应用层的数据包;
- 在TCP的协议头中, 没有如同UDP一样的 “报文长度” 这样的字段, 但是有一个序号这样的字段;
- 站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中;
- 站在应用层的角度, 看到的只是一串连续的字节数据;
- 那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包.
正常情况
沾包
半包
代码实现
客户端发送10条消息给服务器端。
//服务器端
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerError
//端口号
private static final int port = 9005;
//内容长度
private static final int len = 1024;
public static void main(String[] args) throws IOException
//创建 TCP 服务端
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器已启动");
//得到客户端连接
Socket socket = serverSocket.accept();
System.out.println(String.format("已连接,IP:%s,Port:%d",socket.getInetAddress().getHostAddress(),socket.getPort()));
try(InputStream inputStream = socket.getInputStream())
while(true)
byte[] bytes = new byte[len];
//将内容读取到数组中
int result = inputStream.read(bytes,0,len);
if(result > 0)
String msg = new String(bytes);
System.out.println("收到消息:" + msg);
//客户端
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class TCPClientError
//IP
private static final String IP = "127.0.0.1";
//端口号
private static final int Port = 9005;
public static void main(String[] args) throws IOException
//创建Socket 连接服务器
Socket socket = new Socket(IP,Port);
String msg = "hello world!";
//得到发送对象
try(OutputStream outputStream = socket.getOutputStream())
for (int i = 0; i < 10; i++)
//发消息给服务器端
outputStream.write(msg.getBytes());
outputStream.flush();
结果
解决方案1
1.使用 \\n 作为流的结束符,这样流就有边界了,就能正常的收、发消息了。
代码实现
//服务器端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerError
//端口号
private static final int port = 9005;
//内容长度
private static final int len = 1024;
public static void main(String[] args) throws IOException
//创建 TCP 服务端
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器已启动");
//得到客户端连接
Socket socket = serverSocket.accept();
System.out.println(String.format("已连接,IP:%s,Port:%d",socket.getInetAddress().getHostAddress(),socket.getPort()));
//解决方案1
try(BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())))
while(true)
//按行分割读取内容
String msg = reader.readLine();
if(msg != null && !msg.equals(" "))
System.out.println("收到消息:" + msg);
//客户端
import java.io.*;
import java.net.Socket;
public class TCPClientError
//IP
private static final String IP = "127.0.0.1";
//端口号
private static final int Port = 9005;
public static void main(String[] args) throws IOException
//创建Socket 连接服务器
Socket socket = new Socket(IP,Port);
//字符串末尾加\\n
String msg = "hello world!\\n";
//得到发送对象
try(OutputStream outputStream = socket.getOutputStream())
for (int i = 0; i < 10; i++)
//发消息给服务器端
outputStream.write(msg.getBytes());
outputStream.flush();
打印结果
解决方案2
2.每次发送固定大小的流信息,这样也能确定每个数据边界,正常收、发消息了。
代码实现
//服务器端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerError
//端口号
private static final int port = 9005;
//内容长度
private static final int len = 1024;
public static void main(String[] args) throws IOException
//创建 TCP 服务端
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器已启动");
//得到客户端连接
Socket socket = serverSocket.accept();
System.out.println(String.format("已连接,IP:%s,Port:%d",socket.getInetAddress().getHostAddress(),socket.getPort()));
//解决方案2
try(InputStream inputStream = socket.getInputStream())
while(true)
byte[] bytes = new byte[len];
//将内容读取到数组中
int result = inputStream.read(bytes,0,len);
if(result > 0)
String msg = new String(bytes);
System.out.println("收到消息:" + msg.trim());
//客户端
import java.io.*;
import java.net.Socket;
public class TCPClientError
//IP
private static final String IP = "127.0.0.1";
//端口号
private static final int Port = 9005;
public static void main(String[] args) throws IOException
//创建Socket 连接服务器
Socket socket = new Socket(IP,Port);
//字符串末尾加\\n
String msg = "hello world!\\n";
//得到发送对象
try(OutputStream outputStream = socket.getOutputStream())
//规定流的大小就为1024
byte[] bytes = new byte[1024];
int index = 0;
for(Byte item : msg.getBytes())
bytes[index++] = item;
for (int i = 0; i < 10; i++)
//发消息给服务器端
outputStream.write(bytes);
outputStream.flush();
执行结果
以上是关于TCP沾包问题的主要内容,如果未能解决你的问题,请参考以下文章