发送 recv 调用时带有 C++ 字符串的垃圾文本

garbage text with c++ strings on send recv calls

本文关键字:字符串 文本 C++ recv 调用 发送      更新时间:2023-10-16
// In server.cpp after connection has established
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore();  // =====(1)=====
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len        = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0); // =====(2)=====
std::cout << "Input length : " << input.length() << std::endl
          << "Input bytes sent : " << bytes_sent << std::endl;

我的目标是在简单的 tcp 客户端服务器程序中使用 std::string 而不是普通的旧char[fixed]。所以在server.cpp我有 2 个疑问。到目前为止,我最初的猜测正在按预期工作。我已经在上面的代码中标记了它们。

  1. cin.ignore() vs cin.clear() + cin.sync()
  2. std::string.data()std::string.c_str()

我应该使用哪个?我什至不确定这些中的任何一个黑白的区别,我不知道它们是否导致了我的问题。


// In client.cpp 
std::string message;
message.reserve(5);
auto len        = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0);  // =====(1)=====
message[len] = 0; // =====(2)=====
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl;  // =====(3)=====

在客户端.cpp中,当我尝试发送更大的字符串时,一切都出错了。但我可能知道原因,但是解决方案实施起来有些棘手。

  1. 我是否在做正确的事情来传递&std::string.front()来写入传入数据?
  2. 这是错误的,字符串类应该管理这个,对吧?但是,由于我直接写信给&front(),我想length不会更新,或者我不确定会发生什么,但是在使用std::cout << message;输出时数据肯定会丢失。
  3. 我这样做只是因为我直接写入&front,如果返回的数据以某种方式小于总长度,它仍然会产生垃圾,可能是因为它无法在正确的位置找到终止字符?

服务器.cpp

// compile as 'g++ server.cpp -o server.app -std=c++14'

// run as : './server.app 8080'

#include <iostream>
#include <string>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
    if(argc != 2) {
        std::cerr << "Run program as 'program port'" << std::endl;
        return -1;
    }
    auto &portNum              = argv[1];
    const unsigned int backLog = 5;
    struct addrinfo hints, *res, *p;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags    = AI_PASSIVE;
    int gAddRes = getaddrinfo(NULL, portNum, &hints, &res);
    if(gAddRes != 0) {
        std::cerr << gai_strerror(gAddRes) << std::endl;
        return -2;
    }
    std::cout << "Detecting addresses" << std::endl;
    unsigned int numOfAddr = 0;
    char ipStr[INET6_ADDRSTRLEN];
    for(p = res; p != NULL; p = p->ai_next) {
        void *addr;
        std::string ipVer = "IPv0";
        if(p->ai_family == AF_INET) {
            ipVer                    = "IPv4";
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
            addr                     = &(ipv4->sin_addr);
            ++numOfAddr;
        }
        else {
            ipVer                     = "IPv6";
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr                      = &(ipv6->sin6_addr);
            ++numOfAddr;
        }
        inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
        std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
                  << std::endl;
    }
    if(!numOfAddr) {
        std::cerr << "Found no host address to use" << std::endl;
        return -3;
    }
    std::cout << "Enter the number of host address to bind with:" << std::endl;
    unsigned int choice = 0;
    bool madeChoice     = false;
    do {
        std::cin >> choice;
        if(choice > (numOfAddr + 1) || choice < 1) {
            madeChoice = false;
            std::cout << "Wrong choice, try again!" << std::endl;
        }
        else
            madeChoice = true;
    } while(!madeChoice);
    p           = res;
    bool isIPv4 = true;
    if(choice > 1) {
        unsigned int temp = 1;
        while(choice < temp) {
            p = p->ai_next;
            ++temp;
        }
        if(p->ai_family == AF_INET) {
            isIPv4 = true;
        }
        else
            isIPv4 = false;
    }
    int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if(sockFD == -1) {
        std::cerr << "Error while creating socket" << std::endl;
        freeaddrinfo(res);
        return -4;
    }
    int bindR = bind(sockFD, p->ai_addr, p->ai_addrlen);
    if(bindR == -1) {
        std::cerr << "Error while binding socket" << std::endl;
        close(sockFD);
        freeaddrinfo(res);
        return -5;
    }
    int listenR = listen(sockFD, backLog);
    if(listenR == -1) {
        std::cerr << "Error while Listening on socket" << std::endl;
        close(sockFD);
        freeaddrinfo(res);
        return -6;
    }
    struct sockaddr_storage client_addr;
    socklen_t client_addr_size = sizeof(client_addr);
    int newFD =
        accept(sockFD, (struct sockaddr *)&client_addr, &client_addr_size);
    if(newFD == -1) {
        std::cerr << "Error while Accepting on socket" << std::endl;
        close(sockFD);
        freeaddrinfo(res);
        return -7;
    }

    std::string input;
    input.reserve(5);
    std::cout << "Enter message to send: ";
    std::cin.ignore();
    std::getline(std::cin, input);
    std::cout << "Sending..." << std::endl;
    auto len        = input.length();
    auto bytes_sent = send(newFD, input.data(), len, 0);
    std::cout << "Input length : " << input.length() << std::endl
              << "Input bytes sent : " << bytes_sent << std::endl;
    close(newFD);
    close(sockFD);
    freeaddrinfo(res);
    return 0;
}

客户端.cpp

// compile as 'g++ client.cpp -o client.app -std=c++14'

// run as : './client.app 0 8080'

#include <iostream>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
    if(argc != 3) {
        std::cerr << "Run program as 'program ipaddress port'" << std::endl;
        return -1;
    }
    auto &ipAddress = argv[1];
    auto &portNum   = argv[2];
    struct addrinfo hints, *res, *p;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags    = AI_PASSIVE;
    int gAddRes = getaddrinfo(ipAddress, portNum, &hints, &res);
    if(gAddRes != 0) {
        std::cerr << gai_strerror(gAddRes) << std::endl;
        return -2;
    }
    std::cout << "Detecting addresses" << std::endl;
    unsigned int numOfAddr = 0;
    char ipStr[INET6_ADDRSTRLEN];
    for(p = res; p != NULL; p = p->ai_next) {
        void *addr;
        std::string ipVer = "IPv0";
        if(p->ai_family == AF_INET) {
            ipVer                    = "IPv4";
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
            addr                     = &(ipv4->sin_addr);
            ++numOfAddr;
        }
        else {
            ipVer                     = "IPv6";
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr                      = &(ipv6->sin6_addr);
            ++numOfAddr;
        }
        inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
        std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
                  << std::endl;
    }
    if(!numOfAddr) {
        std::cerr << "Found no host address to use" << std::endl;
        return -3;
    }
    std::cout << "Enter the number of host address to bind with:" << std::endl;
    unsigned int choice = 0;
    bool madeChoice     = false;
    do {
        std::cin >> choice;
        if(choice > (numOfAddr + 1) || choice < 1) {
            madeChoice = false;
            std::cout << "Wrong choice, try again!" << std::endl;
        }
        else
            madeChoice = true;
    } while(!madeChoice);
    p           = res;
    bool isIPv4 = true;
    if(choice > 1) {
        unsigned int temp = 1;
        while(choice < temp) {
            p = p->ai_next;
            ++temp;
        }
        if(p->ai_family == AF_INET) {
            isIPv4 = true;
        }
        else
            isIPv4 = false;
    }
    int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if(sockFD == -1) {
        std::cerr << "Error while creating socket" << std::endl;
        return -4;
    }
    int connectR = connect(sockFD, p->ai_addr, p->ai_addrlen);
    if(connectR == -1) {
        close(sockFD);
        std::cerr << "Error while connecting socket" << std::endl;
        return -5;
    }
    std::string message;
    message.reserve(5);
    auto len        = message.capacity();
    auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0);
    message[len] = 0;
    close(sockFD);
    freeaddrinfo(res);
    std::cout << "Bytes recieved :" << bytes_recv << std::endl;
    std::cout << message.c_str() << std::endl;
    return 0;
}
  1. 你对此的问题太模糊了,无法提供有用的答案。

  2. 自 C++11 年以来,data()c_str()实际上是一回事。使用哪一个并不重要。编辑:在C++17中,data()将有一个非const重载,返回一个非const char*,因此您无需执行&message.front()即可访问可修改形式的底层缓冲区。 c_str()将保持const.

  3. &message.front()是对的...错了。这是获得对std::string内容的非常量char*的方法。但是message是未初始化的,并且在代码的那个点有size() 0,所以我甚至不确定那行代码是明确定义的行为。与其做reserve(5),不如这样构造你的stringauto message = std::string(5, ' ');然后当你把它传递到recv时,那里实际上会有有效的东西供它覆盖,之后你可以从message读取它。

  4. 是的,这是错误的。您应该将字符串设置为所需的实际大小。我怀疑如果你这样做,你可以通过len而不是len - 1。关于这个主题,您确定您收到的所有内容只有 4 个字节长吗?还是您有意一次只读取 4 个字节?

  5. a( 您不需要将c_str()传递给std::cout<<也超载接受std::string。b( recv返回您收到的字节数。如果该值小于您将message初始化为的大小,则字符串中的其余字符将是垃圾(如果您遵循我的建议,则为 ' ' char s:#3(。收到消息后我会做message.resize(bytes_recv);

你的问题已经用大写字母解决了,但我的 2 美分。拥有自己的发送/接收功能并隐藏复杂性怎么样?

例如,沿着以下行:

ssize_t recv(int sockfd, std::string &buf, size_t len, int flags) {
    buf.resize(len);  // current status unknown -> make it fit
    ssize_t n = ::recv(sockfd, (void *)buf.data(), len, flags);
    buf.resize(n >= 0 ? n : 0);  // take error into account
    return n;
}