C 中的 HTTP GET 服务器,长时间等待 (TTFB)
HTTP GET server in C, long waiting (TTFB)
我正在尝试构建一些 http 服务器。我的程序很简单:我收到 http 请求,等待它结束,然后发送我的响应。
不幸的是,在接收浏览器 header 和从 recv 接收空缓冲区之间有几秒钟的间隔,以检测到结束,现在我可以发送响应了。
socklen_t clientAddressLength;
int clientSocket = accept(socket, (struct sockaddr *)&socketAddress, &clientAddressLength);
if (clientSocket < 0) {ESP_LOGI(TAG, "failed 3!"); goto network_task_finish;}
if (setsockopt(clientSocket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
ESP_LOGI(TAG, "Cannot Set SO_SNDTIMEO for socket");
if (setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)
ESP_LOGI(TAG, "Cannot Set SO_RCVTIMEO for socket");
while (1) {
ret = recv(clientSocket, recv_buf, NETWORK_RECV_BUF_LEN - 1, 0);
if (ret <= 0) break;
if (ret <= 2 && *recv_buf == '\r') break;
if (ret < NETWORK_RECV_BUF_LEN)
recv_buf[ret] = '[=10=]';
ESP_LOGI(TAG, "RECV");
if (strstr(recv_buf," HTTP/")) {
char *ptr = recv_buf;
if (strncmp(recv_buf, "GET ", 4) == 0)
ptr += 4;
else if (strncmp(recv_buf, "POST ", 4) == 0)
ptr += 5;
else
continue;
ESP_LOGI(TAG, "HEADER!");
}
}
ESP_LOGI(TAG, "RESPONSE");
send(clientSocket, "HTTP/1.1 200 OK\r\n", 17, 0);
send(clientSocket, "Content-Type: text/html\r\n", 25, 0);
send(clientSocket, "Connection: close\r\n", 19, 0);
send(clientSocket, "\r\n", 2, 0);
send(clientSocket, "WORKS", 5, 0);
ESP_LOGI(TAG, "SEND");
日志:
server: RECV
server: HEADER!
... long time ...
server: RESPONSE
server: SEND
处理请求的准确方法是什么?
也许我不必等待它的结束或者我可以通过其他方式检测到 header 结束
问题似乎不是您不知道请求如何结束(只有一个空行),而是您如何检查。
你必须记住,HTTP 是建立在 TCP 之上的,TCP 是一种 流 协议,没有消息边界或 fixed-size 数据包。
这意味着您可以在一次 recv
调用、两次、三次或更多次调用中获得整个请求。如果单个 recv
调用只给您 只是 空行而没有其他任何内容,您只会检测到 header 的结尾,这是不太可能发生的事件。这也可能给你一个 false end-of-request,因为换行符可能在 header.
的中间
相反,我建议您重新设计接收代码以在循环中调用 recv
并附加到缓冲区。然后在循环中检查最后 四个 字节是否是双换行序列 (\r\n\r\n
)。如果是,那么 你已经找到了请求的结尾,可以打破循环并继续处理请求并实际检查需要什么(GET
或 POST
等)。
我正在尝试构建一些 http 服务器。我的程序很简单:我收到 http 请求,等待它结束,然后发送我的响应。 不幸的是,在接收浏览器 header 和从 recv 接收空缓冲区之间有几秒钟的间隔,以检测到结束,现在我可以发送响应了。
socklen_t clientAddressLength;
int clientSocket = accept(socket, (struct sockaddr *)&socketAddress, &clientAddressLength);
if (clientSocket < 0) {ESP_LOGI(TAG, "failed 3!"); goto network_task_finish;}
if (setsockopt(clientSocket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
ESP_LOGI(TAG, "Cannot Set SO_SNDTIMEO for socket");
if (setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)
ESP_LOGI(TAG, "Cannot Set SO_RCVTIMEO for socket");
while (1) {
ret = recv(clientSocket, recv_buf, NETWORK_RECV_BUF_LEN - 1, 0);
if (ret <= 0) break;
if (ret <= 2 && *recv_buf == '\r') break;
if (ret < NETWORK_RECV_BUF_LEN)
recv_buf[ret] = '[=10=]';
ESP_LOGI(TAG, "RECV");
if (strstr(recv_buf," HTTP/")) {
char *ptr = recv_buf;
if (strncmp(recv_buf, "GET ", 4) == 0)
ptr += 4;
else if (strncmp(recv_buf, "POST ", 4) == 0)
ptr += 5;
else
continue;
ESP_LOGI(TAG, "HEADER!");
}
}
ESP_LOGI(TAG, "RESPONSE");
send(clientSocket, "HTTP/1.1 200 OK\r\n", 17, 0);
send(clientSocket, "Content-Type: text/html\r\n", 25, 0);
send(clientSocket, "Connection: close\r\n", 19, 0);
send(clientSocket, "\r\n", 2, 0);
send(clientSocket, "WORKS", 5, 0);
ESP_LOGI(TAG, "SEND");
日志:
server: RECV
server: HEADER!
... long time ...
server: RESPONSE
server: SEND
处理请求的准确方法是什么? 也许我不必等待它的结束或者我可以通过其他方式检测到 header 结束
问题似乎不是您不知道请求如何结束(只有一个空行),而是您如何检查。
你必须记住,HTTP 是建立在 TCP 之上的,TCP 是一种 流 协议,没有消息边界或 fixed-size 数据包。
这意味着您可以在一次 recv
调用、两次、三次或更多次调用中获得整个请求。如果单个 recv
调用只给您 只是 空行而没有其他任何内容,您只会检测到 header 的结尾,这是不太可能发生的事件。这也可能给你一个 false end-of-request,因为换行符可能在 header.
相反,我建议您重新设计接收代码以在循环中调用 recv
并附加到缓冲区。然后在循环中检查最后 四个 字节是否是双换行序列 (\r\n\r\n
)。如果是,那么 你已经找到了请求的结尾,可以打破循环并继续处理请求并实际检查需要什么(GET
或 POST
等)。