教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢/投訴熱線:400-618-4000

Java中tcp粘包是怎么產(chǎn)生的?

更新時(shí)間:2023年04月19日13時(shí)48分 來(lái)源:傳智教育 瀏覽次數(shù):

好口碑IT培訓(xùn)

  TCP粘包是指發(fā)送方在發(fā)送數(shù)據(jù)時(shí),將多個(gè)小數(shù)據(jù)包粘合成一個(gè)大數(shù)據(jù)包發(fā)送到接收方,或者接收方在接收數(shù)據(jù)時(shí),將一個(gè)大數(shù)據(jù)包拆分成多個(gè)小數(shù)據(jù)包。這種情況常常發(fā)生在TCP數(shù)據(jù)流傳輸?shù)倪^程中。

  TCP協(xié)議本身是一種基于流的傳輸協(xié)議,它并沒有像UDP那樣的數(shù)據(jù)報(bào)文概念。在發(fā)送方發(fā)送數(shù)據(jù)時(shí),TCP會(huì)將數(shù)據(jù)分成一個(gè)個(gè)小的TCP數(shù)據(jù)包進(jìn)行傳輸,并且不保證這些小的TCP數(shù)據(jù)包按照發(fā)送順序到達(dá)接收方。在接收方接收數(shù)據(jù)時(shí),TCP會(huì)將接收到的數(shù)據(jù)按照TCP數(shù)據(jù)包的順序重新組合成一個(gè)完整的數(shù)據(jù)流。

  由于TCP傳輸?shù)臄?shù)據(jù)是一個(gè)流,而不是數(shù)據(jù)包,所以在數(shù)據(jù)發(fā)送和接收的過程中,會(huì)存在數(shù)據(jù)包的大小和數(shù)量不一致的情況,從而導(dǎo)致TCP粘包的問題。

  以下是一個(gè)簡(jiǎn)單的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();
        }
    }
}

  上述代碼中,我們創(chuàng)建了一個(gè)TCP服務(wù)器,監(jiān)聽本地的8888端口。在接收到客戶端的連接之后,我們使用一個(gè)新的線程來(lái)處理這個(gè)連接。在這個(gè)線程中,我們通過輸入流從客戶端接收數(shù)據(jù),并且通過輸出流將數(shù)據(jù)發(fā)送回客戶端。

  現(xiàn)在,我們使用另外一個(gè)Java程序來(lái)模擬一個(gè)客戶端連接:

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();
    }
}

  上述代碼中,我們創(chuàng)建了一個(gè)TCP客戶端,連接到本地的8888端口。在連接建立之后,我們向服務(wù)器發(fā)送10個(gè)"Hello, World!"字符串。在發(fā)送完這些數(shù)據(jù)之后,我們從服務(wù)器端的角度來(lái)看,當(dāng)接收到客戶端發(fā)送的數(shù)據(jù)時(shí),我們使用一個(gè)1024字節(jié)大小的緩沖區(qū)來(lái)接收數(shù)據(jù)。在讀取數(shù)據(jù)時(shí),我們使用一個(gè)while循環(huán)來(lái)不斷從輸入流中讀取數(shù)據(jù),并將其輸出到控制臺(tái)上。在輸出數(shù)據(jù)時(shí),我們并沒有做任何的數(shù)據(jù)處理,而是直接將數(shù)據(jù)輸出到控制臺(tái)上。

  在客戶端發(fā)送數(shù)據(jù)時(shí),我們發(fā)送了10個(gè)"Hello, World!"字符串。由于TCP是一種基于流的協(xié)議,所以這些數(shù)據(jù)可能會(huì)被組合成一個(gè)大的數(shù)據(jù)包發(fā)送到服務(wù)器端。在接收數(shù)據(jù)時(shí),服務(wù)器端每次從輸入流中讀取1024個(gè)字節(jié)大小的數(shù)據(jù),然后直接輸出到控制臺(tái)上。如果客戶端發(fā)送的數(shù)據(jù)包的大小小于1024字節(jié),那么這些數(shù)據(jù)可能會(huì)和后面的數(shù)據(jù)一起被讀取,從而導(dǎo)致粘包的問題。

  為了演示這個(gè)問題,我們可以將客戶端發(fā)送的數(shù)據(jù)包大小改為比緩沖區(qū)大小小的數(shù)據(jù),例如:

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();
}

  在上述代碼中,我們將每個(gè)"Hello, World!"字符串拆分成兩個(gè)大小相等的字節(jié)數(shù)組發(fā)送到服務(wù)器端。這樣,每個(gè)數(shù)據(jù)包的大小就會(huì)小于1024字節(jié)。運(yùn)行客戶端程序后,我們可以看到服務(wù)器端輸出了一些類似于以下的輸出:

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!"字符串被拆分成了多個(gè)部分,從而導(dǎo)致了TCP粘包的問題。

0 分享到:
和我們?cè)诰€交談!