Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
通过套接字C++发送长度和数据_C++_Sockets - Fatal编程技术网

通过套接字C++发送长度和数据

通过套接字C++发送长度和数据,c++,sockets,C++,Sockets,所以我目前正在开发一个使用socket的应用程序,因为现在一切正常,我只想知道是否有更有效的方法来发送数据长度和数据,而不是发送两次 i、 e:一串发送所有内容 void SendPackage(const SOCKET sock, const std::string package) { int length = package.lenth(); send(sock, std::to_string(length).c_str(), 10, 0); //Buffer l

所以我目前正在开发一个使用socket的应用程序,因为现在一切正常,我只想知道是否有更有效的方法来发送数据长度和数据,而不是发送两次

i、 e:一串发送所有内容

void SendPackage(const SOCKET sock, const std::string package)
{
    int length = package.lenth();

    send(sock, std::to_string(length).c_str(), 10, 0);     //Buffer length is 10 assuming data length
    send(sock, package.c_str(), length, 0);        //will never be greater than 9,999,999,999
}

void ReceivePackage(const SOCKET sock, std::string &package, int bufferLength)
{
    std::vector<char> recvBuffer(10);
    int length, bytesProcessed;

    recv(sock, &recvBuffer[0], 10, 0); //Receiving length
    length = atoi(&recvBuffer[0]);
    recvBuffer.resize(length);

    for (int i = 0; i < length; i += bytesProcessed)
    {
        bytesProcessed = recv(sock, &recvBuffer[0] + i, bufferLength, 0);
        if (bytesProcessed < 0) break;
    }

    package = &recvBuffer[0];
}
这是UB。您告诉它数据有10个字节,但这是不正确的。除此之外,不需要将int转换为字符串,然后发送字符串。刚刚发送了int,为了避免平台不兼容,我建议使用固定大小的类型:

std::int32_t length = package.length();
send(sock, &length, sizeof(length), 0);
在接收端,您必须确保实际等待所有字节到达。您不能简单地调用recv并假设它提供您所需的所有字节。您需要在一个循环中调用它,并检查它的返回值,它告诉您得到了多少字节。要接收长度,您需要将适当数量的字节读入缓冲区,然后将该缓冲区重新解释为您选择的类型,例如std::int32\t

此外,您应该确保int的endianness是正确的。对网络数据使用big-endian是一个事实上的标准,但最终这取决于您。不是标准afaik的一部分,但在通用平台上可用的是帮助函数htonl和ntohl,它们代表主机到网络,long和网络到主机,long。它们获取并返回一个std::int32\t,您可以简单地使用它们来确保endianness对双方都有效

发件人:

std::int32_t length = htonl(package.length());
send(sock, &length, sizeof(length), 0);
接收人:

// after successfully reading all bytes for the length value into an adequately sized buffer:
std::int32_t length = ntohl(*reinterpret_cast<std::int32_t*>(buffer));

就套接字接口而言,您不会发送两次。套接字只是通过一个或多个send调用将所有内容放在一端,并在自己的时间内发送出去。我假设第一次调用时不会超过套接字缓冲区,这是一个相当安全的假设。类似地,您不需要将recv调用与send调用相匹配。你可以一次收回全部,然后稍后再拆分,但实际上,这样做没有什么大的好处

因此,无论您是先打包字符串然后发送,还是调用send两次,这都是一件非常麻烦的事情。创建长度连接到起始的新字符串所需的时间和RAM不会比只调用send两次更有效

但是,您肯定应该将长度作为输入的int发送,而不是作为字符串发送。这样,它将始终占用sizeofint字节。缓冲区长度10通常不为真,并导致读取字符串的结尾。例如:

send(sock, &length, sizeof(length), 0);


与此相关的是,如果您学习,只发送编码后的长度,然后解码,这会容易得多。在TCP中,发送和接收之间没有1:1的对应关系。您需要一直调用recv,直到获得预期的字节或超时;真的编译吗?如果字节处理>0,则中断;如果bytesProcessed加上@stark所说的,TCP就是一个字节流。虽然您最终将获得您发送的所有字节,但它们的输入速度以及获取它们所需的读取调用数量根本无法保证。这实际上是一个有点复杂的协议,因为您必须不断地读取缓冲区并在正确的时间解析它们。MSG_WAITALL标志可能会对此有所帮助。同样,长度在发送方和接收方可能有不同的大小。同样,不同的CPU架构可能在内存中以不同的字节顺序存储int。如果可能的话,我会坚持发短信。而且你可能会遇到符号可移植性问题,所以你真的应该发送未签名的数字。嘿,听起来像是人们在解决自己的问题,而不是OP的问题。显然,SendPackage和ReceivePackage在同一个体系结构上运行,因此endianess是不相关的。显然,问题不在于recv返回时没有数据,因此等待是不相关的,很可能这是在我们可以看到的代码之外处理的。OP的问题得到了回答,作为奖励,我在我们可以看到的代码上添加了一些适合观众的提示。我们都必须从某个地方开始。Endian和不同大小的整数可能会因编译器和编译器选项的不同而有所不同,即使目标体系结构不是这个问题的旁白,但它们仍然需要注意。我不会对他们投反对票,除非发生了一些令人震惊的虐待事件,或者问题是专门针对他们的。不过,recv可能无法在一次拍摄中获得全部长度,这是一个致命的缺陷,并且具有相对简单的分辨率。我想不出一个不解决这个问题的好理由。你应该使用unsigned int来保持可移植性。此外,您不能仅从任意字节缓冲区将转换重新解释为std::uint32。最好将STD::It32t转换成一个字符,并直接读入。@ Galik不太清楚为什么要考虑使用一个无符号类型更便携。特别是当我们认为接收端可以用另一种语言编写时
不支持无符号类型,如Java。选角顺序似乎更多的是关于个人偏好,但我很想知道是否有技术上的原因,为什么它应该更好。好的,是的,总的来说,我认为你是对的。措辞确实表明C标准在所有方面都应该是明确的——不仅是声明,而且是语义上的强化。
send(sock, &length, sizeof(length), 0);
recv(sock, &length, sizeof(length), 0); //Receiving length