Socket recv()挂起带有MSG_WAITALL的大消息
我有一个从服务器读取大文件的应用程序,它经常挂起在特定的机器上。它已经在RHEL5.2下成功运行了很长一段时间。我们最近升级到了RHEL6.1,它现在定期挂起 我已经创建了一个测试应用程序来重现这个问题。它挂起的次数约为100次中的98次Socket recv()挂起带有MSG_WAITALL的大消息,c,linux,sockets,networking,tcp,C,Linux,Sockets,Networking,Tcp,我有一个从服务器读取大文件的应用程序,它经常挂起在特定的机器上。它已经在RHEL5.2下成功运行了很长一段时间。我们最近升级到了RHEL6.1,它现在定期挂起 我已经创建了一个测试应用程序来重现这个问题。它挂起的次数约为100次中的98次 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/param.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
int mFD = 0;
void open_socket()
{
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
if (getaddrinfo("localhost", "60000", &hints, &res) != 0)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
mFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (mFD == -1)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
if (connect(mFD, res->ai_addr, res->ai_addrlen) < 0)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
freeaddrinfo(res);
}
void read_message(int size, void* data)
{
int bytesLeft = size;
int numRd = 0;
while (bytesLeft != 0)
{
fprintf(stderr, "reading %d bytes\n", bytesLeft);
/* Replacing MSG_WAITALL with 0 works fine */
int num = recv(mFD, data, bytesLeft, MSG_WAITALL);
if (num == 0)
{
break;
}
else if (num < 0 && errno != EINTR)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
else if (num > 0)
{
numRd += num;
data += num;
bytesLeft -= num;
fprintf(stderr, "read %d bytes - remaining = %d\n", num, bytesLeft);
}
}
fprintf(stderr, "read total of %d bytes\n", numRd);
}
int main(int argc, char **argv)
{
open_socket();
uint32_t raw_len = atoi(argv[1]);
char raw[raw_len];
read_message(raw_len, raw);
return 0;
}
这将看到6000000字节发送到测试应用程序,该应用程序尝试使用对recv()的单个调用读取数据
我希望听到任何关于我可能做错了什么的建议,或者任何进一步调试问题的方法。MSG_WAITALL
应该阻止,直到收到所有数据。从:
此标志请求操作块停止,直到完全请求得到满足
但是,网络堆栈中的缓冲区可能不够大,无法包含所有内容,这就是服务器上出现错误消息的原因。客户端网络堆栈根本无法容纳那么多数据
解决方案是增加缓冲区大小(sorcvbuf
选项到setsockopt
),将消息拆分为较小的片段,或者接收较小的片段,将其放入自己的缓冲区。最后是我的建议
编辑:我在您的代码中看到,您已经按照我的建议执行了(使用自己的缓冲读取较小的数据块),所以只需删除MSG_WAITALL
标志,它就可以工作了
哦,当
recv
返回零时,这意味着另一端已经关闭了连接,您也应该这样做。考虑以下两个可能的规则:
> gcc socket_test.c -o socket_test
> perl -e 'for (1..6000000){ print "a" }' | nc -l 60000
> ./socket_test 6000000