Socket.BeginReceive/Socket.EndReceive Documentation Problem

  • Thread starter Thread starter Cool Guy
  • Start date Start date
C

Cool Guy

In the documentation for both Socket.BeginReceive and Socket.EndReceive, it
is stated that receiving/sending will be happen in a separate thread.

However, from looking at the code in Reflector, you can see that this
simply isn't true. Instead they just use WSARecv and WSASend,
respectively.

WTF?
 
Cool Guy said:
In the documentation for both Socket.BeginReceive and Socket.EndReceive,
it
is stated that receiving/sending will be happen in a separate thread.

However, from looking at the code in Reflector, you can see that this
simply isn't true. Instead they just use WSARecv and WSASend,
respectively.

WTF?

Yes, and what? The are called on the same IOCP pool thread.

Willy.
 
Willy Denoyette said:
Yes, and what? The are called on the same IOCP pool thread.

Huh? Are you saying that the documentation is correct?

I was under the impression that WSASend/Recv run on the same thread as the
one on which they're called.
 
Yes, BeginReceive is on the caller's thread, EndReceive is on a thread pool
thread (more specificaly on a IOCP thread).
The same goes for BeginSend/EndSend, both are on a separate thread.

After calling BeginSend the thread can simply continue with other things,
while the IOCP thread is blocked on the EndReceive waiting for data to come
in, this is the foundation for all asynchronous IO in .NET.
Note that it's mandatory to pair each BeginXXX with a EndXXXX.


Willy.
 
I am new to this topic, and happened upon this thread while trying to make
sense of the MSDN sample code at:

ms- help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1033/cpguide/html/cpconNon-
blockingServerSocketExample.htm

(a VB version of this is available online at:
http://msdn2.microsoft.com/library/fx6588te(en- us,vs.80).aspx)

My question is really about that sample, but first, let me state my
understanding of what you are saying, so you can let me know whether I have
it right:

You are saying that BeginReceive is "on the caller's thread." Certainly, it
is invoked from the caller's thread (which perhaps is what you mean).
However, the documentation clearly states that "When your application calls
BeginReceive, the system will use a separate thread to execute the
specified callback method, and will block on EndReceive until the Socket
reads data or throws an exception." So, according to the documentation, the
*callback* method does *not* execute on the caller's thread, but rather on
a thread that is spawned by the BeginReceive method.

Now, when EndReceive is invoked, it "blocks" until at least some data has
been received by the client. I assume that means that if any data has been
received at all, even a single character, it will "release," returning that
data.

So, when you say that EndReceive is "on a thread pool thread," I'm guessing
that you mean the thread that was allocated when BeginReceive was called;
the same thread on which the callback function that invoked EndReceive is
running. Which means that EndReceive does not allocate any new thread of
its own; it is a synchronous function (so far as its caller is concerned)
that waits for input from the client, and returns as soon as any has been
received.

Apologies for the length of the preceding; hopefully all of the above is
correct. Assuming that it is, on to my question about the sample code:

Given that the initial BeginReceive operation spawns a new thread on which
ReadCallBack runs, why is it necessary for ReadCallBack to invoke
BeginReceive *again*, spawning yet another thread, just to pick up the next
portion of the input from the client (which could happen an indefinite
number of times)? Why not have a Socket function that can perform a
*synchronous* read from the client (i.e., Socket.Receive()), and invoke
that function within a loop to pick up the rest of the input? The main
thread will not be blocked, as it has already spawned a worker thread when
it called the BeginAccept function.

Now, I know that the documentation for Socket.Receive says that "The
Receive method will only read data that arrives from the remote host
established in the Connect or Accept method." So, does that mean it will
*not* read data from a host established in the *BeginAccept* method?
Because I don't see why you couldn't mix an asynchronously-spawned
connection (e.g., BeginAccept / EndAccept) with synchronously performed
reading (e.g., Receive).

Creating a new thread for each little fragment of input seems unnecessarily
expensive, given that each invocation of the ReadCallBack delegate is just
exiting after it calls BeginReceive.

Thanks in advance for any insights you might provide.

Carl
Yes, BeginReceive is on the caller's thread, EndReceive is on a thread pool
thread (more specificaly on a IOCP thread).
The same goes for BeginSend/EndSend, both are on a separate thread.

After calling BeginSend the thread can simply continue with other things,
while the IOCP thread is blocked on the EndReceive waiting for data to come
in, this is the foundation for all asynchronous IO in .NET.
Note that it's mandatory to pair each BeginXXX with a EndXXXX.

Willy.
[quoted text clipped - 9 lines]

Yes, BeginReceive is on the caller's thread, EndReceive is on a thread pool
thread (more specificaly on a IOCP thread).
The same goes for BeginSend/EndSend, both are on a separate thread.

After calling BeginSend the thread can simply continue with other things,
while the IOCP thread is blocked on the EndReceive waiting for data to come
in, this is the foundation for all asynchronous IO in .NET.
Note that it's mandatory to pair each BeginXXX with a EndXXXX.

Willy.
[quoted text clipped - 9 lines]
 
OK, I have figured out the nuances of this and am posting again in case
others are trying to figure this out themselves.

When you use BeginRead(), passing it a callback, no new thread is created
at that time. Instead, the framework makes a note of the callback, the
port to be monitored, the buffer location, etc., and then watches that port
for input without creating an additional thread. Clearly the requirements
for saving state are significantly less than those for a typical thread
(which needs a stack for local variables, current program counter location,
priority, etc., etc.).

When input eventually appears on the port, a thread is allocated from the
existing (reusable) thread pool and your callback method is run on that
thread. Your callback should be written so that it does not block. As a
result, your callback will complete very rapidly, obtaining whatever data
was read by calling EndRead(), processing it, calling BeginRead() again if
there is more input, and then terminating. When your callback completes,
the thread is returned to the thread pool.

This means that you are tying up a thread only during the time that your
callback is running. While you are waiting for client input, there is no
active thread.

So, say that you are running a chat application with thousands of
simultaneous client (user) connections. Let's say that 99 percent of the
time of these users is spent thinking and typing, and when they hit the
Enter key for each chat message, it takes only about one hundreth of the
time to process that input as it did to think it up and type it. Then, we
will have one one-hundreth of the threads running using the BeginRead() /
EndRead() approach that we would have had if we had allocated a new thread
for each client connection and waited synchronously on that thread for
input to arrive.

That is a huge increase in efficiency, and so that is why people go to the
trouble of using BeginRead() / EndRead() rather than just creating a new
thread for each connection.

Carl said:
I am new to this topic, and happened upon this thread while trying to make
sense of the MSDN sample code at:

ms- help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1033/cpguide/html/cpconNon-
blockingServerSocketExample.htm

(a VB version of this is available online at:
http://msdn2.microsoft.com/library/fx6588te(en- us,vs.80).aspx)

My question is really about that sample, but first, let me state my
understanding of what you are saying, so you can let me know whether I have
it right:

You are saying that BeginReceive is "on the caller's thread." Certainly, it
is invoked from the caller's thread (which perhaps is what you mean).
However, the documentation clearly states that "When your application calls
BeginReceive, the system will use a separate thread to execute the
specified callback method, and will block on EndReceive until the Socket
reads data or throws an exception." So, according to the documentation, the
*callback* method does *not* execute on the caller's thread, but rather on
a thread that is spawned by the BeginReceive method.

Now, when EndReceive is invoked, it "blocks" until at least some data has
been received by the client. I assume that means that if any data has been
received at all, even a single character, it will "release," returning that
data.

So, when you say that EndReceive is "on a thread pool thread," I'm guessing
that you mean the thread that was allocated when BeginReceive was called;
the same thread on which the callback function that invoked EndReceive is
running. Which means that EndReceive does not allocate any new thread of
its own; it is a synchronous function (so far as its caller is concerned)
that waits for input from the client, and returns as soon as any has been
received.

Apologies for the length of the preceding; hopefully all of the above is
correct. Assuming that it is, on to my question about the sample code:

Given that the initial BeginReceive operation spawns a new thread on which
ReadCallBack runs, why is it necessary for ReadCallBack to invoke
BeginReceive *again*, spawning yet another thread, just to pick up the next
portion of the input from the client (which could happen an indefinite
number of times)? Why not have a Socket function that can perform a
*synchronous* read from the client (i.e., Socket.Receive()), and invoke
that function within a loop to pick up the rest of the input? The main
thread will not be blocked, as it has already spawned a worker thread when
it called the BeginAccept function.

Now, I know that the documentation for Socket.Receive says that "The
Receive method will only read data that arrives from the remote host
established in the Connect or Accept method." So, does that mean it will
*not* read data from a host established in the *BeginAccept* method?
Because I don't see why you couldn't mix an asynchronously-spawned
connection (e.g., BeginAccept / EndAccept) with synchronously performed
reading (e.g., Receive).

Creating a new thread for each little fragment of input seems unnecessarily
expensive, given that each invocation of the ReadCallBack delegate is just
exiting after it calls BeginReceive.

Thanks in advance for any insights you might provide.

Carl
Yes, BeginReceive is on the caller's thread, EndReceive is on a thread pool
thread (more specificaly on a IOCP thread).
[quoted text clipped - 12 lines]
Yes, BeginReceive is on the caller's thread, EndReceive is on a thread pool
thread (more specificaly on a IOCP thread).
[quoted text clipped - 12 lines]
 
Back
Top