读取调用使执行速度减慢 1 分钟?

Read call is slowing down execution by 1 minute?

所以,我在 C 中有一个尝试发出 HTTP 请求的简单程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void err(const char *msg) {
    fprintf(stderr, "[ERROR] %s\n", msg);
    exit(1);
}

int main(int argc,char *argv[])
{
    char *host;
    int port;
    char *request;

    host = "api.ipify.org";
    port = 80;
    request = "GET / HTTP/1.1\r\nHost: api.ipify.org\r\n\r\n";

    struct hostent *server;
    struct sockaddr_in serv_addr;
    int sockfd, bytes, sent, received, total;
    char response[4096];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) err("Couldn't open socket");

    server = gethostbyname(host);
    if (server == NULL) err("No such host");

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);

    /* connect the socket */
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) err("Couldn't connect");

    /* send the request */
    total = strlen(request);
    sent = 0;
    while (sent < total) {
        bytes = write(sockfd, request + sent, total - sent);
        if (bytes < 0) err("Couldn't send request");
        if (bytes == 0) break;
        sent += bytes;
    }

    /* receive the response */
    memset(response, 0, sizeof(response));
    total = sizeof(response) - 1;
    received = 0;
    while (received < total) {
        bytes = read(sockfd, response + received, total - received);
        if (bytes < 0) err("Couldn't receive response");
        if (bytes == 0) break;
        received += bytes;
    }

    /*
     * if the number of received bytes is the total size of the
     * array then we have run out of space to store the response
     * and it hasn't all arrived yet - so that's a bad thing
     */
    if (received == total) err("Couldn't store response");

    /* close the socket */
    close(sockfd);

    /* process response */
    printf("Response:\n%s\n",response);

    return 0;
}

但是,当我编译它并 运行 它时,它做了它应该做的,但它需要很长时间。 运行 它与 time 命令一起显示它需要 ~1m 0.3s 来执行。如果我注释掉 read 函数调用,执行时间会回到 0.3 秒。这意味着由于某种原因,它使我的程序延迟了整整 1 分钟。

我试过将 printf 放在 main 函数的最开始,但直到 1 分钟后才调用它。

为什么整个主函数被一个函数延迟,我该如何解决这个问题?

首先,您应该在 header 中包含一个字段,如下所示:

Content-length: 0\r\n

请求确实是 GET 请求,GET 请求不包含 body 也是正确的。但是这个要求不是强制性的,如果你愿意,你可以发送一个body(而服务器需要忽略它,对于GET请求)它是不禁止为 get 发送非空的 body。您应该发送一个 Content-length 字段。

其次,由于您没有关闭连接,也没有在您这边关闭它,并且由于您使用的是 HTTP1.1 版本,服务器正在等待更多内容come(一个新的请求,或者第一个GET方法的body)。阅读 RFC 文档 RFC2616 以获取有关 1.1 连接模型的信息。

更改以下行后

    while (received < total) {
        bytes = read(sockfd, response + received, total - received);
        if (bytes < 0) err("Couldn't receive response");
        if (bytes == 0) break;
        received += bytes;
    }

进入

    while (received < total) {
        bytes = read(sockfd, response + received, total - received);
        if (bytes < 0) err("Couldn't receive response");
        if (bytes == 0) break;
        write(1, response + received, bytes); /* <<< this line added */
        received += bytes;
    }

看看我们收到了什么, 我能够观察到服务器向您发送了一个简短的响应:

$ http
HTTP/1.1 200 OK
Server: Cowboy
Connection: keep-alive
Content-Type: text/plain
Vary: Origin
Date: Mon, 21 Feb 2022 21:39:22 GMT
Content-Length: 14
Via: 1.1 vegur

82.181.193.234_    (<-- cursor remains there, as the last _ char)

并且确实仍在等待下一个请求(如 HTTP/1.1 中指定的那样,用于保持连接)最后,服务器超时,并关闭连接。

您可以切换到HTTP/1.0,因为HTTP/1.0不支持保持活动连接,并且会在请求后立即看到服务器如何关闭连接。为了与服务器进行良好的交互,您应该在收到响应时解析 header,然后解释服务器发送的 Content-length:(或块,如果 Content-encoding: chunked content) 以检测响应何时完成。原因与请求相同。它被另一方使用,以便为下一个请求重用连接,并检测 request/response 结束的位置。在这种情况下,服务器发送一个

Content-Length: 14\r\n

所以当您收到两个连续的 CRLFs 时,您应该读取 14 个字节并停在那里,以便从该点继续读取对下一个请求的响应。