C 套接字 : recv() blocks when all data is downloaded

标签 c unix winsock2

我正在为 Windows 和 Linux 上的 Berkley 套接字编写一个包装器。测试程序在这里出现问题:

char buf[BUFSIZE];
int res = 0;
while((res = NetRecv(sock, buf, BUFSIZE, 0)) > 0) // 'NetRecv' is pointing to 'recv'
{
    buf[res-1] = '\0';
    printf("%s", buf);
}

响应是对网页内容的 HTTP-Get 请求。套接字正在流式传输。

“NetRecv”已正确初始化 - 也就是说,函数指针不存在类型不匹配,我已检查过。

所以,Windows 版本运行完美,Linux 版本在阅读完所有页面后卡住了。也就是说,前一个到最后一个“NetRecv”调用接受响应的最后一个 block ,将其输出,并且下一个(最后一个)调用将被阻塞。关闭终端会导致“SIGHUP”信号。 看起来 Linux 版本只是没有意识到它收到了最后一 block 数据并等待更多数据。

这是应该的吗?那么不明白,出于什么原因,存在阻塞调用的可能性。 现在,我当然可以进行非阻塞调用并使用“select”,但我真的必须这样做吗?

提前致谢)

编辑:最小工作示例(省略所有检查,网络函数是标准函数,也经过测试):

int sock = socket(AF_INET, SOCK_STREAM, 0);

// Here getting good IP address of google.com - no problem here
char serv_ip[IPADDR_BUFSIZE];
GetHostAddrByName(AF_INET, "www.google.com", serv_ip, IPADDR_BUFSIZE);
//                 ip ver        site        out buf   out buf size
// The routine above is made with 'getaddrinfo', to be precise

printf("Current IP of '%s' is '%s'.\n", SERV_URL, serv_ip);

// Copying IP string to address struct
struct sockaddr_in addr;
NetIpFromStr(AF_INET, serv_ip, &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = NetHtons(80);

connect(sock, (const struct sockaddr*)&addr, sizeof(addr));

const char* msg = "GET / HTTP/1.1\r\n\r\n";
send(sock, msg, strlen(msg), 0);

char buf[BUFSIZE];
int res = 0;
while((res = recv(sock, buf, BUFSIZE-1, 0)) > 0)
{
    buf[res] = '\0';
    printf("%s", buf);
}

编辑2:重要通知:当读取所有数据时,Windows 版本也会阻止调用。关闭终端不会使程序崩溃,就像 Linux 中那样。因此,整个问题是这样的:如何实现所有数据都被读取?

最佳答案

问题是您在循环中盲目地从套接字读取数据,直到发生错误。一旦收到完整的响应,您将返回套接字并继续读取,然后阻塞,因为没有任何内容可供读取。此时可能发生的唯一错误是连接关闭(或丢失)时,服务器可能不会执行此操作,因为您正在发送 HTTP 1.1 请求,其中 keep-alive 是 1.1 的默认行为(请参阅 RFC 2616 Section 8.1 Persistent Connections )

正确的解决方案是解析 HTTP 响应,并在到达响应末尾时停止从套接字读取,而不是简单地依赖服务器关闭套接字。阅读 RFC 2616 Section 4.4 Message Length 了解如何检测何时到达响应末尾。请勿阅读超出回复所示内容的内容!一旦停止阅读,您就可以决定是否关闭套接字一端,或将其重新用于新请求。

查看 this pseudo code,了解您需要使用的解析和读取逻辑的类型。

此外,您的 HTTP 请求格式错误,因为您没有发送必需的 Host header ,因此无论如何,您始终会收到来自任何服务器的 400 Bad Request 响应HTTP 1.1 兼容服务器:

const char* msg = "GET / HTTP/1.1\r\n"
                  "Host: www.google.com\r\n" // <-- add this!
                  "\r\n";

关于C 套接字 : recv() blocks when all data is downloaded,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51917021/

相关文章:

linux - 能否合并多个 .gz 文件,以便将它们提取到一个文件中?

shell - 在 linux bourne shell : How to count the occurrences of a specific word in a file

c++ - 强制 WSARecv 重叠

tcp - 监听应用程序 (winsock2) 对端口扫描 (Syn Scan) 的行为

c - 希望 execve() 运行的可执行文件使用我预加载的库

c - 当我尝试在字符串末尾添加 NULL 终止符时,为什么会出现段错误?

计算前缀表达式unix

c - 以二进制形式打开图像文件,将图像存储为字节字符串,保存图像 - 可以用纯 C 语言吗?

c - scanf 成一个字符串数组

c - WSAStringToAddress 错误 10022/从控制台参数读取 IPv6