(Apparently) Gracefully Closed UDPClient leaves the socket blocked

余生长醉 提交于 2019-12-10 13:14:52

问题


The following code, despite apparently closing the UDP Socket, leaves it hanging and unable to reconnect to the same address / port.

These are the class variables I use:

    Thread t_listener;
    List<string> XSensAvailablePorts;
    private volatile bool stopT_listener = false;        
    volatile UdpClient listener;
    IPEndPoint groupEP;

I create and launch a new thread with a method which will handle the Socket connection and listening:

private void startSocketButton_Click(object sender, RoutedEventArgs e)
        {
            stopT_listener = false;
            closeSocketButton.IsEnabled = true;
            startSocketButton.IsEnabled = false;
            t_listener = new Thread(UDPListener);
            t_listener.Name = "UDPListenerThread";
            t_listener.Start();
        }

The method is the following (I use a time-out on the Receive in order to not leave it blocked if nothing is being sent on the socket and a Stop is being issued):

    private void UDPListener()
    {
        int numberOfPorts = XSensAvailablePorts.Count();
        int currAttempt = 0;
        int currPort = 0;
        bool successfullAttempt = false;
        while (!successfullAttempt && currAttempt < numberOfPorts)
        {
            try
            {
                currPort = Int32.Parse(XSensAvailablePorts[currAttempt]);
                listener = new UdpClient(currPort);
                successfullAttempt = true;
            }
            catch (Exception e)
            {
                currAttempt++;
            }
        }
        if (successfullAttempt)
        {   
            //timeout = 2000 millis
            listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000);
            //Tried with and without, no change: listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            statusTB.Dispatcher.BeginInvoke((Action)delegate() { statusTB.Text = "Connected on port:" + currPort; });
            groupEP = new IPEndPoint(IPAddress.Parse("143.225.85.130"), currPort);

            byte[] receive_byte_array;
            try
            {
                while (!stopT_listener)
                {
                    try
                    {
                        receive_byte_array = listener.Receive(ref groupEP);
                        if (receive_byte_array.Length == 0 || receive_byte_array == null)
                            continue;

                        ParseData(receive_byte_array, samplingDatagramCounter);

                    }
                    catch (SocketException ex)
                    {
                        if (ex.SocketErrorCode == SocketError.TimedOut)
                            continue;
                    }


                }
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
            finally
            {
                if (listener != null)
                {
                    listener.Client.Shutdown(SocketShutdown.Both);
                    listener.Close();
                }
            }
        }
        statusTB.Dispatcher.BeginInvoke((Action)delegate() { statusTB.Text = "Not Connected"; });
        return;
    }

I order the thread / socket to stop with this method:

private void closeSocketButton_Click(object sender, RoutedEventArgs e)
        {
            stopT_listener = true;
            closeSocketButton.IsEnabled = false;
            startSocketButton.IsEnabled = true;
            t_listener.Join();
            if (listener.Client != null)
            {
                listener.Client.Shutdown(SocketShutdown.Both);
                listener.Close();
            }
            if (t_listener.IsAlive)
            {
                t_listener.Abort();
                statusTB.Text = "Aborted";
            }
            else
                statusTB.Text = "Not Connected";
        }

Despite checking in debug that the socket has been closed, if I retry to connect to the same port, I am unable to do so because it raises a SocketException saying that only one usage of port/address is normally permitted.


回答1:


I put code you provided in a simple form to run it and... I cannot directly reproduce your problem. I haven't send any data to the client though, but as far as I understand it shouldn't change anything as it's UDP and we're investigating (re)opening socket, not transmitting data.

When clicking Start/Stop buttons the socket is always properly opened and closed, reopening works as intended. For me the only way to force the SocketException you mentioned was to introduce some obvious misuse of socket logic:

  1. Run two instances of application and click Start in both.
  2. Remove BOTH occurrences of Shutdown and Close (Stop doesn't close socket).
  3. Running app, opening socket, closing the app without closing socket, running app again, trying to open socket.

Only changes I made in your code was removing ParseData(...) line and adding some ports to XSensAvailablePorts list.

Can you check if the port is still open after you apparently close it? You can use netstat -an, or an excellent tool ProcessExplorer. You can also check if the t_listener thread is terminating correctly (standard Task Manager or ProcessExplorer can help you).




回答2:


Set the listener object to NULL so the resource is released which should also free the connection.




回答3:


i have same problem, and the problem is in UDPClient.Receive(), she keep the socket in state of used even you call Close/shutdown/... `

try{ // receive loop}
catch{}
 finally {
UDP_Listner.Close();
                    UDP_Listner = null;
                }

EDIT :

t_listener = new Thread(UDPListener);//replace by :
 t_listener = new Thread(new ThreadStart(UDPListener));

`

to safely close socket & thread ( http://msdn.microsoft.com/en-us/library/system.threading.threadstart(v=vs.110).aspx )




回答4:


I have the same problem, I am the safest programmer, I always close everything nicely. yet I found that the .net class does not close the socket fast enough. because if I go slow it doesn't happen, but if I open and close(cleanup fully) and open it fast, I get the same error. especially if the user wants to run the same code again and open the port again.




回答5:


Might be an old answer, but in your attempt to find a usable port, but which failed, i would dispose the listener instance you tested before the next iteration.

            try
            {
                currPort = Int32.Parse(XSensAvailablePorts[currAttempt]);
                listener = new UdpClient(currPort);
                successfullAttempt = true;
            }
            catch (Exception e)
            {
                currAttempt++;

                if(listener != null)
                {
                   listener.Close();
                }
            }



回答6:


I think Bind or reuse can solve this (even if socket is not closed yet it can be reused and no error is thrown) Example code:

udpClient = new UdpClient();
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, p));


来源:https://stackoverflow.com/questions/23564939/apparently-gracefully-closed-udpclient-leaves-the-socket-blocked

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