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

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

BOOST async_receive doesn't return when socket is closed

问题

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

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:

  1. #include <boost/asio/co_spawn.hpp>
  2. #include <boost/asio/detached.hpp>
  3. #include <boost/asio/io_service.hpp>
  4. #include <boost/asio/ip/tcp.hpp>
  5. #include <boost/asio/awaitable.hpp>
  6. #include <boost/asio.hpp>
  7. #include <string>
  8. #include <iostream>
  9. using namespace std;
  10. namespace asio = boost::asio;
  11. using namespace boost::asio::ip;
  12. asio::awaitable<void> handle_connection(tcp::socket&& stream)
  13. {
  14. std::cout << "new connection\n";
  15. std::string buffer;
  16. uint64_t buffer_size;
  17. while(stream.is_open())
  18. {
  19. co_await stream.async_receive(asio::buffer(&buffer_size, 8), asio::use_awaitable);
  20. buffer = std::string(buffer_size, '
    #include <boost/asio/co_spawn.hpp>
  21. #include <boost/asio/detached.hpp>
  22. #include <boost/asio/io_service.hpp>
  23. #include <boost/asio/ip/tcp.hpp>
  24. #include <boost/asio/awaitable.hpp>
  25. #include <boost/asio.hpp>
  26. #include <string>
  27. #include <iostream>
  28. using namespace std;
  29. namespace asio = boost::asio;
  30. using namespace boost::asio::ip;
  31. asio::awaitable<void> handle_connection(tcp::socket&& stream)
  32. {
  33. std::cout << "new connection\n";
  34. std::string buffer;
  35. uint64_t buffer_size;
  36. while(stream.is_open())
  37. {
  38. co_await stream.async_receive(asio::buffer(&buffer_size, 8), asio::use_awaitable);
  39. buffer = std::string(buffer_size, '\0');
  40. std::cout << "message size: " << buffer_size << "\n";
  41. co_await stream.async_receive(asio::buffer(buffer, buffer_size), asio::use_awaitable);
  42. std::cout << "new message: " << buffer << "\n";
  43. }
  44. // The program never reached this point.
  45. cout << "socket closed\n";
  46. co_return;
  47. }
  48. asio::awaitable<void> accept_connection()
  49. {
  50. auto ex = co_await asio::this_coro::executor;
  51. boost::asio::ip::tcp::acceptor acceptor(ex, {{}, 8000});
  52. tcp::socket sock = co_await acceptor.async_accept(asio::use_awaitable);
  53. while(true)
  54. {
  55. co_spawn(ex, handle_connection(std::move(sock)), asio::detached);
  56. sock = co_await acceptor.async_accept(asio::use_awaitable);
  57. }
  58. co_return;
  59. }
  60. int main()
  61. {
  62. asio::io_context io_ctx(2);
  63. co_spawn(io_ctx, accept_connection(), asio::detached);
  64. io_ctx.run();
  65. }
  66. ');
  67. std::cout << "message size: " << buffer_size << "\n";
  68. co_await stream.async_receive(asio::buffer(buffer, buffer_size), asio::use_awaitable);
  69. std::cout << "new message: " << buffer << "\n";
  70. }
  71. // The program never reached this point.
  72. cout << "socket closed\n";
  73. co_return;
  74. }
  75. asio::awaitable<void> accept_connection()
  76. {
  77. auto ex = co_await asio::this_coro::executor;
  78. boost::asio::ip::tcp::acceptor acceptor(ex, {{}, 8000});
  79. tcp::socket sock = co_await acceptor.async_accept(asio::use_awaitable);
  80. while(true)
  81. {
  82. co_spawn(ex, handle_connection(std::move(sock)), asio::detached);
  83. sock = co_await acceptor.async_accept(asio::use_awaitable);
  84. }
  85. co_return;
  86. }
  87. int main()
  88. {
  89. asio::io_context io_ctx(2);
  90. co_spawn(io_ctx, accept_connection(), asio::detached);
  91. io_ctx.run();
  92. }

Any help would be much appreciated.

答案1

得分: 3

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

  1. Your loop exits. By exception. Because you you don't handle the exception you don't see it.
  2. To show you three ways in which to handle the errors (including Eof):
  3. asio::awaitable<void> handle_connection(tcp::socket stream) {
  4. std::cout << "new connection" << std::endl;
  5. for (std::string buffer; true;)
  6. try {
  7. boost::endian::big_uint64_t buffer_size[1];
  8. co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
  9. buffer.assign(*buffer_size, '
    Your loop exits. By exception. Because you you don't handle the exception you don't see it.
  10. To show you three ways in which to handle the errors (including Eof):
  11. asio::awaitable<void> handle_connection(tcp::socket stream) {
  12. std::cout << "new connection" << std::endl;
  13. for (std::string buffer; true;)
  14. try {
  15. boost::endian::big_uint64_t buffer_size[1];
  16. co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
  17. buffer.assign(*buffer_size, '\0');
  18. std::cout << "message size: " << *buffer_size << "" << std::endl;
  19. auto [ec, n] =
  20. co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable));
  21. if (n) {
  22. buffer.resize(n);
  23. std::cout << "new message: " << buffer << "" << std::endl;
  24. }
  25. if (ec) {
  26. std::cout << "socket closed (ec: " << ec.message() << ")" << std::endl;
  27. break;
  28. }
  29. } catch (boost::system::system_error const& se) {
  30. std::cout << "socket closed (thrown ec: " << se.code().message() << ")" << std::endl;
  31. break;
  32. } catch (std::exception const& e) {
  33. std::cout << "socket closed (exception: " << e.what() << ")" << std::endl;
  34. break;
  35. }
  36. }
  37. ');
  38. std::cout << "message size: " << *buffer_size << "" << std::endl;
  39. auto [ec, n] =
  40. co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable));
  41. if (n) {
  42. buffer.resize(n);
  43. std::cout << "new message: " << buffer << "" << std::endl;
  44. }
  45. if (ec) {
  46. std::cout << "socket closed (ec: " << ec.message() << ")" << std::endl;
  47. break;
  48. }
  49. } catch (boost::system::system_error const& se) {
  50. std::cout << "socket closed (thrown ec: " << se.code().message() << ")" << std::endl;
  51. break;
  52. } catch (std::exception const& e) {
  53. std::cout << "socket closed (exception: " << e.what() << ")" << std::endl;
  54. break;
  55. }
  56. }
  1. 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).
  2. > _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._
  1. #include <boost/asio.hpp>
  2. #include <boost/endian/arithmetic.hpp>
  3. #include <iostream>
  4. namespace asio = boost::asio;
  5. using asio::ip::tcp;
  6. using asio::use_awaitable;
  7. asio::awaitable<void> handle_connection(tcp::socket stream) {
  8. auto tok = as_tuple(use_awaitable);
  9. std::cout << "new connection" << std::endl;
  10. boost::system::error_code ec;
  11. for (std::string buffer;;) {
  12. boost::endian::big_uint64_t buffer_size[1];
  13. size_t n = 0;
  14. tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
  15. if (ec) break;
  16. std::cout << "message size: " << *buffer_size << std::endl;
  17. buffer.assign(*buffer_size, '
    #include <boost/asio.hpp>
  18. #include <boost/endian/arithmetic.hpp>
  19. #include <iostream>
  20. namespace asio = boost::asio;
  21. using asio::ip::tcp;
  22. using asio::use_awaitable;
  23. asio::awaitable<void> handle_connection(tcp::socket stream) {
  24. auto tok = as_tuple(use_awaitable);
  25. std::cout << "new connection" << std::endl;
  26. boost::system::error_code ec;
  27. for (std::string buffer;;) {
  28. boost::endian::big_uint64_t buffer_size[1];
  29. size_t n = 0;
  30. tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
  31. if (ec) break;
  32. std::cout << "message size: " << *buffer_size << std::endl;
  33. buffer.assign(*buffer_size, '\0');
  34. std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok);
  35. if (n) {
  36. buffer.resize(n);
  37. std::cout << "new message: " << buffer << std::endl;
  38. }
  39. if (ec) break;
  40. }
  41. std::cout << "socket closed (" << ec.message() << ")" << std::endl;
  42. }
  43. asio::awaitable<void> accept_connection() {
  44. auto ex = co_await asio::this_coro::executor;
  45. tcp::acceptor acceptor(ex, {{}, 8989});
  46. while (true) {
  47. co_spawn(
  48. ex,
  49. handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)),
  50. asio::detached);
  51. }
  52. }
  53. int main() {
  54. asio::io_context io_ctx;
  55. co_spawn(io_ctx, accept_connection(), asio::detached);
  56. io_ctx.run();
  57. }
  58. ');
  59. std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok);
  60. if (n) {
  61. buffer.resize(n);
  62. std::cout << "new message: " << buffer << std::endl;
  63. }
  64. if (ec) break;
  65. }
  66. std::cout << "socket closed (" << ec.message() << ")" << std::endl;
  67. }
  68. asio::awaitable<void> accept_connection() {
  69. auto ex = co_await asio::this_coro::executor;
  70. tcp::acceptor acceptor(ex, {{}, 8989});
  71. while (true) {
  72. co_spawn(
  73. ex,
  74. handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)),
  75. asio::detached);
  76. }
  77. }
  78. int main() {
  79. asio::io_context io_ctx;
  80. co_spawn(io_ctx, accept_connection(), asio::detached);
  81. io_ctx.run();
  82. }

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

英文:

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):

  1. asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
  2. std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;
  3. for (std::string buffer; true;)
  4. try {
  5. boost::endian::big_uint64_t buffer_size[1];
  6. co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
  7. buffer.assign(*buffer_size, &#39;
    asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
  8. std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;
  9. for (std::string buffer; true;)
  10. try {
  11. boost::endian::big_uint64_t buffer_size[1];
  12. co_await stream.async_receive(asio::buffer(buffer_size), use_awaitable);
  13. buffer.assign(*buffer_size, &#39;\0&#39;);
  14. std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; &quot;&quot; &lt;&lt; std::endl;
  15. auto [ec, n] =
  16. co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable));
  17. if (n) {
  18. buffer.resize(n);
  19. std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; &quot;&quot; &lt;&lt; std::endl;
  20. }
  21. if (ec) {
  22. std::cout &lt;&lt; &quot;socket closed (ec: &quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  23. break;
  24. }
  25. } catch (boost::system::system_error const&amp; se) {
  26. std::cout &lt;&lt; &quot;socket closed (thrown ec: &quot; &lt;&lt; se.code().message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  27. break;
  28. } catch (std::exception const&amp; e) {
  29. std::cout &lt;&lt; &quot;socket closed (exception: &quot; &lt;&lt; e.what() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  30. break;
  31. }
  32. }
  33. &#39;);
  34. std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; &quot;&quot; &lt;&lt; std::endl;
  35. auto [ec, n] =
  36. co_await stream.async_receive(asio::buffer(buffer), as_tuple(use_awaitable));
  37. if (n) {
  38. buffer.resize(n);
  39. std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; &quot;&quot; &lt;&lt; std::endl;
  40. }
  41. if (ec) {
  42. std::cout &lt;&lt; &quot;socket closed (ec: &quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  43. break;
  44. }
  45. } catch (boost::system::system_error const&amp; se) {
  46. std::cout &lt;&lt; &quot;socket closed (thrown ec: &quot; &lt;&lt; se.code().message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  47. break;
  48. } catch (std::exception const&amp; e) {
  49. std::cout &lt;&lt; &quot;socket closed (exception: &quot; &lt;&lt; e.what() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  50. break;
  51. }
  52. }

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

  1. #include &lt;boost/asio.hpp&gt;
  2. #include &lt;boost/endian/arithmetic.hpp&gt;
  3. #include &lt;iostream&gt;
  4. namespace asio = boost::asio;
  5. using asio::ip::tcp;
  6. using asio::use_awaitable;
  7. asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
  8. auto tok = as_tuple(use_awaitable);
  9. std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;
  10. boost::system::error_code ec;
  11. for (std::string buffer;;) {
  12. boost::endian::big_uint64_t buffer_size[1];
  13. size_t n = 0;
  14. tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
  15. if (ec) break;
  16. std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; std::endl;
  17. buffer.assign(*buffer_size, &#39;
    #include &lt;boost/asio.hpp&gt;
  18. #include &lt;boost/endian/arithmetic.hpp&gt;
  19. #include &lt;iostream&gt;
  20. namespace asio = boost::asio;
  21. using asio::ip::tcp;
  22. using asio::use_awaitable;
  23. asio::awaitable&lt;void&gt; handle_connection(tcp::socket stream) {
  24. auto tok = as_tuple(use_awaitable);
  25. std::cout &lt;&lt; &quot;new connection&quot; &lt;&lt; std::endl;
  26. boost::system::error_code ec;
  27. for (std::string buffer;;) {
  28. boost::endian::big_uint64_t buffer_size[1];
  29. size_t n = 0;
  30. tie(ec, std::ignore) = co_await stream.async_receive(asio::buffer(buffer_size), tok);
  31. if (ec) break;
  32. std::cout &lt;&lt; &quot;message size: &quot; &lt;&lt; *buffer_size &lt;&lt; std::endl;
  33. buffer.assign(*buffer_size, &#39;\0&#39;);
  34. std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok);
  35. if (n) {
  36. buffer.resize(n);
  37. std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; std::endl;
  38. }
  39. if (ec) break;
  40. }
  41. std::cout &lt;&lt; &quot;socket closed (&quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  42. }
  43. asio::awaitable&lt;void&gt; accept_connection() {
  44. auto ex = co_await asio::this_coro::executor;
  45. tcp::acceptor acceptor(ex, {{}, 8989});
  46. while (true) {
  47. co_spawn(
  48. ex,
  49. handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)),
  50. asio::detached);
  51. }
  52. }
  53. int main() {
  54. asio::io_context io_ctx;
  55. co_spawn(io_ctx, accept_connection(), asio::detached);
  56. io_ctx.run();
  57. }
  58. &#39;);
  59. std::tie(ec, n) = co_await stream.async_receive(asio::buffer(buffer), tok);
  60. if (n) {
  61. buffer.resize(n);
  62. std::cout &lt;&lt; &quot;new message: &quot; &lt;&lt; buffer &lt;&lt; std::endl;
  63. }
  64. if (ec) break;
  65. }
  66. std::cout &lt;&lt; &quot;socket closed (&quot; &lt;&lt; ec.message() &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
  67. }
  68. asio::awaitable&lt;void&gt; accept_connection() {
  69. auto ex = co_await asio::this_coro::executor;
  70. tcp::acceptor acceptor(ex, {{}, 8989});
  71. while (true) {
  72. co_spawn(
  73. ex,
  74. handle_connection(co_await acceptor.async_accept(make_strand(ex), use_awaitable)),
  75. asio::detached);
  76. }
  77. }
  78. int main() {
  79. asio::io_context io_ctx;
  80. co_spawn(io_ctx, accept_connection(), asio::detached);
  81. io_ctx.run();
  82. }

Testing with

  1. g++ -std=c++20 -Os -Wall -pedantic -pthread main.cpp;
  2. ./a.out&amp; sleep 1;
  3. 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

  1. new connection
  2. message size: 12
  3. new message: Hello world!
  4. message size: 3
  5. new message: Bye
  6. 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:

确定