|
-
Aug 17th, 2009, 02:35 PM
#1
[RESOLVED] Closing a TcpListener without getting WSACancelBlockingCall exception
I have a background thread that sits waiting for new clients to connect to my TcpListener and when a new client connects it passes it off onto another worker thread and then loops straight back to waiting for another client. Like so:
vb.net Code:
Do While DoListen
Threading.ThreadPool.QueueUserWorkItem(AddressOf ManageSession, SMTPListener.AcceptTcpClient())
Loop
Thats all fine but I want to be able to cancel this 'listening' activity, which I cannot do as AcceptTcpClient is a blocking method. So when I call Stop() on the TcpClient instance I get the following exception:
A blocking operation was interrupted by a call to WSACancelBlockingCall
Obviously I could just handle the exception and ignore it but I'm sure there must be a more proper way of doing it. One thing I am thinking of trying is using the BeginAcceptTcpClient and EndAcceptTcpClient methods but for one thing I'm not sure if that will actually help and for another thing the Begin and End versions of methods have always confused me in the past... I mean for a start how do you know when to call End?
Anyway, I figured this must be a fairly common problem but searching the forum only turned up one result and it wasnt any use. I'll keep playing around and trawling google but just wondered if anyone had come across this before?
-
Aug 17th, 2009, 09:01 PM
#2
Re: Closing a TcpListener without getting WSACancelBlockingCall exception
 Originally Posted by chris128
One thing I am thinking of trying is using the BeginAcceptTcpClient and EndAcceptTcpClient methods but for one thing I'm not sure if that will actually help and for another thing the Begin and End versions of methods have always confused me in the past... I mean for a start how do you know when to call End?
This is presumably why you didn't really take to my suggestion of calling BeginRead in another of your threads.
Calling BeginAcceptTcpClient will presumably solve your problem because your problem is specifically that you're interrupting a blocking call, while asynchronous methods are inherently non-blocking.
The asynchronous model is not all that complicated. When using synchronous methods you call the method and then you use the result when it returns. When using asynchronous methods you call the method and specify a callback, i.e. a method you want invoked when an asynchronous result is available. In that method you call the appropriate End method and then you use the result. The End method returns what the corresponding synchronous method would have returned. For instance, using the synchronous model you might do this:
vb.net Code:
Private Sub Listen() Dim server As New TcpListener(IPAddress.Parse("127.0.0.1"), 12345) server.Start() Dim client = server.AcceptTcpClient() 'Use client here. End Sub
while the asynchronous equivalent would be:
vb.net Code:
Private Sub Listen() Dim server As New TcpListener(IPAddress.Parse("127.0.0.1"), 12345) server.Start() server.BeginAcceptTcpClient(AddressOf Accept, server) End Sub Private Sub Accept(ByVal ar As IAsyncResult) Dim server = DirectCast(ar.AsyncState, TcpListener) Dim client = server.EndAcceptTcpClient(ar) 'Use client here. End Sub
It's important to note that the callback is invoked on a thread pool thread, NOT the thread on which you called the Begin method.
Last edited by jmcilhinney; Aug 17th, 2009 at 09:04 PM.
-
Aug 18th, 2009, 03:16 AM
#3
Re: Closing a TcpListener without getting WSACancelBlockingCall exception
Ah I see, well that doesnt seem very difficult at all I had tried before using the MSDN documentation and failed miserably. Will definitely try that tonight though and let you know how it goes
Oh and no its not why I didnt take to your suggestion about the BeginRead method I just thought you had not quite understood what the problem was with my loop in the other thread.
-
Aug 18th, 2009, 01:53 PM
#4
Re: Closing a TcpListener without getting WSACancelBlockingCall exception
OK so I've got the Async version working but with one big problem - it can only run once. With the blocking method obviously I could just do an infinite loop that kept calling the blocking AcceptTcpClient method because it would only loop round when a new client connects. With this BeginAcceptTcpClient method though it just passes straight over that line obviously, so I didnt put it in a loop for obvious reasons.
In the MSDN documentation they use something I've not come across before: a ManualResetEvent.
vb.net Code:
' Thread signal.
Public Shared tcpClientConnected As New ManualResetEvent(False)
Then when a connection is accepted they call the WaitOne method and then in the callback after they have called EndAcceptTcpClient they reset the ManualResetEvent so that the original thread can continue. So I figure I can just do the same thing but in a loop but is this the standard practice for something like this? I'm just surprised I've never seen anyone mention this class before
EDIT: I have this working the way I want now using the ManualResetEvent but would still be grateful if someone could just confirm whether or not this is a pretty normal way of dealing with this situation here is my current code:
vb.net Code:
'At class level
Private ThreadWaitSignal As New System.Threading.ManualResetEvent(False)
vb.net Code:
'In a method that runs on a background thread
Do While DoListen
ThreadWaitSignal.Reset()
SMTPListener.BeginAcceptTcpClient(AddressOf IncomingConnection, SMTPListener)
ThreadWaitSignal.WaitOne()
Loop
vb.net Code:
'The method that the BeginAcceptTcpClient calls back to when complete
Private Sub IncomingConnection(ByVal ar As IAsyncResult)
Try
Dim Client = DirectCast(ar.AsyncState, TcpListener).EndAcceptTcpClient(ar)
Threading.ThreadPool.QueueUserWorkItem(AddressOf ManageSession, Client)
ThreadWaitSignal.Set()
Catch disposedEx As ObjectDisposedException
Debug.WriteLine(disposedEx.Message)
End Try
End Sub
Oh and if anyone has any ideas how to avoid having to catch that ObjectDisposedException that would be good but google seems to suggest that is all you can do (when you call Stop on the TcpListener then that exception is thrown in the callback)
Last edited by chris128; Aug 18th, 2009 at 02:48 PM.
-
Aug 27th, 2009, 01:28 PM
#5
Re: Closing a TcpListener without getting WSACancelBlockingCall exception
Any thoughts/comments on whether or not using this ManualResetEvent thingy is the best way to go about this?
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|