教育行业A股IPO第一股(股票代码 003032)

全国咨询/投诉热线:400-618-4000

Java中tcp粘包是怎么产生的?

更新时间:2023年04月19日13时48分 来源:传智教育 浏览次数:

好口碑IT培训

  TCP粘包是指发送方在发送数据时,将多个小数据包粘合成一个大数据包发送到接收方,或者接收方在接收数据时,将一个大数据包拆分成多个小数据包。这种情况常常发生在TCP数据流传输的过程中。

  TCP协议本身是一种基于流的传输协议,它并没有像UDP那样的数据报文概念。在发送方发送数据时,TCP会将数据分成一个个小的TCP数据包进行传输,并且不保证这些小的TCP数据包按照发送顺序到达接收方。在接收方接收数据时,TCP会将接收到的数据按照TCP数据包的顺序重新组合成一个完整的数据流。

  由于TCP传输的数据是一个流,而不是数据包,所以在数据发送和接收的过程中,会存在数据包的大小和数量不一致的情况,从而导致TCP粘包的问题。

  以下是一个简单的Java代码演示TCP粘包的问题:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {

    public static void main(String[] args) throws IOException {

        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            Socket socket = serverSocket.accept();

            new Thread(() -> {
                try {
                    InputStream inputStream = socket.getInputStream();
                    OutputStream outputStream = socket.getOutputStream();

                    byte[] buffer = new byte[1024];

                    int len;
                    while ((len = inputStream.read(buffer)) != -1) {
                        System.out.println(new String(buffer, 0, len));
                        outputStream.write(buffer, 0, len);
                        outputStream.flush();
                    }

                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

  上述代码中,我们创建了一个TCP服务器,监听本地的8888端口。在接收到客户端的连接之后,我们使用一个新的线程来处理这个连接。在这个线程中,我们通过输入流从客户端接收数据,并且通过输出流将数据发送回客户端。

  现在,我们使用另外一个Java程序来模拟一个客户端连接:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {

    public static void main(String[] args) throws IOException {

        Socket socket = new Socket("localhost", 8888);

        OutputStream outputStream = socket.getOutputStream();

        for (int i = 0; i < 10; i++) {
            String message = "Hello, World!";
            byte[] buffer = message.getBytes();
            outputStream.write(buffer);
            outputStream.flush();
        }

        InputStream inputStream = socket.getInputStream();

        byte[] buffer = new byte[1024];
        int len = inputStream.read(buffer);
        System.out.println(new String(buffer, 0, len));

        socket.close();
    }
}

  上述代码中,我们创建了一个TCP客户端,连接到本地的8888端口。在连接建立之后,我们向服务器发送10个"Hello, World!"字符串。在发送完这些数据之后,我们从服务器端的角度来看,当接收到客户端发送的数据时,我们使用一个1024字节大小的缓冲区来接收数据。在读取数据时,我们使用一个while循环来不断从输入流中读取数据,并将其输出到控制台上。在输出数据时,我们并没有做任何的数据处理,而是直接将数据输出到控制台上。

  在客户端发送数据时,我们发送了10个"Hello, World!"字符串。由于TCP是一种基于流的协议,所以这些数据可能会被组合成一个大的数据包发送到服务器端。在接收数据时,服务器端每次从输入流中读取1024个字节大小的数据,然后直接输出到控制台上。如果客户端发送的数据包的大小小于1024字节,那么这些数据可能会和后面的数据一起被读取,从而导致粘包的问题。

  为了演示这个问题,我们可以将客户端发送的数据包大小改为比缓冲区大小小的数据,例如:

for (int i = 0; i < 10; i++) {
    String message = "Hello, World!";
    byte[] buffer = message.getBytes();
    outputStream.write(buffer, 0, buffer.length / 2);
    outputStream.write(buffer, buffer.length / 2, buffer.length / 2);
    outputStream.flush();
}

  在上述代码中,我们将每个"Hello, World!"字符串拆分成两个大小相等的字节数组发送到服务器端。这样,每个数据包的大小就会小于1024字节。运行客户端程序后,我们可以看到服务器端输出了一些类似于以下的输出:

Hello, Wo
rld!Hello,
 World!Hel
lo, World!H
ello, Worl
d!Hello, Wo
rld!Hello,
 World!Hello
, World!Hel
lo, World!H
ello, Worl
d!Hello, Wo
rld!Hello,
 World!Hello
, World!Hel
lo, World!

  从上面的输出中,我们可以看到"Hello, World!"字符串被拆分成了多个部分,从而导致了TCP粘包的问题。

0 分享到:
和我们在线交谈!