Properly closing a TcpListener

  • Thread starter Ertugrul Söylemez
  • Start date
E

Ertugrul Söylemez

Hello group,

I'm having difficulty with the TcpListener class. See the code below.
For some reason, as soon as I call the stop() method after having called
start(), suddenly the AsyncCallback is invoked. It runs up to the
lst.EndAcceptTcpClient call, where the program aborts with an
ObjectDisposedException.

I could solve the problem by catching and ignoring the exception, but
this appears incorrect to me. Any ideas?

Thanks,
Ertugrul.

public class DPServer {

private TcpListener listener;
private ServerFunc callback;

public DPServer(ServerFunc f, int port, IPAddress addr) {
listener = new TcpListener(addr, port);
callback = f;
}

public DPServer(ServerFunc f, int port)
: this(f, port, IPAddress.Any) { }

public DPServer(ServerFunc f)
: this(f, 9346, IPAddress.Any) { }

~DPServer() { stop(); }

public void start() {
listener.Start();
listener.BeginAcceptTcpClient(Utils.asyncCbFix2((r, ar) => {
TcpListener lst = (TcpListener)ar.AsyncState;
TcpClient tcp;

tcp = lst.EndAcceptTcpClient(ar);
ThreadPool.QueueUserWorkItem(
obj => { using (tcp) callback(tcp); },
null);
lst.BeginAcceptTcpClient(r, lst);
}), listener);
}

public void stop() {
listener.Stop();
}
}
 
E

Ertugrul Söylemez

Peter Duniho said:
Because that's the normal behavior when you've got an i/o operation
pending on an object that you dispose.

Good to know, thank you.

How did you expect the operation to complete? Why does it seem
incorrect to you to catch and ignore the exception? Inasmuch as you
have no cleanup to do (as far as I can see from your example), and as
the exception is normal behavior for a disposed object with a pending
i/o operation, it seems fine to me.

Because since no request ever came in, I would expect the callback never
to be called. It gets called _because_ of the TcpListener.Stop(). That
appears incorrect to me.


Greets,
Ertugrul.
 
E

Ertugrul Söylemez

Peter Duniho said:
Because that's the normal behavior when you've got an i/o operation
pending on an object that you dispose.

Good to know, thank you.

How did you expect the operation to complete? Why does it seem
incorrect to you to catch and ignore the exception? Inasmuch as you
have no cleanup to do (as far as I can see from your example), and as
the exception is normal behavior for a disposed object with a pending
i/o operation, it seems fine to me.

Because since no request ever came in, I would expect the callback never
to be called. It gets called _because_ of the TcpListener.Stop(). That
appears incorrect to me.


Greets,
Ertugrul.
 
E

Ertugrul Söylemez

Peter Duniho said:
Because since no request ever came in, I would expect the callback
never to be called. It gets called _because_ of the
TcpListener.Stop(). That appears incorrect to me.

Well, that's just not how the async pattern works. Think of the
"Begin..." and "End..." methods as two halves of a single
method. [...]

Microsoft's idea of asynchronous I/O is total gibberish to me. Maybe
someone should write a wrapper library, which makes all this useful.

However, what is your suggestion as to how to abort at the "first
halve"? It's a case, where calling the corresponding End* method does
not make sense anymore. Ignoring exceptions still appears incorrect or
at least extremely bad style to me, so I will for now go the slightly
messier way of using an extra variable.

// Extra property for DPServer:
private bool isActive;

// In the callback:
if (!isActive) return;
tcp = lst.EndAcceptTcpClient(ar);


Greets,
Ertugrul.
 
E

Ertugrul Söylemez

Peter Duniho said:
Because since no request ever came in, I would expect the callback
never to be called. It gets called _because_ of the
TcpListener.Stop(). That appears incorrect to me.

Well, that's just not how the async pattern works. Think of the
"Begin..." and "End..." methods as two halves of a single
method. [...]

Microsoft's idea of asynchronous I/O is total gibberish to me. Maybe
someone should write a wrapper library, which makes all this useful.

However, what is your suggestion as to how to abort at the "first
halve"? It's a case, where calling the corresponding End* method does
not make sense anymore. Ignoring exceptions still appears incorrect or
at least extremely bad style to me, so I will for now go the slightly
messier way of using an extra variable.

// Extra property for DPServer:
private bool isActive;

// In the callback:
if (!isActive) return;
tcp = lst.EndAcceptTcpClient(ar);


Greets,
Ertugrul.
 
E

Ertugrul Söylemez

Peter said:
Or you could just spend a little more time learning it, so that it's not
"total gibberish" any more.

I'm learning it. That's not my point. It's total gibberish, because it
makes doing async stuff painful and error-prone. Leaving all those
responsibilities to the programmer simply is bad design. See cheap
concurrency in Erlang, Haskell and many more languages for how to do it
properly.

It appears like other than unifying some programming languages and
operating systems, .NET has achieved nothing. It's essentially a step,
which other libraries and programming languages/environments have done
decades ago. At least F# appears to be one step into the right
direction. I'm hoping to see it become an official part of Visual
Studio soon. It's scheduled for inclusion in VS 2010, AFAIK. Of course
that won't improve the concurrency model, though.

Of course it does. You can't call "half a method". That's my point.
Once you start the method going, it is your responsibility to let it run
to completion. That means allowing _both_ halves to execute.
Otherwise, you only have called half a method.

Hmm. You "can't" call "half a method"? Apparently in .NET you can. ;)

In the same sense you "can't" leave a socket or file handle open. You
"can't" leave memory unfreed. You "can't" remove USB memory before
unmounting it. You "can't" turn off the computer before shutting down
your operating system.

Like computer manufacturer handbooks seldomly tell you how to shut down
your operating system properly, the .NET documentation doesn't tell me
how to abort a listener. It gave me the intuition that Begin* sets up
some sort of once-only event handler and in fact I would prefer that,
but there seems to be no sane way to do it.

The fact that you don't yet comprehend the async pattern in .NET is no
reason to ignore and/or abuse it.


This is ABSOLUTELY WRONG.

As I wrote in my previous post, if you call the "Begin..." method, you
must call the "End..." method. Otherwise, you will leak memory and
unmanaged resources.

If you don't like the .NET async pattern, then don't use it. At all.
But if you insist on using it, use it correctly.

Good, then I will catch the exception and ignore it only, when isActive
is not set. I don't feel like ignoring exceptions generally just
because the async pattern is badly designed.

You've been very helpful. Thanks a lot.


Greets,
Ertugrul.
 
E

Ertugrul Söylemez

Peter said:
Or you could just spend a little more time learning it, so that it's not
"total gibberish" any more.

I'm learning it. That's not my point. It's total gibberish, because it
makes doing async stuff painful and error-prone. Leaving all those
responsibilities to the programmer simply is bad design. See cheap
concurrency in Erlang, Haskell and many more languages for how to do it
properly.

It appears like other than unifying some programming languages and
operating systems, .NET has achieved nothing. It's essentially a step,
which other libraries and programming languages/environments have done
decades ago. At least F# appears to be one step into the right
direction. I'm hoping to see it become an official part of Visual
Studio soon. It's scheduled for inclusion in VS 2010, AFAIK. Of course
that won't improve the concurrency model, though.

Of course it does. You can't call "half a method". That's my point.
Once you start the method going, it is your responsibility to let it run
to completion. That means allowing _both_ halves to execute.
Otherwise, you only have called half a method.

Hmm. You "can't" call "half a method"? Apparently in .NET you can. ;)

In the same sense you "can't" leave a socket or file handle open. You
"can't" leave memory unfreed. You "can't" remove USB memory before
unmounting it. You "can't" turn off the computer before shutting down
your operating system.

Like computer manufacturer handbooks seldomly tell you how to shut down
your operating system properly, the .NET documentation doesn't tell me
how to abort a listener. It gave me the intuition that Begin* sets up
some sort of once-only event handler and in fact I would prefer that,
but there seems to be no sane way to do it.

The fact that you don't yet comprehend the async pattern in .NET is no
reason to ignore and/or abuse it.


This is ABSOLUTELY WRONG.

As I wrote in my previous post, if you call the "Begin..." method, you
must call the "End..." method. Otherwise, you will leak memory and
unmanaged resources.

If you don't like the .NET async pattern, then don't use it. At all.
But if you insist on using it, use it correctly.

Good, then I will catch the exception and ignore it only, when isActive
is not set. I don't feel like ignoring exceptions generally just
because the async pattern is badly designed.

You've been very helpful. Thanks a lot.


Greets,
Ertugrul.
 

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