我正在尝试构建一些http服务器。我的程序很简单:我收到http请求,等待结束并在此之后发送我的回复。 不幸的是,在接收浏览器标头和从 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] = '\0';
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
处理请求的准确方法是什么? 也许我不必等待它的结束,或者我可以通过其他方式检测标题
答案 0 :(得分:2)
问题似乎并不是你不知道请求是如何结束的(只有一个空白行),而是你检查的方式。
您必须记住,HTTP是基于TCP构建的,而TCP是流式协议,没有消息边界或固定大小的数据包。
这意味着您可以通过一次recv
次呼叫,或两次,三次或更多次呼叫来获取整个请求。如果单个recv
调用为您提供 空行而没有其他内容,那么您只检测标题的结尾,这是一个非常不可能发生的事件。这也可能会为您提供 false 请求结束,因为换行符可能位于标题的中间。
相反,我建议您重新设计接收代码,以便在循环中调用recv
并附加到缓冲区。然后在循环中检查最后的四个字节是否是双换行序列(\r\n\r\n
)。如果是,那么您已找到请求的结束并可以中断循环并继续处理请求并实际检查所需内容(GET
或POST
等)。