boost::asio::acceptor - 在旧连接仍然打开时接受新的传入连接

Posted

技术标签:

【中文标题】boost::asio::acceptor - 在旧连接仍然打开时接受新的传入连接【英文标题】:boost::asio::acceptor - accept new incoming connections while old ones still open 【发布时间】:2013-05-07 10:17:45 【问题描述】:

我正在编写基于 boost asio 的代理服务器。在我负责接受从浏览器到代理服务器的传入连接的代码部分中,我面临着我不完全理解的行为。

所以 - 我正在使用下一个构造函数创建接受器对象:

_acceptor(_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port ), true)

从这里开始收听(start_accept):

_new_connection.reset(new connection(*_io_services.front(), _connection_id));
_acceptor.async_accept(_new_connection->get_socket(),
                            boost::bind(&server::handle_accept, this,
                                        boost::asio::placeholders::error));

和handle_accept

if (!error) 
    _new_connection->start();


// continue waiting for incoming connections
start_accept();

一般来说,我接受传入连接的代码与HTTP Server 2 示例中的代码相同

只有当第一个传入连接没有关闭时才会出现问题,然后第二个传入将排队等待,直到第一个被关闭。

根据这两个答案: boost::asio acceptor reopen and async read after EOF

How to launch an "event" when my Boost::asio tcp server just start running ( AKA io_service.run() )?

acceptor 对象会将所有传入的连接添加到队列中,并且在挂起的连接不会关闭之前不会接受它们。

我想立即处理所有传入的连接 - 所以它们不会在接受者的队列中等待处理,到目前为止我还没有找到任何解决方案。

你能帮我吗,什么是正确的方法来实现这个?

连接->start() 函数

void
connection::start() 
    _bsocket.async_read_some(boost::asio::buffer(_bbuffer),
            boost::bind(&connection::handle_browser_read_headers,
                        shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred
        ));

Graphical representation 更新:提升 asio 日志

@asio|1368460995.389629|0*1|socket@00CCFBE4.async_accept
@asio|1368461003.855113|>1|ec=system:0
@asio|1368461003.855113|1*2|socket@00E26850.async_receive
@asio|1368461003.855113|>2|ec=system:0,bytes_transferred=318
@asio|1368461003.856113|1*3|socket@00CCFBE4.async_accept
@asio|1368461003.856113|<1|
@asio|1368461003.856113|2*4|resolver@00E268D8.async_resolve
@asio|1368461003.856113|<2|
@asio|1368461003.866114|>4|ec=system:0,...
@asio|1368461003.866114|4*5|socket@00E26894.async_connect
@asio|1368461003.868114|<4|
@asio|1368461004.204133|>5|ec=system:0
@asio|1368461004.204133|5*6|socket@00E26894.async_send
@asio|1368461004.204133|<5|
@asio|1368461004.204133|>6|ec=system:0,bytes_transferred=302
@asio|1368461004.204133|6*7|socket@00E26894.async_receive
@asio|1368461004.204133|<6|
@asio|1368461004.613156|>7|ec=system:0,bytes_transferred=16384
@asio|1368461004.613156|7*8|socket@00E26850.async_send
@asio|1368461004.614157|<7|
@asio|1368461004.614157|>8|ec=system:0,bytes_transferred=16384
@asio|1368461004.614157|8*9|socket@00E26894.async_receive
@asio|1368461004.614157|<8|
@asio|1368461004.614157|>9|ec=system:0,bytes_transferred=1946
@asio|1368461004.614157|9*10|socket@00E26850.async_send
@asio|1368461004.614157|<9|
@asio|1368461004.614157|>10|ec=system:0,bytes_transferred=1946
@asio|1368461004.614157|10*11|socket@00E26894.async_receive
@asio|1368461004.614157|<10|
@asio|1368461004.618157|>11|ec=system:0,bytes_transferred=14080
@asio|1368461004.618157|11*12|socket@00E26850.async_send
@asio|1368461004.619157|<11|
@asio|1368461004.619157|>12|ec=system:0,bytes_transferred=14080
@asio|1368461004.619157|12*13|socket@00E26894.async_receive
@asio|1368461004.619157|<12|
@asio|1368461019.248994|>13|ec=asio.misc:2,bytes_transferred=0
@asio|1368461019.248994|13|socket@00E26894.close
@asio|1368461019.248994|13|socket@00E26850.close
@asio|1368461019.248994|<13|
@asio|1368461019.253994|0|resolver@00E268D8.cancel
@asio|1368461019.253994|>3|ec=system:0
@asio|1368461019.253994|3*14|socket@00E32688.async_receive
@asio|1368461019.254994|3*15|socket@00CCFBE4.async_accept
@asio|1368461019.254994|<3|
@asio|1368461019.254994|>14|ec=system:0,bytes_transferred=489
@asio|1368461019.254994|14*16|resolver@00E32710.async_resolve
@asio|1368461019.254994|<14|
@asio|1368461019.281995|>16|ec=system:0,...
@asio|1368461019.281995|16*17|socket@00E326CC.async_connect
@asio|1368461019.282996|<16|
@asio|1368461019.293996|>17|ec=system:0
@asio|1368461019.293996|17*18|socket@00E326CC.async_send
@asio|1368461019.293996|<17|
@asio|1368461019.293996|>18|ec=system:0,bytes_transferred=470
@asio|1368461019.293996|18*19|socket@00E326CC.async_receive
@asio|1368461019.293996|<18|
@asio|1368461019.315997|>19|ec=system:0,bytes_transferred=11001
@asio|1368461019.315997|19*20|socket@00E32688.async_send
@asio|1368461019.349999|<19|
@asio|1368461019.349999|>20|ec=system:0,bytes_transferred=11001
@asio|1368461019.349999|20|socket@00E326CC.close
@asio|1368461019.349999|20|socket@00E32688.close
@asio|1368461019.349999|<20|
@asio|1368461019.349999|0|resolver@00E32710.cancel

我发现接受器的行为取决于我用于从服务器套接字读取数据的函数。连接类从浏览器读取数据,修改请求url,连接到主机并发送请求,然后从服务器读取响应并将其写回浏览器。所以在我需要读取服务器主体的那一刻 - 我使用这个功能

    _ssocket.async_read_some(boost::asio::buffer(_sbuffer),
            boost::bind(&connection::handle_server_read_body,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
        ));

如果服务响应标头中未指定内容长度,我将一直阅读到 EOF。如果调用了 async_read_some 函数并且在套接字上没有更多数据要读取,则它会等待约 15 秒,然后才会引发 EOF。在这 15 秒内所有新的传入连接都不会被接受器接受。

但如果我使用的是 async_read 的另一种变体 -

        boost::asio::async_read(_ssocket, boost::asio::buffer(_sbuffer),
            boost::bind(&connection::handle_server_read_body,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
        ));

传入连接被接受就好了。但它 boost::asio::async_read 工作有点慢,它正在等待从套接字读取一堆数据并且在读取数据之前不会调用处理程序,所以 - 我想我会指定 transfer_at_least

        boost::asio::async_read(_ssocket, boost::asio::buffer(_sbuffer), boost::asio::transfer_at_least(1),
            boost::bind(&connection::handle_server_read_body,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
        ));

是的,它变得更好了 - 但接受新连接的问题返回:/

- async_read_some 和 boost::asio::async_read 之间的真正区别是什么 - 感觉就像某些东西被阻塞了。

【问题讨论】:

你能在你的 start() 函数中显示代码吗?也许它阻塞了你的代码,所以它不会继续接受()。 我更新了问题。一般来说,我检查了这一点,我没有在连接类中使用任何阻塞操作。从调试日志中,我看到 server::start_accept 函数在 handle_accept 之后立即被调用。但是接受者不接受新的连接。只有当第一个关闭时才会被接受。 简单看一下代码看起来不错。只要运行io_service 的线程可用,那么如果存在未完成的async_accept 操作,则应该接受连接;否则,连接将在接受器中排队,直到启动 accept 操作。如果您使用的是 Boost.Asio 1.47+,那么启用 handler tracking 可以更好地了解正在发生的事情。 异步链看起来是正确的。由于代码看起来也不错,我倾向于认为问题出在服务器代码之外。考虑使用较低级别的工具,例如netstat 来获取连接信息,并使用netcat 来建立连接。我会先在与服务器代码相同的主机上使用 netcat,然后在运行浏览器的机器上使用它。 有趣。 boost::async_read是按照stream.async_read_some实现的,CompletionCondition默认为boost::asio::transfer_all() 【参考方案1】:

我不知道这是否会有所帮助,但在我的服务器中,我正在使用以下内容作为会话的读取请求:

boost::asio::async_read( socket(), boost::asio::buffer( _incoming ),
    boost::asio::transfer_at_least( 1 ),
    boost::bind( &server_class::handle_read, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred ) );

然后,我将收到的任何内容转储到解析器中,以确保其正常(处理状态等)。

否则,根据我在这里看到的情况,我相信我正在做你正在做的一切。

如果这可行,那么 asio 的行为似乎不直观。

【讨论】:

以上是关于boost::asio::acceptor - 在旧连接仍然打开时接受新的传入连接的主要内容,如果未能解决你的问题,请参考以下文章

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效

如何在保存在 Mongoose (ExpressJS) 之前在模型中格式化数据