Search code examples
.netsocketsasynchronoustcpclient

Asynchronous TCP sockets - clarification?


Asynchronous does not need parallelism in order to exists. For example , while IO operations (web , hard-disk operation....) the current thread can be released and then , after the IO operation has complete , via completion ports , a thread is back to handle the result.

The whole point is not blocking .

I'm not as that much as familier with sockets , but I've read quite a lot .

But when I read about asynchronous sockets , all I saw were examples where the main thread which accepting new connections , is opening a NEW background thread to sit and wait for messages from a client.

Like said here:

//  The meaning of "asynchronous" is that the socket operation, whether
//  it be a read, write, connect, or accept, happens in a background
//  thread and thus leaves your application free to do something
//  else while the socket operation is running.

From MSDN :

Asynchronous sockets use threads from the system thread pool to process incoming connections. One thread is responsible for accepting connections, another thread is used to handle each incoming connection, and another thread is responsible for receiving data from the connection. These could be the same thread, depending on which thread is assigned by the thread pool. In the following example, the System.Threading.ManualResetEvent class suspends execution of the main thread and signals when execution can continue.

I also read Stephen Cleary's blog who mentioned the phases :

  • Constructing
  • Binding
  • Listening // <-- what's my question about
  • Accepting
  • Connecting
  • Reading
  • Writing
  • Disconnecting
  • Shutting down
  • Closing

Question

Looking at the listening phase-

AFAIU , an asynchronous socket while listening - will tie a thread that wait/block ?

Or am I completely wrong and a socket can be "free-tied-thread-while waiting" so that only when there is a message , a thread will server the operation .?


Solution

  • The async socket operations leverage something called non-blocking IO. Here is the Non-Blocking IO behavior:

    1. If a thread calls read with a “open_non_blocking” flag, and no data is available, then the call returns “try_again”.
    2. If a thread calls write with a “open_non_blocking” flag, and the output buffer is full, then the call returns “try_again”.

    Further, a select() system call takes non-blocking IO to the next level. It built on top of non-blocking IO, and offloads the task of checking stream readiness to the driver. The driver is asked to check a set of streams for readiness, and it return to the calling thread a bit mask for each of the set of streams. The bit mask indicates to the thread on what streams are ready. This allows the calling thread to multiplex many active streams using a single thread by leveraging the readiness information returned by the driver. The select call used by application servers to handle large numbers of clients, and scale for high volume. Select operation works by blocking the application thread until something happens on a set of file descriptors (socket is represented as a file descriptor). What is that something? Until one of the file descriptors has data ready to be read or written. Mostly, the select() based application servers would perform the below:

    1. Populate the fd_set data structure with the file descriptors that they want to read in.
    2. Populate the fd_set data structure with the file descriptors that they want to write to.
    3. Call the select().
    4. The network driver blocks the call, until something.
    5. When one of the file descriptor’s status has changed, the network driver awakens the thread, and returns the call.
    6. Once select() returns, the application thread can find what file descriptors are serviceable by inspecting a bit mask for each file descriptor returned by the select() call. The thread can service the ready FDs by performing reads, writes, or hangups.
    7. The application thread can repeat the same process.

    In your case, the async socket framework leverages the select() calls, which allows it to react to events on the file descriptors; and while your application can continue do its own work until the read/write is completed by the async socket framework.