Search code examples
c++serversendrecvwinsockets

C++ server with recv/send commands & request/response design


I'm trying to create a server with blocking sockets (one new thread for each new client). This thread should be able to receive commands from the client (and send back the result) and periodically send commands to the client (and request back the result).

What I've thought is creating two threads for each client, one for recv, second for send. However:

  1. it's double of the normal thread overhead.
  2. due to request/response design, recv I do in the first thread (to wait for client's commands) can be the request I look for in the second thread (client's result to my send) and vice versa. Making it all properly synced is probably a hell story. So now I'm thinking to do that from a single thread this way:

In a loop:

  1. setsockopt(SO_RCVTIMEO, &small_timeout); // set the timeout for the recv (like 1000 ms).
  2. recv(); // check for client's requests first. if returns WSAETIMEDOUT than I assume no data is requested and do nothing. if I get a normal request I handle it.
  3. if (clientbufferToSend != nullptr) send(clientbufferToSend); // now when client's request has been processed we check the command list we have to send to the client. if there is commands in queue, we send them. SO_SNDTIMEO timeout can be set to a large value so we don't deadlock if client looses connection.
    1. setsockopt(SO_RCVTIMEO, &large_timeout); // set the timeout for the recv (as large as SO_SNDTIMEO, just to not deadlock if anything).

    2. recv(); // now we wait the response from the client.

Is this the legal way to do what I want? Or are there better alternatives (preferrably with blocking sockets and threads)?

P.S. Does recv() with timeout returns WSAETIMEDOUT only if no data is available? Can it return this error if there is the data, but recv() wasn't fast enough to handle it all, thus returning partial data?


Solution

  • One approach is only create a background thread for reading from that socket. Write on whatever random thread your unsolicited events are raised.

    You’ll need following stuff.

    1. A critical section or mutex per socket to serialize writes, like when background thread is sending response to client-initiated message, and other thread wants to send message to the same client.

    2. Some other synchronization primitive like a conditional variable for client thread to sleep while waiting for responses.

    3. The background thread which receives messages needs to distinguish client-initiated messages (which need to be responded by the same background thread) from responses to server-initiated messages. If your network protocol doesn’t have that data you’ll have to change the protocol.

    This will work OK if your server-initiated events are only happening on a single thread, e.g. they come from some serialized source like a device or OS interface.

    If however the event source is multithreaded as well, and you want good performance, you gonna need non-trivial complexity to dispatch the responses to the correct server thread, like 1 conditional variable per client thread, maybe some queues, etc.