【问题标题】:Why do my simple HTTP server implemented with Boost.ASIO needs sleep to work correctly为什么我用 Boost.ASIO 实现的简单 HTTP 服务器需要睡眠才能正常工作
【发布时间】:2014-04-22 09:27:05
【问题描述】:

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

#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 OK\r\n\r\n<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 位代码。

【问题讨论】:

  • 明天再来,你会得到答案:)

标签: c++ networking boost boost-asio


【解决方案1】:

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

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

  "HTTP/1.0 200 OK\r\nContent-Length: 45\r\n\r\n<html><body><i>Hello, world</i></body></html>";

我希望您的邮件正文的Content-Length 正确。 ;)

【讨论】:

  • 听起来很合理,确实。 +1
  • 这适用于我的 Mac,但不适用于安装了 Windows 7 的工作机器。现在完全困惑了。
  • HTTP comms 并不像最初看起来那么简单。一年多前,我开始为 HTTP 服务器创建 C++ 库,直到现在我才相信它已经接近完成!它位于via-httplib,上面有一些与您类似的简单示例。请随意尝试一下,看看它是否可以解决您的问题。
【解决方案2】:

更新

客户可能需要 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 个)连接

这个old answer ('08) says backlog used to be 5 in Asio, by default

【讨论】:

  • 我在我的 Mac 上试过。 Chrome 不显示任何内容,而是显示错误。
  • 在 Mac 上我仍然需要添加 sleep hack 才能使其工作。
  • @C.R.更新了另一个基于连接保活语义的想法。
  • Connection: close 听起来很合理,但它不起作用。我不知道为什么。
【解决方案3】:

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

如果我在写入之前添加一个虚拟请求侦听器,您的示例无需延迟技巧即可工作:

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) {});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 2017-12-22
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多