• c++处理tcp粘包问题以及substr方法


    1.粘包原因

    在TCP通信中,粘包是指发送方在发送数据时,多个小的数据包被合并成一个大的数据包,或者接收方在接收数据时,一个大的数据包被拆分成多个小的数据包。这种情况可能会导致接收方无法正确解析数据,从而造成数据处理错误。
    
    • 1

    2.tcp基础

    TCP是一种面向连接的协议,它提供可靠的、有序的、基于字节流的数据传输。TCP建立连接的过程包括“三次握手”,即客户端发送连接请求,服务器回应确认,最后客户端再次回应确认。连接建立后,TCP通过使用滑动窗口、序列号和确认机制来保证数据的顺序和完整性。TCP还支持流量控制和拥塞控制机制,以保证网络的可靠性和稳定性。因此,TCP适用于需要可靠传输的应用,如网页浏览、文件传输、电子邮件等。

    三次握手

    1.第一次握手:客户端发送连接请求报文段(SYN)到服务器,进入SYN_SENT状态。
    2.第二次握手:服务器收到请求后,回复一个确认报文段(SYN+ACK)以及自己的连接请求报文段(SYN),进入SYN_RCVD状态。
    3.第三次握手:客户端收到确认后,再发送一个确认报文段(ACK),双方进入Established状态,连接建立。
    三次握手的主要目的是双方确认彼此的发送和接收能力正常,并且同步初始序列号。

    四次挥手

    1.第一次挥手:发起关闭的一方发送一个FIN报文段给对方,进入FIN_WAIT_1状态。
    2.第二次挥手:对方收到FIN后,回复一个确认报文段(ACK),进入CLOSE_WAIT状态。
    3.第三次挥手:当对方不再需要发送数据时,会发送一个FIN报文段给发起关闭的一方,进入LAST_ACK状态。
    4.第四次挥手:发起关闭的一方收到对方的FIN后,发送一个确认报文段(ACK),进入TIME_WAIT状态。等待2MSL时间后,进入CLOSED状态。
    四次挥手的主要目的是确保双方都能够完成未发送完的数据的传输,并且结束连接。

    长连接和和短连接

    • 长连接:指在一个TCP连接中可以传输多个数据包,在处理完一个请求后不会立即断开连接,而是保持连接状态,等待后续的请求。长连接可以减少连接建立和断开的开销,适用于频繁的数据交换场景,如网页浏览、移动App等。
    • 短连接:指每次请求都要建立一个新的TCP连接,在请求处理完毕后立即断开连接。短连接适用于一次性传输少量数据的场景,如DNS查询、文件下载等。
      选择长连接还是短连接取决于具体的应用场景和性能要求。

    3.解决方式

    解决TCP粘包问题有多种方法,下面介绍两种常用的方式:

    1.定长消息:

    发送方在发送数据时,将每个数据包的长度固定为一个固定值。接收方在接收数据时,根据固定的长度对数据进行拆分。这种方式简单直接,但是对于不同长度的数据包处理不便。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    const int MESSAGE_LENGTH = 10;  // 定义消息长度为10
    int main() {
    // 创建socket
    int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == -1) {
    std::cerr << "Failed to create socket" << std::endl;
    return -1;
    }
    // 设置socket地址
    sockaddr_in serverAddress{};
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(8080);
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    
    // 绑定socket地址
    if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
        std::cerr << "Failed to bind socket" << std::endl;
        close(serverSocket);
        return -1;
    }
    
    // 监听socket
    if (listen(serverSocket, SOMAXCONN) == -1) {
        std::cerr << "Failed to listen on socket" << std::endl;
        close(serverSocket);
        return -1;
    }
    
    while (true) {
        // 接受新的连接
        sockaddr_in clientAddress{};
        socklen_t clientAddressSize = sizeof(clientAddress);
        int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);
        if (clientSocket == -1) {
            std::cerr << "Failed to accept connection" << std::endl;
            close(serverSocket);
            return -1;
        }
    
        std::cout << "New connection accepted" << std::endl;
    
        // 接收数据
        char buffer[MESSAGE_LENGTH];
        std::string receivedData;
    
        while (true) {
            memset(buffer, 0, sizeof(buffer));
            ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);
            if (bytesRead == -1) {
                std::cerr << "Failed to receive data" << std::endl;
                close(clientSocket);
                break;
            }
            if (bytesRead == 0) {
                std::cout << "Connection closed" << std::endl;
                close(clientSocket);
                break;
            }
    
            receivedData += buffer;
    
            // 检查是否有完整的消息
            while (receivedData.length() >= MESSAGE_LENGTH) {
                std::string message = receivedData.substr(0, MESSAGE_LENGTH);
                std::cout << "Received message: " << message << std::endl;
    
                // 处理消息
    
                // 移除已处理的消息
                receivedData = receivedData.substr(MESSAGE_LENGTH);
            }
        }
    }
    
    // 关闭socket
    close(serverSocket);
    
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    2.分隔符消息:

    发送方在发送数据时,在每个数据包的末尾添加一个特定的分隔符,例如换行符或特殊字符。接收方在接收数据时,根据分隔符将数据包拆分成多个小的数据包。这种方式对于不同长度的数据包处理更加灵活,但需要注意选择合适的分隔符,以避免与数据内容冲突。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    const char DELIMITER = '\n';
    
    int main() {
        // 创建socket
        int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (serverSocket == -1) {
            std::cerr << "Failed to create socket" << std::endl;
            return -1;
        }
    
        // 设置socket地址
        sockaddr_in serverAddress{};
        serverAddress.sin_family = AF_INET;
        serverAddress.sin_port = htons(8080);
        serverAddress.sin_addr.s_addr = INADDR_ANY;
    
        // 绑定socket地址
        if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
            std::cerr << "Failed to bind socket" << std::endl;
            close(serverSocket);
            return -1;
        }
    
        // 监听socket
        if (listen(serverSocket, SOMAXCONN) == -1) {
            std::cerr << "Failed to listen on socket" << std::endl;
            close(serverSocket);
            return -1;
        }
    
        while (true) {
            // 接受新的连接
            sockaddr_in clientAddress{};
            socklen_t clientAddressSize = sizeof(clientAddress);
            int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);
            if (clientSocket == -1) {
                std::cerr << "Failed to accept connection" << std::endl;
                close(serverSocket);
                return -1;
            }
    
            std::cout << "New connection accepted" << std::endl;
    
            // 接收数据
            char buffer[1024];
            std::string receivedData;
    
            while (true) {
                memset(buffer, 0, sizeof(buffer));
                ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
                if (bytesRead == -1) {
                    std::cerr << "Failed to receive data" << std::endl;
                    close(clientSocket);
                    break;
                }
                if (bytesRead == 0) {
                    std::cout << "Connection closed" << std::endl;
                    close(clientSocket);
                    break;
                }
    
                receivedData += buffer;
    
                // 检查是否有完整的消息
                size_t delimiterPos = receivedData.find(DELIMITER);
                while (delimiterPos != std::string::npos) {
                    std::string message = receivedData.substr(0, delimiterPos);
                    std::cout << "Received message: " << message << std::endl;
    
                    // 处理消息
    
                    // 移除已处理的消息
                    receivedData = receivedData.substr(delimiterPos + 1);
    
                    // 继续查找下一个分隔符
                    delimiterPos = receivedData.find(DELIMITER);
                }
            }
        }
    
        // 关闭socket
        close(serverSocket);
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    4.substr方法

    C++ 标准库中的一个字符串处理函数,用于从一个字符串中提取子字符串。
    substr 函数的语法如下:

    string substr(size_t pos = 0, size_t len = npos) const;
    
    • 1

    其中,pos 参数表示要提取的子字符串的起始位置,len 参数表示要提取的子字符串的长度。如果不指定 len 参数,则默认提取从 pos 位置到字符串末尾的所有字符。
    substr 函数返回一个新的字符串,包含了从原始字符串中提取的子字符串。
    下面是一个简单的示例,演示了如何使用 substr 函数:

    #include 
    #include 
    
    int main() {
        std::string str1 = "Hello, World!";
    
        // 提取从位置 7 开始的子字符串
        std::string substr1 = str1.substr(7);
        std::cout << "Substring 1: " << substr1 << std::endl;
    
        // 提取从位置 0 开始,长度为 5 的子字符串
        std::string substr2 = str1.substr(0, 5);
        std::cout << "Substring 2: " << substr2 << std::endl;
        std::cout << sizeof(str1) << std::endl;
    
    
        //清空定长str1
        str1 = str1.substr(7);
        //清空所有
        //str1.clear();
        //str1 = "";
    
        std::cout << "str1: " << str1 << std::endl;
    
    
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出:

    Substring 1: World!
    Substring 2: Hello
    str1:World!
    
    • 1
    • 2
    • 3
  • 相关阅读:
    2022-12-02 编译Android平台OpenCV,用到读取视频时报错:AMediaXXX
    旧版Mac如何装新系统
    【WINDOWS / DOS 批处理】call命令的变量延迟展开特性
    Nginx中实现自签名SSL证书生成与配置
    23种设计模式
    【LeetCode热题100】--128. 最长连续序列
    代码随想录算法训练营day50
    居家必备练习题(C语言)
    springcloudAlibaba之Nacos服务注册源码分析
    STM32cubeMX配置FreeRTOS-51、USB-U盘读写
  • 原文地址:https://blog.csdn.net/qq_44913716/article/details/134515550