Async TCPListener (FW 2.0): Quit listening

A

Armin Zingler

Hi,

surprisingly, I didn't find any satisfying answer to the following question:

Situation:
I use the TCPListener's BeginAcceptTcpClient method. In the callback
procedure I call EndAcceptTcpClient. To stop listening, the listener's stop
method is to be called.

PRB:
If I call Stop, the callback procedure is called even though no connection
has been made. This leads to an ObjectDisposedException when calling
EndAcceptTcpClient.

Questions:
- Why is the callback called at all? There is no incoming connection.
- What is the correct way to handle the problem? Am I to call
EndAcceptTcpClient in any case and catch the ObjectDisposedException? Or
should I better query listener.Pending and call EndAcceptTcpClient only if
it returns True? If it is False, what happens due to the missing call to
EndAcceptTcpClient? I thought Begin* and End* are to be used in pairs.
- I actually don't want to "End" listening, so why call End* even though I
want to keep on accepting more clients?
- The FW's async model is not very comprehensive. Why is there not a simple
Event from the listener (maybe in another thread)?


Armin
 
P

Peter Duniho

Armin said:
[...]
PRB:
If I call Stop, the callback procedure is called even though no connection
has been made. This leads to an ObjectDisposedException when calling
EndAcceptTcpClient.

Questions:
- Why is the callback called at all? There is no incoming connection.

The callback simply notifies you that the operation has completed. It's
not restricted to successful completions.
- What is the correct way to handle the problem? Am I to call
EndAcceptTcpClient in any case and catch the ObjectDisposedException?
Yes.

Or
should I better query listener.Pending and call EndAcceptTcpClient only if
it returns True? If it is False, what happens due to the missing call to
EndAcceptTcpClient? I thought Begin* and End* are to be used in pairs.

They should be, and that's why your callback is called and why you
should still call EndAcceptTcpClient.

You should think of the Begin/End methods as two halves of the single
synchronous method. The single synchronous method doesn't complete
halfway; if there's an error, it still does the entire thing.

So, if you call AcceptTcpClient and then close the socket from a
different thread, AcceptTcpClient will throw an exception. Likewise,
since calling BeginAcceptTcpClient is like the calling AcceptTcpClient,
while EndAcceptTcpClient is like having your code go back to that call
and allowing it to complete, you should expect EndAcceptTcpClient to do
whatever AcceptTcpClient might have done. Including throwing an
exception when you close the socket.
- I actually don't want to "End" listening, so why call End* even though I
want to keep on accepting more clients?

You need to call End because you called Begin.
- The FW's async model is not very comprehensive. Why is there not a simple
Event from the listener (maybe in another thread)?

Because that's not the design pattern that was chosen for TcpClient.
You may want to review the MSDN documentation regarding asynchronous
programming in .NET:

http://msdn2.microsoft.com/en-us/library/ms228969.aspx

IMHO, that section of the docs is not perfect, but it does a reasonable
job of explaining the patterns used and how to use them.

Personally, I would disagree that the lack of an event for dealing with
asynchronous logic indicates that the model "is not very comprehensive".
I don't think it's reasonable to expect every asynchronous API to
support every possible implementation, or even all of the common
patterns in .NET. The IAsyncResult design provides quite a lot of
flexibility and frankly I think it's a better fit for this particular
situation, because it is well-defined how the Begin and End methods
should work given the existing synchronous method they correspond to.

Pete
 
A

Armin Zingler

Peter Duniho said:
Armin said:
[...]
PRB:
If I call Stop, the callback procedure is called even though no
connection has been made. This leads to an ObjectDisposedException
when calling EndAcceptTcpClient.

Questions:
- Why is the callback called at all? There is no incoming
connection.

The callback simply notifies you that the operation has completed. It's
not restricted to successful completions.


In order to avoid a lengthier discussion, I only say that I have to accept
it like it is. Though, an event driven model (ConnectionRequest Event) would
be easier to use, IMO.


Thanks.


Armin
 
C

Chris Mullins [MVP - C#]

Armin Zingler said:
In order to avoid a lengthier discussion, I only say that I have to accept
it like it is. Though, an event driven model (ConnectionRequest Event)
would be easier to use, IMO.

I've played with a number of models for doing this, and the Event Stuff
always starts out easier, but ends up as problem later down the line.

I quite like the Async stuff as it sits in the .Net framework...
 
P

Peter Duniho

Armin said:
In order to avoid a lengthier discussion, I only say that I have to
accept it like it is. Though, an event driven model (ConnectionRequest
Event) would be easier to use, IMO.

It would be trivial for you to wrap the existing IAsyncResult pattern in
an event-based pattern, if you feel that strongly about it.

Pete
 
A

Armin Zingler

Chris Mullins said:
I've played with a number of models for doing this, and the Event
Stuff always starts out easier, but ends up as problem later down
the line.

I quite like the Async stuff as it sits in the .Net framework...


Ok, thanks. Do you happen to have a link to a good tutorial about using the
TcpClient class (apart from MSDN lib)? (for example, samples that also
include none-standard situations).


Armin
 
P

Peter Duniho

Armin said:
Ok, thanks. Do you happen to have a link to a good tutorial about using the
TcpClient class (apart from MSDN lib)? (for example, samples that also
include none-standard situations).

What sort of "non-standard situations"?

I think the MSDN docs are pretty good, except that if you are using
TcpClient and intend to use it with asynchronous API, you're going to
find yourself in the samples for async i/o using the NetworkStream
class, and if I recall correctly those samples aren't actually very good
(I think the NetworkStream.EndRead sample doesn't even work).

A better bet would be to look at the samples for the async methods on
the Socket class. I think those samples are pretty good. You may find
that you'll prefer to use the Socket class directly anyway (there's no
reason not to use TcpClient, except that a Socket turns out to be very
similar to a Stream anyway when dealing with TCP, and so there's not
really any practical difference between using Socket directly and using
the NetworkStream you get from a TcpClient).

I don't think the MSDN samples will cover "non-standard situations", but
that's what the newsgroups are for :). As long as you stay away from
the samples that come with NetworkStream, I think MSDN should give you a
good start though.

Pete
 
A

Armin Zingler

Peter Duniho said:
What sort of "non-standard situations"?

My current issue is how to asynchronously cancel sending without throwing
an exception and how to find out how many bytes have already been sent.

You don't have to make any efforts to elaborate on this (partially it's
clear meanwhile). Just wanted to know whether anybody has a link where I can
read it up on my own. I don't have more specific questions currently but I
think that more will come. :)

Thank you


Armin
 
P

Peter Duniho

Armin said:
My current issue is how to asynchronously cancel sending without throwing
an exception and how to find out how many bytes have already been sent.

You don't have to make any efforts to elaborate on this

Well, I will anyway, since the answer may not be what you expect. :)

The only way to cancel data you've already sent is to close the
connection. This can be done at any time, including while you have an
outstanding asynchronous i/o request.

The only way to find out how many bytes have already been sent after you
close the connection is to ask the recipient. There is no reliable way
using TCP/IP, regardless of what API you use, to provide that information.

I don't think you're likely to find sample code that addresses these
specific scenarios. The first issue is trivial to deal with (close the
socket), and the latter is too closely dependent on the exact
application protocol.

Pete
 
P

Peter Duniho

Peter said:
The only way to cancel data you've already sent is to close the
connection. This can be done at any time, including while you have an
outstanding asynchronous i/o request. [...]

Sorry, I think I wasn't very clear here (though from the second part of
my message, maybe you've inferred the correct meaning).

You can't really cancel the data with certainty. You can close the
connection, and that will possibly abort a transmission that's taking
place. But you have no way to _guarantee_ that data you've already
tried to send won't be sent. It might be or it might not be, and even
if you don't receive notification of completion of the send, it's still
possible that the data will have been sent successfully.

My statement is correct, but re-reading it, it seems to imply that
closing the connection _will_ for sure cancel the data. That's not
actually the case. It _might_ cancel data you've already sent, and
that's the only way to accomplish that, but there's no guarantee that it
will actually work.

Sorry for any confusion.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top