1

Source code: https://think-async.com/Asio/asio-1.18.0/doc/asio/tutorial/tutdaytime7/src.html

tcp_server shows an intention to use socket on the heap, wrapped by a type called tcp_connection.

class tcp_server
{
private:
  void start_accept()
  {
    tcp_connection::pointer new_connection =
      tcp_connection::create(io_context_);

    acceptor_.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
          asio::placeholders::error));
  }

  void handle_accept(tcp_connection::pointer new_connection,
      const asio::error_code& error)
  {
    if (!error)
    {
      new_connection->start();
    }

    start_accept();
  }
  ...

socket heap objects are managed by enable_shared_from_this aka shared_ptr

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>
{
public:
  typedef boost::shared_ptr<tcp_connection> pointer;

  static pointer create(asio::io_context& io_context)
  {
    return pointer(new tcp_connection(io_context));
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    message_ = make_daytime_string();

    asio::async_write(socket_, asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this()));
  }
  ...

While udp server just use member socket.

class udp_server
{
public:
  udp_server(asio::io_context& io_context)
    : socket_(io_context, udp::endpoint(udp::v4(), 13))
  {
    start_receive();
  }

private:
  void start_receive()
  {
    socket_.async_receive_from(
        asio::buffer(recv_buffer_), remote_endpoint_,
        boost::bind(&udp_server::handle_receive, this,
          asio::placeholders::error));
  }

  void handle_receive(const asio::error_code& error)
  {
    if (!error)
    {
      boost::shared_ptr<std::string> message(
          new std::string(make_daytime_string()));

      socket_.async_send_to(asio::buffer(*message), remote_endpoint_,
          boost::bind(&udp_server::handle_send, this, message));

      start_receive();
    }
  }

  void handle_send(boost::shared_ptr<std::string> /*message*/)
  {
  }

  udp::socket socket_;
  udp::endpoint remote_endpoint_;
  boost::array<char, 1> recv_buffer_;
};

My question is why tcp acceptor and udp socket examples choose different approaches?

sandthorn
  • 2,770
  • 1
  • 15
  • 59

2 Answers2

2

The shared pointer is there to manage the lifetime of the connection.

In the case of TCP it is more common to have multiple connections which leads to a situation where you will likely have multiple connections instances with unrelated lifetimes.

UDP is connection-less and many times is used for one-shot messages.

In fact you can come up with scenarios where you'd use shared pointers with UDP (e.g. with "logical connections", such as streaming audio over UDP).

Also, conversely you CAN solve the lifetime puzzle differently (regardless of TCP/UDP). For example here I used a std::list (for reference stability) judicious single-threaded access to it: How to pass a boost asio tcp socket to a thread for sending heartbeat to client or server ¹


¹ I previously compared that to a shared_ptr approach in this answer: How to eliminate crashes when destroying boost::asio entities on fly?, which might interest you as well

sehe
  • 374,641
  • 47
  • 450
  • 633
  • You just read my mind! As a matter of facts, having to do `shared_ptr` in current form with `enable_shared_from_this` inheritance idiom feels a bit like hack for me. I think we should have some interface using a move-only heap type like in .NET [`TcpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener.accepttcpclientasync?view=net-5.0) in the standard. – sandthorn Nov 27 '20 at 04:58
  • Do you think if we should also have so-called "easier" async/await interface laying around in the standard network library or we should come after this executor approach only? Will the sender/reciever pattern via stack-less coroutines really have that much impact on machine throughput? – sandthorn Nov 27 '20 at 04:58
  • Hmm. I think you're mixing a few things there. The CoroutineTS enables _stackful_ coroutines. CoroutineTS enables libraries to plug in to the syntax. The way ASIO is designed allows for the user to decide what kind of paradigm they prefer, and even to mix & match. The mechanism is by supplying a completion token that implicitly selects an async_result. This is called the async_completion "protocol [my wording, not sure whether official]". – sehe Nov 30 '20 at 14:31
  • So the point of ASIO is that you have all the options. I'd say in general coroutines have the benefit of making code more legible and easier to compose, not so much the performance benefit (though it should at least be comparable to [composed operations](http://blog.think-async.com/2009/08/composed-operations-coroutines-and-code.html) in any other form. – sehe Nov 30 '20 at 14:33
  • Erm, checking back I might have been confused about CoroutineTS myself - looks like it is stackless by default (although you can probably write customizations to "wrap" a stackful coroutine). – sehe Nov 30 '20 at 15:20
  • That said, with coroutine support in the compiler there ARE definitely [more opportunities for the compiler to optimize](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0981r0.html) (mostly by eliminating code) which could indeed lead to (marginal?) gains \[[patent pending](https://worldwide.espacenet.com/patent/search/family/055861205/publication/WO2016176058A1?q=pn%3DWO2016176058A1)]. I think I've seen some excellent talks by Gor Nishanov on Youtube that describes when and how they work best. – sehe Nov 30 '20 at 15:30
  • There is nothing wrong with the executor's cb chaining interface. I guess it must be faster in compilation than standard coroutines. However the task-based interface of coroutine looks way way more natural than executor's to me. I hope c++23 will have both styles in Networking extension or maybe filestream gets portable async calls also.. – sandthorn Dec 04 '20 at 07:01
1

Only to show different approaches ...
So considering the context, you may use one or another solution.

Jean Davy
  • 2,062
  • 1
  • 19
  • 24
  • 1
    This is correct, but I think OP wants to learn what aspects are at play – sehe Nov 26 '20 at 14:25
  • 1
    @sehe Yes and I "+1" your answer. You answer even if the question is not explicit ;-) – Jean Davy Nov 26 '20 at 18:52
  • sehe is like magic as if can read my mind!! Guys, please feel free to edit my question's context to make it more explicit for this exact purpose. – sandthorn Nov 27 '20 at 04:21