TCP的面向字节流和粘包问题(用Java代码理解)

一、面向字节流

如何理解TCP是面向字节流协议

之所以说TCP是面向字节流协议,UDP是面向报文协议。主要是因为发送方的发送消息的机制不同

UDP是面向报文协议

        udp在发送消息时,在传输层直接就将一个消息打包成一个完整的包,组装好udp头部,不进行切割,就转发给网络层。也就是每一个UDP报文就是一个消息。服务端在接收到udp报文时,会将它放到一个队列中,一个元素就是一个udp报文。每次读取时,读取一个元素

TCP是面向字节流协议

        当tcp在传输层发送消息时,一个消息可能会被分割成多个tcp报文进行转发给网络层。我们不能认为一个tcp报文就是一个消息,所以tcp是面向字节流协议

由于一个消息对应的不是一个tcp报文,如果接受方不知道一个消息的长度或者分割的边界在哪里,就会无法组装成一个消息,这就形成了粘包问题

二、粘包问题解决

三种方式:

  • 固定长度的消息
  • 特殊字符当做边界
  • 自定义消息结构

固定长度的消息

将消息长度固定,比如规定一个消息长度为64字节,那么只要接收到了64字节,就认为这个内容是一个完整的消息

特殊字符作为边界

我们可以在两个消息之间加入一个特殊字符,如果读到了这个特殊字符,则认为一个消息已经完整的接收到了。
比如我们的http

HTTP通过回车换行来判断消息边界。

需要注意的是,如果消息内容中含有分割消息的特殊字符的话,需要进行转义处理,否则则会错误判断消息边界

自定义消息结构

我们可以自定义消息结构,由包头和数据来构成,包头里有一个字段表示紧随其后的一个消息有多大。

当接收方接收到包头时,就会解析包头里的数据长度,接下来就读取数据,知道读取到指定长度时,就是一个完整的消息

        TCP是一种面向字节流的协议,它将数据视为连续的字节流,而不是分割成消息。这意味着在发送端,应用程序将数据写入TCP连接时,TCP会将数据转换为字节流,并在接收端将字节流重新转换回数据。

        面向字节流的特性使得TCP非常灵活,可以传输任意类型的数据,包括文本、图像、音频等。然而,正是由于面向字节流的特性,导致了粘包问题的出现。

下面是一个简单的Java代码示例,演示了粘包问题的可能场景:

import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        Socket socket = serverSocket.accept();

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            System.out.println("Received message: " + inputLine);
        }

        socket.close();
        serverSocket.close();
    }
}
import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 8888);
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

        out.println("Message 1");
        out.println("Message 2");
        out.println("Message 3");

        socket.close();
    }
}

        在这个示例中,客户端连续发送了三条消息给服务器端,但由于TCP的面向字节流特性,这三条消息可能会被合并成一个数据包发送给服务器端,从而造成粘包问题。

三、解决方法

        解决粘包问题的方法有多种,常用的方法包括消息边界标记、消息长度标记和使用消息头等。其中,消息边界标记是一种常见且简单的解决方法,即在每条消息的末尾添加特定的分隔符,如换行符(\n),接收端根据分隔符来识别消息的边界。

下面是一个使用消息边界标记解决粘包问题的Java代码示例:

import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 8888);
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

        out.println("Message 1\n");
        out.println("Message 2\n");
        out.println("Message 3\n");

        socket.close();
    }
}

        在这个示例中,客户端在每条消息的末尾添加了换行符(\n),接收端可以根据换行符来识别消息的边界,从而正确解析数据,避免了粘包问题的出现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值