【问题标题】:C Sockets: recv() blocks when all data is downloaded [duplicate]C套接字:下载所有数据时recv()阻塞[重复]
【发布时间】:2019-01-25 18:33:35
【问题描述】:

我正在为 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'调用的前一个接收响应的最后一个块,输出它,并且下一个(最后一个)调用只是阻塞。关闭终端会导致“SIGHUP”信号。 看起来 Linux 版本只是没有意识到,它收到了最后一块数据并等待更多。

它应该是这样吗?那不明白,为什么会有阻塞调用的可能性。 现在,我当然可以进行非阻塞调用并使用“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 中发生的那样。因此,整个问题是这样的:如何实现读取所有数据?

【问题讨论】:

  • 请提供minimal complete and verifiable example 以增加您获得答案的机会。
  • buf[res-1] = '\0';覆盖收到的最后一个字符。使用 'buf[res] = '\0';'并将缓冲区超大一; 'char buf[1+BUFSIZE];',或简称:'NetRecv(sock, buf, BUFSIZE-1, 0)'。
  • HTTP 的哪个版本?如果连接没有被服务器关闭,并且你没有解析 header 来获取 content-length,你将不知道你什么时候收到了所有的数据。
  • int res 应该是ssize_t res
  • Enen 更好 - 在网络代码中完全不要依赖 NUL 终止符:)

标签: c unix winsock2


【解决方案1】:

问题是您在循环中盲目地从套接字读取,直到发生错误。收到整个响应后,您返回套接字并继续阅读,然后由于没有什么可阅读的内容而阻塞。此时可能发生的唯一错误是连接关闭(或丢失)时,服务器可能不会这样做,因为您正在发送 HTTP 1.1 请求,其中 keep-alive 是 1.1 的默认行为(请参阅@987654321 @)

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

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

此外,您的 HTTP 请求格式不正确,因为您没有发送 必需 Host header,所以无论如何,您总是会收到来自任何符合 HTTP 1.1 的服务器的 400 Bad Request 响应:

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

【讨论】:

    【解决方案2】:

    解决方案是在 Windows 和 Linux 中关闭套接字以进行读取:

    // after sending a request:
    shutdown(sock, SD_SEND);     // or 'SHUT_WR' in Linux
    
    // now read loop
    

    奇怪的是,Winsock 教程中也调用了“shutdown”,但我认为这是不必要的。

    【讨论】:

      猜你喜欢
      • 2011-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 2016-06-04
      相关资源
      最近更新 更多