App hangs for 20 secs on exit after TcpClient BeginConnect

北城余情 提交于 2019-12-08 07:10:38

问题


I have problem with strange behavior of TcpClient.BeginConnect() and TcpClient.EndConnect() methods.

I use BeginConnect() to add timeout to Connect() method (see extension method code below). If I get connected I call EndConnect() and every thing is ok, but If I get timeout I can't call EndConnect() because it will block for about 20 seconds, so I just call this non-blocking code:

result.AsyncWaitHandle.Close();
result.AsyncWaitHandle.Dispose();
client.Close();

Problem is when I close the form right after getting timeout (after running above code) my app will "hang" for about 20 seconds.

Is there any way to prevent this hanging?

It is unpleasant only on app exit so, it can be some dirty kill if nothing else is possible ;]

Some notes:

  • Hanging happens only if I can't connect to the server.
  • On exit the form gets closed, just some process is still running in the background.
  • While app is hanging if I press Pause button in VS2013 I'm not taken to any code, so I suppose it is some automatically created thread where hanging code (EndConnect()?) is called.
  • When I wait for about 20 seconds after timeout (app is responsive) and then I close the form there is no hanging - so this blocking code (EndConnect()?) is automatically called from some other thread.
  • If I call method TcpClient.Connect() (without timeout) I get blocked for about 20 seconds, but there is no hanging on exit.
  • If I run this non-blocking code on timeout:

                client.Client.Close();
                client.Client.Dispose();
                client.Close();
    

    it will still hang on exit.

  • Even if I change BeginConnect(ip, port, null, null) to BeginConnect(ip, port, new AsyncCallback(ConnectCallback), null) and call EndConnect() and Close() on the client in ConnectCallback, app hungs on exit (ConnectCallback gets called before exit and its code is not hanging).
  • I wrote about 20 seconds, but it is more likely like (20 - timeout) secs. In my code I I have timeout = 1 sec, so in this case app hangs for about 20 secs.

Below is my connection code:

    public bool Connect(string localMachineName)
    {
        // Get ips for local machine
        IPAddress[] ips;
        try
        {
            ips = Dns.GetHostEntry(localMachineName).AddressList.Where(
                    o => o.AddressFamily == AddressFamily.InterNetwork).ToArray();
        }
        catch { return false; }

        // loop and try to connect
        foreach (IPAddress ip in ips)
        {
            TcpClient c = new TcpClient();
            try
            {
                c.NoDelay = true;
                c.SendTimeout = 1000;     // this seems to not change anything
                c.ReceiveTimeout = 1000;  // this seems to not change anything

                // call connect with timeout - extension method
                // this leave after 1 sec (as required) but hangs app on exit
                c.Connect(ip, 8888, new TimeSpan(0, 0, 1));

                // if you comment previous line and uncomment code line below
                // it will block for about 20 secs and not hangs on apps exit
                // c.Connect(ip, 8888);

                if (c.Connected)
                {
                    MyClient = c;
                    break;
                }
                else
                {
                    MyClient = null;
                }
            }
            catch
            {
                c.Close();
                MyClient = null;
            }
        }
        return (MyClient != null) && (MyClient.Connected);
    }

Code above is using extension method to connect with timeout, code of this method is below (based on the code from some SO answer):

    public static void Connect(this TcpClient client, IPAddress ip, int port, TimeSpan timeout)
    {
        // begin async connection
        IAsyncResult result = client.BeginConnect(ip, port, null, null);

        if (result.CompletedSynchronously)
        {
            client.EndConnect(result);
            return;
        }

        try
        {
            // wait until connected or timeouted or app shutdown
            WaitHandle[] handles = new WaitHandle[] { result.AsyncWaitHandle, shutdownEvent };
            int index = WaitHandle.WaitAny(handles, timeout);
            if (index == 0)
            {
                // connected
                client.EndConnect(result);
            }
            else if ((index == 1) || (index == WaitHandle.WaitTimeout))
            {
                // timeout or app shutdown

                /* 
                 * Enabling this will block on EndConnect for about 15-20 seconds
                client.EndConnect(result);
                client.Close();
                return;
                */

                /*
                 * Alternatively, after commenting above I tried this code,
                 * it doesn't block, but then it hangs for about 20 secs on app exit
                client.Client.Close();
                client.Client.Dispose();
                */

                // this doesn't block, but then it hangs for about 20 secs on app exit
                result.AsyncWaitHandle.Close();
                result.AsyncWaitHandle.Dispose();
                client.Close();
            }
        }
        catch (Exception e)
        {
            client.Close();
            throw new Exception("Connecting with timeout error: " + e.Message);
        }
    }

I search a lot about this issue, but can't see any info related to this specific problem. Please help!

来源:https://stackoverflow.com/questions/27417990/app-hangs-for-20-secs-on-exit-after-tcpclient-beginconnect

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!