为什么我用Boost.ASIO实现的简单HTTP服务器需要睡眠才能正常工作

Why do my simple HTTP server implemented with Boost.ASIO needs sleep to work correctly

本文关键字:常工作 工作 ASIO Boost 实现 服务器 HTTP 简单 为什么      更新时间:2023-10-16

我正在尝试使用Boost.Asio编写一个非常简单的HTTP服务器。以下是代码(几乎与Boost.Acio教程中的示例相同)

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <thread>
#include <chrono>
using boost::asio::ip::tcp;
int main()
{
    try
    {
        boost::asio::io_service io_service;
        tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 12345));
        for (;;)
        {
            tcp::socket socket(io_service);
            acceptor.accept(socket);
            const char message[] = "HTTP/1.0 200 OKrnrn<html><body><i>Hello, world</i></body></html>";
            boost::system::error_code ignored_error;
            boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
        }
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    return 0;
}

当我运行这个示例时,我尝试使用地址为127.0.0.1:12345的Chrome,但它显示"此网页不可用"。但是,如果我从调试器开始,一步一步地,它会正确地显示斜体的"Hello,world"。事实上,如果在写入操作之后添加一行std::this_thread::sleep_for(std::chrono::seconds(1));,它将正常工作。我做错了什么?有没有办法避免这种丑陋的黑客行为?

我使用的是Windows 7上的Visual Studio 2013。编译为64位代码。

我认为问题是HTTP问题,而不是网络问题。

该消息缺少Content-Length标头,因此您的HTTP/1.1客户端(Chrome)可能正在等待服务器关闭连接以标记消息正文的结束,请参阅:rfc2616第4.4节。尝试将消息更改为:

  "HTTP/1.0 200 OKrnContent-Length: 45rnrn<html><body><i>Hello, world</i></body></html>";

我希望我把你的消息正文的Content-Length写对了。)

更新

客户可能需要内容长度的想法可能有一些优点。

相反,您可以添加一个Connection: close标头,这将阻止浏览器保持打开状态(因此"EOF"将被视为响应结束)。


代码看起来很好,在我的盒子(Ubuntu/Opera)上可以不眠不休地工作。

唯一让我觉得…有趣的是

  • 您正在发送终止NUL字符作为响应的一部分:

    $ netcat localhost 12345 | xxd
    0000000: 4854 5450 2f31 2e30 2032 3030 204f 4b0d  HTTP/1.0 200 OK.
    0000010: 0a0d 0a3c 6874 6d6c 3e3c 626f 6479 3e3c  ...<html><body><
    0000020: 693e 4865 6c6c 6f2c 2077 6f72 6c64 3c2f  i>Hello, world</
    0000030: 693e 3c2f 626f 6479 3e3c 2f68 746d 6c3e  i></body></html>
    0000040: 00                                       .
    
  • 您没有收到请求;根据客户的不同,这可能会让客户有点困惑(?)。我不认为这可能是这里的问题,因为很少有宝贵的请求信息可以被拆分成包

更新实际上,如果您的浏览器执行其他请求(如GET /favicon.ico,例如并行请求),则可能会出现/a问题。您的服务器同步处理连接,并且这些连接可能同时到达。请确保侦听套接字在"囤积"中允许至少1个(或10个)连接

这个老答案('08)说默认情况下,backlog在Asio中曾经是5。

我认为这里的问题是您没有等待请求。如果在发送请求之前关闭了套接字,则客户端会将其视为重置连接。您的1秒延迟技巧之所以有效,是因为它可以保持连接打开足够长的时间,以便客户端通过其请求进行发送。

如果我在写入之前添加一个伪请求侦听器,那么您的示例就可以在没有延迟技巧的情况下工作

std::array<char, 8192> ignored_buffer;
socket.async_read_some(boost::asio::buffer(ignored_buffer),
    [](boost::system::error_code ec, std::size_t bytes_transferred) {});