读取调用使执行速度减慢 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
字段。
其次,由于您没有关闭连接,也没有在您这边关闭它,并且由于您使用的是 HTTP
的 1.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
所以当您收到两个连续的 CRLF
s 时,您应该读取 14 个字节并停在那里,以便从该点继续读取对下一个请求的响应。
所以,我在 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
字段。
其次,由于您没有关闭连接,也没有在您这边关闭它,并且由于您使用的是 HTTP
的 1.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
所以当您收到两个连续的 CRLF
s 时,您应该读取 14 个字节并停在那里,以便从该点继续读取对下一个请求的响应。