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沾包问题的主要内容,如果未能解决你的问题,请参考以下文章

TCP沾包问题

TCP通信中的“沾包”现象

3万字聊聊计算机网络

TCP/IP

TCP/IP

计算机网络协议复习 —— TCP/IP。。。