从套接字读取单个 recv() 方法期间的所有数据
Read all data during single recv() method from a socket
我正在尝试使用套接字编程连续读取数据。我使用 recv() 在套接字上接收数据。我将它存储在缓冲区中。 recv() returns 读取的字节数。以下是片段:
while (true) {
try {
char buff[2048];
int bytes = recv(sockfd, buff, 2048, 0);
buff[bytes] = '[=10=]';
cout << strlen(buff) << endl;
cout << bytes << endl;
cout << "-------" << endl;
} catch (const char *e) {
}
}
代码输出如下:
1
1204
-------
1
1390
-------
1
25
-------
1
1204
-------
通过 recv() 方法接收到的字节数是正确的,但我无法读取确切的数据数。如何读取单个 recv() 方法期间捕获的所有数据?
您需要像文档中那样将正确的标志传递给 recv。基于:https://man7.org/linux/man-pages/man2/recv.2.html
有标志MSG_WAITALL(自Linux 2.2)可以做你想做的事。
调用看起来像:
int bytes = recv(sockfd, buff, 2048, MSG_WAITALL);
除非您确定只收到文本,否则通过 strlen 检查接收字节的长度是错误的。
您可以创建一个 std::string_view
指向读取的数据。
while (true) {
char buff[2048];
int bytes = recv(sockfd, buff, 2048, 0);
if (bytes >= 0) {
std::string_view read { buff, bytes };
std::cout << read << std::endl;
std::cout << "-------" << std::endl;
} // else?
}
函数 strlen
会将 buff
的内容视为 null-terminated 字符串,return 该字符串的长度。
行中
buff[bytes] = '[=10=]';
您在数据末尾写了一个空终止符。这样,您就确保了数据是 null-terminated.
但是,recv
读取的字节可能包含值为 0
的字节。函数 strlen
将无法区分这样的字节和实际的终止空字符。它只是 return 第一个字节位置的索引,值为 0
.
这就是表达式strlen(buff)
与实际数据长度不对应的原因。
因此,您不应在二进制数据上使用函数 strlen
。该函数仅适用于文本数据,不能包含值为 0
的字符,但标记字符串结尾的字符除外。
而不是使用 strlen
来确定数据的长度,您应该只使用由 recv
编辑的值 return(您已将其存储在变量 bytes
).只要记住这个值,就永远知道长度,所以不需要用终止空字符来标记数据的结尾。
只要recv
return输入正值(负数表示失败),你会发现数组buff
中的所有数据,return recv
的值将指定数据的长度。例如,如果您想要打印在 recv
函数调用中收到的所有数据,您可以在 recv
函数调用之后立即编写以下内容:
for ( int i = 0; i < bytes; i++ )
cout << hex << setw(2) << setfill( '0' ) << static_cast<int>(buff[i]) << ' ';
请注意,您必须在程序的开头添加 #include <iomanip>
才能使该行生效。
我正在尝试使用套接字编程连续读取数据。我使用 recv() 在套接字上接收数据。我将它存储在缓冲区中。 recv() returns 读取的字节数。以下是片段:
while (true) {
try {
char buff[2048];
int bytes = recv(sockfd, buff, 2048, 0);
buff[bytes] = '[=10=]';
cout << strlen(buff) << endl;
cout << bytes << endl;
cout << "-------" << endl;
} catch (const char *e) {
}
}
代码输出如下:
1
1204
-------
1
1390
-------
1
25
-------
1
1204
-------
通过 recv() 方法接收到的字节数是正确的,但我无法读取确切的数据数。如何读取单个 recv() 方法期间捕获的所有数据?
您需要像文档中那样将正确的标志传递给 recv。基于:https://man7.org/linux/man-pages/man2/recv.2.html 有标志MSG_WAITALL(自Linux 2.2)可以做你想做的事。
调用看起来像:
int bytes = recv(sockfd, buff, 2048, MSG_WAITALL);
除非您确定只收到文本,否则通过 strlen 检查接收字节的长度是错误的。
您可以创建一个 std::string_view
指向读取的数据。
while (true) {
char buff[2048];
int bytes = recv(sockfd, buff, 2048, 0);
if (bytes >= 0) {
std::string_view read { buff, bytes };
std::cout << read << std::endl;
std::cout << "-------" << std::endl;
} // else?
}
函数 strlen
会将 buff
的内容视为 null-terminated 字符串,return 该字符串的长度。
行中
buff[bytes] = '[=10=]';
您在数据末尾写了一个空终止符。这样,您就确保了数据是 null-terminated.
但是,recv
读取的字节可能包含值为 0
的字节。函数 strlen
将无法区分这样的字节和实际的终止空字符。它只是 return 第一个字节位置的索引,值为 0
.
这就是表达式strlen(buff)
与实际数据长度不对应的原因。
因此,您不应在二进制数据上使用函数 strlen
。该函数仅适用于文本数据,不能包含值为 0
的字符,但标记字符串结尾的字符除外。
而不是使用 strlen
来确定数据的长度,您应该只使用由 recv
编辑的值 return(您已将其存储在变量 bytes
).只要记住这个值,就永远知道长度,所以不需要用终止空字符来标记数据的结尾。
只要recv
return输入正值(负数表示失败),你会发现数组buff
中的所有数据,return recv
的值将指定数据的长度。例如,如果您想要打印在 recv
函数调用中收到的所有数据,您可以在 recv
函数调用之后立即编写以下内容:
for ( int i = 0; i < bytes; i++ )
cout << hex << setw(2) << setfill( '0' ) << static_cast<int>(buff[i]) << ' ';
请注意,您必须在程序的开头添加 #include <iomanip>
才能使该行生效。