所以我目前正在使用套接字的应用程序上工作,现在一切正常,我只想知道是否存在一种更有效的方式来发送数据长度和数据而不是发送两次。
即:将所有内容发送到一个字符串中。
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];
}
答案 0 :(得分:0)
send(sock, std::to_string(length).c_str(), 10, 0);
这是UB。您告诉它数据有10个字节,但这是不正确的。除此之外,无需将int转换为字符串然后发送该字符串。刚发送完int,为避免平台不兼容,我建议使用固定大小的类型:
std::int32_t length = package.length();
send(sock, &length, sizeof(length), 0);
在接收端,您必须确保实际上等待所有字节在那里。您不能简单地调用recv
并假定它为您提供了所需的所有字节。您需要在循环中调用它并检查它的返回值,该值告诉您获得了多少字节。要接收length
,您需要将适当数量的字节读入缓冲区,然后将该缓冲区重新解释为您选择的类型(例如std::int32_t
。
此外,您还应该确保int的字节序正确。使用big-endian来存储网络数据是事实上的标准,但是最终由您自己决定。帮助程序功能htonl
和ntohl
不是标准(afaik)的一部分,但在通用平台上可用,它们分别表示“主机到网络,很长”和“网络到主机,很长”。它们采用std::int32_t
并返回,您可以简单地使用它们来确保字节序对双方都有效。
发件人:
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));
答案 1 :(得分:-2)
就套接字接口而言,您不会发送两次。套接字只是通过一个或多个send
调用将一端填充的所有内容发送到自己的时间(我假设您在第一次调用时不超过套接字缓冲区,这是相当合理的)安全假设)。同样,您无需将recv
调用与send
调用进行匹配。您可以一次recv
整批交易,然后再将其分解,但实际上并没有太大收获。
因此,无论您是先打包字符串然后发送字符串还是两次致电send
,这实际上都是一种洗礼。创建长度与开头串联的新字符串所需的时间和RAM不会比仅两次调用send
更有效率。
但是,您绝对应该按输入的int
发送长度,而不是字符串。这样,它将始终占据sizeof(int)
个字节。 10
的缓冲区长度通常不正确,并导致读取字符串的末尾。例如:
send(sock, &length, sizeof(length), 0);
和
recv(sock, &length, sizeof(length), 0); //Receiving length