BOOST async_receive 当套接字关闭时不返回

huangapple go评论74阅读模式
英文:

BOOST async_receive doesn't return when socket is closed

问题

我正在尝试使用 *boost.asio* 编写一个简单的客户端/服务器程序。
我的问题是,当客户端关闭其套接字并退出时,“stream.async_receive()”会阻塞服务器端并且不返回。

这是我的代码:
英文:

I'm trying to write a simple client/server program using boost.asio.
My problem is that when the client closes it's socket and exits, "stream.async_receive()" blocks the server side and doesn't return.

Here's my code:

#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio.hpp>
#include <string>
#include <iostream>

using namespace std;
namespace asio = boost::asio;
using namespace boost::asio::ip;

asio::awaitable<void> handle_connection(tcp::socket&& stream)
{
    std::cout << "new connection\n";
    std::string buffer;
    uint64_t buffer_size;

    while(stream.is_open())
    {
        co_await stream.async_receive(asio::buffer(&buffer_size, 8), asio::use_awaitable);
        buffer = std::string(buffer_size, '
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio.hpp>
#include <string>
#include <iostream>
using namespace std;
namespace asio = boost::asio;
using namespace boost::asio::ip;
asio::awaitable<void> handle_connection(tcp::socket&& stream)
{
std::cout << "new connection\n";
std::string buffer;
uint64_t buffer_size;
while(stream.is_open())
{
co_await stream.async_receive(asio::buffer(&buffer_size, 8), asio::use_awaitable);
buffer = std::string(buffer_size, '\0');
std::cout << "message size: " << buffer_size << "\n";
co_await stream.async_receive(asio::buffer(buffer, buffer_size), asio::use_awaitable);
std::cout << "new message: " << buffer << "\n";
}
// The program never reached this point.
cout << "socket closed\n";
co_return;
}
asio::awaitable<void> accept_connection()
{
auto ex = co_await asio::this_coro::executor;
boost::asio::ip::tcp::acceptor acceptor(ex, {{}, 8000});
tcp::socket sock = co_await acceptor.async_accept(asio::use_awaitable);
while(true)
{
co_spawn(ex, handle_connection(std::move(sock)), asio::detached);
sock = co_await acceptor.async_accept(asio::use_awaitable);
}
co_return;
}
int main()
{
asio::io_context io_ctx(2);
co_spawn(io_ctx, accept_connection(), asio::detached);
io_ctx.run();
}
'); std::cout << "message size: " << buffer_size << "\n"; co_await stream.async_receive(asio::buffer(buffer, buffer_size), asio::use_awaitable); std::cout << "new message: " << buffer << "\n"; } // The program never reached this point. cout << "socket closed\n"; co_return; } asio::awaitable<void> accept_connection() { auto ex = co_await asio::this_coro::executor; boost::asio::ip::tcp::acceptor acceptor(ex, {{}, 8000}); tcp::socket sock = co_await acceptor.async_accept(asio::use_awaitable); while(true) { co_spawn(ex, handle_connection(std::move(sock)), asio::detached); sock = co_await acceptor.async_accept(asio::use_awaitable); } co_return; } int main() { asio::io_context io_ctx(2); co_spawn(io_ctx, accept_connection(), asio::detached); io_ctx.run(); }

Any help would be much appreciated.

答案1

得分: 3

以下是您要求翻译的代码部分:

Your loop exits. By exception. Because you you don't handle the exception you don't see it.

To show you three ways in which to handle the errors (including Eof):

asio::awaitable<void> handle_connection(tcp::socket stream) {
    std::cout << "new connection" << std::endl;

    for (std::string buffer; true;)
        try {
            boost::endian::big_uint64_t buffer_size[1];
            co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
            buffer.assign(*buffer_size, '
Your loop exits. By exception. Because you you don't handle the exception you don't see it.
To show you three ways in which to handle the errors (including Eof):
asio::awaitable<void> handle_connection(tcp::socket stream) {
std::cout << "new connection" << std::endl;
for (std::string buffer; true;)
try {
boost::endian::big_uint64_t buffer_size[1];
co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
buffer.assign(*buffer_size, '\0');
std::cout << "message size: " << *buffer_size << "" << std::endl;
auto [ec, n] =
co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable));
if (n) {
buffer.resize(n);
std::cout << "new message: " << buffer << "" << std::endl;
}
if (ec) {
std::cout << "socket closed (ec: " << ec.message() << ")" << std::endl;
break;
}
} catch (boost::system::system_error const& se) {
std::cout << "socket closed (thrown ec: " << se.code().message() << ")" << std::endl;
break;
} catch (std::exception const& e) {
std::cout << "socket closed (exception: " << e.what() << ")" << std::endl;
break;
}
}
'); std::cout << "message size: " << *buffer_size << "" << std::endl; auto [ec, n] = co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable)); if (n) { buffer.resize(n); std::cout << "new message: " << buffer << "" << std::endl; } if (ec) { std::cout << "socket closed (ec: " << ec.message() << ")" << std::endl; break; } } catch (boost::system::system_error const& se) { std::cout << "socket closed (thrown ec: " << se.code().message() << ")" << std::endl; break; } catch (std::exception const& e) { std::cout << "socket closed (exception: " << e.what() << ")" << std::endl; break; } }
I'd suggest sticking to the `error_code` approach, as it is the only approach (with C++20 coro's) that allows you to gracefully handle partial success (e.g. when the connection is lost half-way receiving the expected bytes). 

> _Whether partial success makes sense depends on your application. Note that if handled without caution this (dealing with unintended partial messages) may lead to security issues._
#include <boost/asio.hpp>
#include <boost/endian/arithmetic.hpp>
#include <iostream>

namespace asio = boost::asio;
using asio::ip::tcp;
using asio::use_awaitable;

asio::awaitable<void> handle_connection(tcp::socket stream) {
    auto tok = as_tuple(use_awaitable);
    std::cout << "new connection" << std::endl;

    boost::system::error_code ec;
    for (std::string buffer;;) {
        boost::endian::big_uint64_t buffer_size[1];
        size_t n = 0;

        tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
        if (ec) break;

        std::cout << "message size: " << *buffer_size << std::endl;
        buffer.assign(*buffer_size, '
#include <boost/asio.hpp>
#include <boost/endian/arithmetic.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using asio::use_awaitable;
asio::awaitable<void> handle_connection(tcp::socket stream) {
auto tok = as_tuple(use_awaitable);
std::cout << "new connection" << std::endl;
boost::system::error_code ec;
for (std::string buffer;;) {
boost::endian::big_uint64_t buffer_size[1];
size_t n = 0;
tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
if (ec) break;
std::cout << "message size: " << *buffer_size << std::endl;
buffer.assign(*buffer_size, '\0');
std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok);
if (n) {
buffer.resize(n);
std::cout << "new message: " << buffer << std::endl;
}
if (ec) break;
}
std::cout << "socket closed (" << ec.message() << ")" << std::endl;
}
asio::awaitable<void> accept_connection() {
auto ex = co_await asio::this_coro::executor;
tcp::acceptor acceptor(ex, {{}, 8989});
while (true) {
co_spawn(
ex,
handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)),
asio::detached);
}
}
int main() {
asio::io_context io_ctx;
co_spawn(io_ctx, accept_connection(), asio::detached);
io_ctx.run();
}
'); std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok); if (n) { buffer.resize(n); std::cout << "new message: " << buffer << std::endl; } if (ec) break; } std::cout << "socket closed (" << ec.message() << ")" << std::endl; } asio::awaitable<void> accept_connection() { auto ex = co_await asio::this_coro::executor; tcp::acceptor acceptor(ex, {{}, 8989}); while (true) { co_spawn( ex, handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)), asio::detached); } } int main() { asio::io_context io_ctx; co_spawn(io_ctx, accept_connection(), asio::detached); io_ctx.run(); }

希望这对您有所帮助。如果您需要任何进一步的帮助,请随时提出。

英文:

Your loop exits. By exception. Because you you don't handle the exception you don't see it.

To show you three ways in which to handle the errors (including Eof):

asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
    std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;

    for (std::string buffer; true;)
        try {
            boost::endian::big_uint64_t buffer_size[1];
            co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
            buffer.assign(*buffer_size, &#39;
asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;
for (std::string buffer; true;)
try {
boost::endian::big_uint64_t buffer_size[1];
co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
buffer.assign(*buffer_size, &#39;\0&#39;);
std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; &quot;&quot; &lt;&lt; std::endl;
auto [ec, n] =
co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable));
if (n) {
buffer.resize(n);
std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; &quot;&quot; &lt;&lt; std::endl;
}
if (ec) {
std::cout &lt;&lt; &quot;socket closed (ec: &quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
break;
}
} catch (boost::system::system_error const&amp; se) {
std::cout &lt;&lt; &quot;socket closed (thrown ec: &quot; &lt;&lt; se.code().message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
break;
} catch (std::exception const&amp; e) {
std::cout &lt;&lt; &quot;socket closed (exception: &quot; &lt;&lt; e.what() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
break;
}
}
&#39;); std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; &quot;&quot; &lt;&lt; std::endl; auto [ec, n] = co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable)); if (n) { buffer.resize(n); std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; &quot;&quot; &lt;&lt; std::endl; } if (ec) { std::cout &lt;&lt; &quot;socket closed (ec: &quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl; break; } } catch (boost::system::system_error const&amp; se) { std::cout &lt;&lt; &quot;socket closed (thrown ec: &quot; &lt;&lt; se.code().message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl; break; } catch (std::exception const&amp; e) { std::cout &lt;&lt; &quot;socket closed (exception: &quot; &lt;&lt; e.what() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl; break; } }

I'd suggest sticking to the error_code approach, as it is the only approach (with C++20 coro's) that allows you to gracefully handle partial success (e.g. when the connection is lost half-way receiving the expected bytes).

> Whether partial success makes sense depends on your application. Note that if handled without caution this (dealing with unintended partial messages) may lead to security issues.

Live On Coliru

#include &lt;boost/asio.hpp&gt;
#include &lt;boost/endian/arithmetic.hpp&gt;
#include &lt;iostream&gt;

namespace asio = boost::asio;
using asio::ip::tcp;
using asio::use_awaitable;

asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
    auto tok = as_tuple(use_awaitable);
    std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;

    boost::system::error_code ec;
    for (std::string buffer;;) {
        boost::endian::big_uint64_t buffer_size[1];
        size_t n = 0;

        tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
        if (ec) break;

        std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; std::endl;
        buffer.assign(*buffer_size, &#39;
#include &lt;boost/asio.hpp&gt;
#include &lt;boost/endian/arithmetic.hpp&gt;
#include &lt;iostream&gt;
namespace asio = boost::asio;
using asio::ip::tcp;
using asio::use_awaitable;
asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
auto tok = as_tuple(use_awaitable);
std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;
boost::system::error_code ec;
for (std::string buffer;;) {
boost::endian::big_uint64_t buffer_size[1];
size_t n = 0;
tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
if (ec) break;
std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; std::endl;
buffer.assign(*buffer_size, &#39;\0&#39;);
std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok);
if (n) {
buffer.resize(n);
std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; std::endl;
}
if (ec) break;
}
std::cout &lt;&lt; &quot;socket closed (&quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
}
asio::awaitable&lt;void&gt; accept_connection() {
auto ex = co_await asio::this_coro::executor;
tcp::acceptor acceptor(ex, {{}, 8989});
while (true) {
co_spawn(
ex,
handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)),
asio::detached);
}
}
int main() {
asio::io_context io_ctx;
co_spawn(io_ctx, accept_connection(), asio::detached);
io_ctx.run();
}
&#39;); std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok); if (n) { buffer.resize(n); std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; std::endl; } if (ec) break; } std::cout &lt;&lt; &quot;socket closed (&quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl; } asio::awaitable&lt;void&gt; accept_connection() { auto ex = co_await asio::this_coro::executor; tcp::acceptor acceptor(ex, {{}, 8989}); while (true) { co_spawn( ex, handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)), asio::detached); } } int main() { asio::io_context io_ctx; co_spawn(io_ctx, accept_connection(), asio::detached); io_ctx.run(); }

Testing with

g++ -std=c++20 -Os -Wall -pedantic -pthread main.cpp;
./a.out&amp; sleep 1;
printf &#39;\x00\x00\x00\x00\x00\x00\x00\x0cHello world!\x00\x00\x00\x00\x00\x00\x00\x03Bye&#39; | nc 127.0.0.1 8000 -w 1

Prints

new connection
message size: 12
new message: Hello world!
message size: 3
new message: Bye
socket closed (End of file)

Interactive:

BOOST async_receive 当套接字关闭时不返回

huangapple
  • 本文由 发表于 2023年2月23日 21:37:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/75545587.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定