7

I use the same socket in my udp server in order to receive data from clients on some port, and later after processing of requests respond to to clients using ip::ud::socket ::async_send_to

Receive is done async with async_receive_from also. The socket uses same ioService (it's the same socket after all) The documentation does not state clearly if one can have at a moment the same udp socket receive datagrams from client A (in async way) and possibly send another datagram to client B (async sent) at the same time I suspect this could lead to problems. I ended up using same socket for reply because I could not bind another socket to the same server port while replying to another client.

How can I bind another socket to the same server port?

EDIT. I try to bind the second udp socket to the same UDP port with:

socket(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))

When I do this first time (binding for server "receive" socket) it's OK but trying to create another socket the second time like that it reports error at bind (asio throws exception)

Ghita
  • 4,465
  • 4
  • 42
  • 69
  • 2
    Please specify your actual question. Is your setup working, and you are looking for confirmation that it's ok by design? Or is it not working, and if not what error do you observe? Or are you asking how to bind another socket to the server port? – mtrw Sep 03 '12 at 19:02
  • @mtrw Is it possible to bind another socket to same server port ? That way I would use different sockets for sending out replays. What happens is, is that I think my setup can cause server malfunction. – Ghita Sep 03 '12 at 19:04
  • Please edit your question to clarify instead of adding comments – Sam Miller Sep 03 '12 at 19:11
  • I updated your question to include the actual question. It would help if you would also post a short piece of code showing your attempt to bind the second socket, and the error that occurs. – mtrw Sep 03 '12 at 19:11
  • @mrtw updated the question with sample – Ghita Sep 03 '12 at 19:48
  • 1
    I suppose the key issue here is trying to bind more than one socket with the same port, which is simply not possible and senseless. – E_net4 Sep 03 '12 at 20:51
  • 3
    @E_net4 It is indeed possible with UDP, via `SO_REUSEADDR`: however I agree that it's pointless. OP: Sockets are full-duplex all the way down to the metal: you can read and write them simultaneously. – user207421 Sep 04 '12 at 07:54

1 Answers1

16

It is possible to have a UDP socket concurrently receiving from one remote endpoint and sending to a different remote endpoint. However, per the Boost.Asio Threads and Boost.Asio documentation, it is generally unsafe to make concurrent calls on a single object.

Thus, this is safe:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive_from( ... );     |
socket.async_send_to( ... );          |

and this is safe:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive_from( ... );     |
                                      | socket.async_send_to( ... );

but this is specified as not being safe:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive_from( ... );     | socket.async_send_to( ... );
                                      |

Be aware that some functions, such as boost::asio::async_read, are a composed operation, and have additional thread safety restrictions.


If either of the following are true, then no additional synchronization needs to occur, as the flow will be implicitly synchronous:

  • All socket calls occur within handlers, and io_service::run() is only invoked from a single thread.
  • async_receive_from and async_send_to are only invoked within the same chain of asynchronous operations. For example, the ReadHandler passed to async_receive_from invokes async_send_to, and the WriteHandler passed to async_send_to invokes async_receive_from.

    void read()
    {
      socket.async_receive_from( ..., handle_read );  --.
    }                                                   |
        .-----------------------------------------------'
        |      .----------------------------------------.
        V      V                                        |
    void handle_read( ... )                             |
    {                                                   |
      socket.async_send_to( ..., handle_write );  --.   |
    }                                               |   |
        .-------------------------------------------'   |
        |                                               |
        V                                               |
    void handle_write( ... )                            |
    {                                                   |
      socket.async_receive_from( ..., handle_read );  --'
    }
    

On the other hand, if there are multiple threads potentially making concurrent calls to the socket, then synchronization needs to occur. Consider performing synchronization by either invoking the functions and handlers through a boost::asio::io_service::strand, or using other synchronization mechanisms, such as Boost.Thread's mutex.


In addition to thread safety, the management of object lifetimes must be considered. If the server needs to process multiple request concurrently, then be careful about ownership of the buffer and endpoint for each request->process->response chain. Per async_receive_from's documentation, the caller retains ownership of both the buffer and endpoint. As such, it may be easier to manage the lifetime of the objects via boost::shared_ptr. Otherwise, if the chain is quick enough that concurrent chains are not required, then it simplifies management, allowing the same buffer and endpoint to be used per request.


Finally, the socket_base::reuse_address class allows a socket to be bound to an address that is already in use. However, I do not think it is an applicable solution here, as it is generally used:

  • For TCP to allow a process to restart and listen to the same port, even if the port is in a TIME_WAIT state.
  • For UDP to allow multiple processes to bind to the same port, allowing each process to receive and broadcast via multicast.
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • Quite an impressive answer. I my case I ahve as I said only one ioService thread (main) but even though async_receive_from/async_send_to cannot be called interleaved one of them can be called before the other has completed (complete handler for the other called). This is because I do processing so to said in another thread that has noting to do with ioService thread. – Ghita Sep 05 '12 at 14:15
  • Another thing. Where is it specified that endpoint classes have to be handled in a particular manner. Once I receive an endpoint as an output param in async_receive_from I make copies to it (using copy constructor) passing it around as a "state" (so that I know where later to respond to) until the moment I need to send response back. – Ghita Sep 05 '12 at 14:19
  • 2
    It is fine to have multiple asynchronous operations pending on an object; it is just specified that concurrently calls on the object are unsafe. If socket calls are occurring from outside of a handler and only one thread is invoking `io_service::run()`, then posting the socket calls into `io_service` will synchronize. Boost.Asio requires that the caller guarantees certain arguments will remain valid until the handler is called; it never forces the user to manage them in a specific way. In some cases, it can be easier to associate the objects lifespan to the async-chain via smart pointers. – Tanner Sansbury Sep 05 '12 at 15:19
  • Postings in asio-users mailing list seems to confirm what you are saying. I'll mark your answer as accepted. – Ghita Sep 05 '12 at 21:49
  • And while I'm here just wanted to ask if you use shared from this approach in case of error if you don't make the corresponding async call from the completion handle are you guaranteed that as soon you leave the handle you get deleted? – Ghita Sep 05 '12 at 21:55
  • If that is the last copy of the shared pointer, then yes. All copies of a handler, and its corresponding arguments (i.e. a copy of the shared pointer), are destroyed no later than immediately after invocation of the handler. See [this](http://stackoverflow.com/questions/11356742/) question for more details. – Tanner Sansbury Sep 05 '12 at 22:19