tags:

views:

139

answers:

2

I'm having trouble implementing a connect timeout using asynchronous socket calls.

The idea being that I call BeginConnect on a Socket object, then use a timer to call Close() on the socket after a timeout period has elapsed.

This works fine as long as the socket is created on the GUI thread - the Close method returns immediately, and the callback method is executed. However, if the socket is created on any other thread, the Close method blocks until the default IP timeout occurs.

Code to reproduce:

private Socket client;

private void button1_Click(object sender, EventArgs e) {
    // Creating the socket on a threadpool thread causes Close to block.
    ThreadPool.QueueUserWorkItem((object state) => {
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IAsyncResult result = client.BeginConnect(IPAddress.Parse("144.1.1.1"), 23, new AsyncCallback(CallbackMethod), client);

        // Wait for 2 seconds before closing the socket.
        if (result.AsyncWaitHandle.WaitOne(2000)) {
            MessageBox.Show("Connected.");
        } else {
            MessageBox.Show("Timed out. Closing socket...");
            client.Close();
            MessageBox.Show("Socket closed.");
        }
    });
}

private void CallbackMethod(IAsyncResult result) {
    MessageBox.Show("Callback started.");
    Socket client = result.AsyncState as Socket;
    try {
        client.EndConnect(result);
    } catch (ObjectDisposedException) {
    }
    MessageBox.Show("Callback finished.");
}

If you remove the QueueUserWorkItem line, creating the socket on the GUI thread, the socket closes instantly without blocking.

Can anyone shed some light on what's going on?
Thanks.

Edit - System.Net trace output seems to be different depending on whether it's being connected on the GUI thread or a different thread:

A: 

Have you tried removing the "MessageBox.Show" calls that you have around the call to Socket.Close()?

feroze
The blocking on Close occurs irrespective of MessageBox.Show.Even setting the [LingerState](http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.lingerstate.aspx) property of the Socket to true with a zero timeout doesn't help.From MSDN: "The following table describes the behavior of the Close method for the possible values of the Enabled property and the LingerTime property stored in the LingerState property."With LingerState enabled and LingerTime set to zero: "Discards any pending data. For connection-oriented socket (TCP, for example), Winsock resets the connection."
Mark
I tried reproing with the above program. I couldnt repro it. The program doesnt hang.
feroze
This occurs for me on Windows 7 and under XP mode, on any version of the .NET Framework. I can reproduce it on a different machine too. The program shouldn't hang, but there should be a delay of around 20 seconds between the "Timed out. Closing socket..." message and the "Socket closed." message.
Mark
hmm. Try getting a tracelog (see http://ferozedaud.blogspot.com/2009/08/tracing-with-systemnet.html) and put the logfile on pastebin.com. I can take a look.
feroze
Thanks for that, I've edited my question to include the trace logs. Seems the underlying socket calls are different depending on which thread is calling them.
Mark
Yeah, I can see in the logfiles that in the case of using the GUI thread, the Socket.Close() operation took 20s to complete. However, are you sure that you are not setting any linger option, or even using the default in the second case? Can you retry it by turning off the liner option? Also, can you get a winsock trace log by following instructions at http://msdn.microsoft.com/en-us/library/bb892100(v=VS.85).aspx
feroze
A: 

What I can't figure is why it closes immediately on the GUI thread. I'd try calling client.Shutdown() before calling client.Close().

I'd also try running the socket object in a regular thread (Thread.Start()) instead of using the ThreadPool. That way you could keep a reference to the thread and call Thread.Interrupt() and/or Thread.Abort() yourself.

sarme
Calling Shutdown before the socket has connected results in a SocketException - "A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied".I'm currently using a single Thread to start many BeginConnects, and then using a System.Timers.Timer to timeout the sockets as required - I can't afford to use a new Thread for every connection. :(
Mark