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

时间:2016-02-16 17:18:54

标签: c++ sockets unix c++11 networking

// 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我有两个疑问。到目前为止,我的初步猜测正如预期的那样。我在代码中将它们标记在上面。

  1. cin.ignore() vs cin.clear() + cin.sync()
  2. std::string.data() vs std::string.c_str()
  3. 我应该使用哪个?我甚至不确定其中任何一个的差异,我也不知道他们是否为我的问题做出了贡献。

    // 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)=====
    

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

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

      server.cpp

           

      &front

           

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

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

      client.cpp

           

      #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; }

           

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

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

2 个答案:

答案 0 :(得分:3)

  1. 您的问题太过模糊,无法提供有用的答案。

  2. 自从C ++ 11以来,
  3. data()c_str()effectively the same thing。你使用哪一个并不重要。 编辑In C++17, data() will have a non-const overload that returns a non-const char*,因此您无需&message.front()访问可修改形式的底层缓冲区。 c_str()将保持const

  4. &message.front()是对的......错了。这是获取char*内容的非常量std::string的方法。但是message未初始化,并且代码中的size() 0 reserve(5),因此我甚至不确定代码行是否是明确定义的行为。而不是string我会像auto message = std::string(5, ' ');那样构建你的recv然后当你将它传递到message时,那里实际上会有有效的东西可以覆盖它,你和#39;之后可以从len阅读。

  5. 是的,这是错误的。您应该将字符串设置为您需要的实际大小。我怀疑如果你这样做,你可以传递len - 1而不是c_str()。关于这个话题,你确定你所收到的一切只有4个字节吗?或者你是否故意一次只读4个字节?

  6. a)您不需要将std::cout传递给<<std::string已超载以接受recv。 b)message返回您收到的字节数。如果该值小于您将' '初始化为的值,则字符串中的其余字符将为垃圾(或char message.resize(bytes_recv); s,如果您遵循我的建议:#3 )。收到邮件后我会<?xml version="1.0" encoding="UTF-8" ?> <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"> <xsl:key name="version" match="foo" use="@version"/> <xsl:key name="item" match="foo/bar/item" use="@key"/> <xsl:variable name="vers2" select="key('version', '2.0.0')"/> <xsl:variable name="k1" select="key('item', 'k1', $vers2)"/> <xsl:variable name="data1" select="$k1/data[starts-with(., 'abc')]"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="$data1| $k1/text()[not(normalize-space())][some $d in $data1 satisfies ($d is following-sibling::node()[1])]"/> </xsl:transform>

答案 1 :(得分:2)

您的问题已通过大写字母解决,但我的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;
}