(Apparently) Gracefully Closed UDPClient leaves the socket blocked
Asked Answered
E

6

7

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.

Endplay answered 9/5, 2014 at 12:41 Comment(3)
Could it be, what listener.Client is null when you click button? You will not call listener.Close() then.Frost
@Frost I checked in debug and that's not the case.Endplay
.Close() should completely dispose of the object. it must be something else, like not actually closing the socket correctly (possibly due to some exception not handled and simply skipped). i don't know since i've just used the .BeginReceive async method to handle data. ;pMccluskey
S
0

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).

Sympathetic answered 18/5, 2014 at 15:21 Comment(1)
I have no access to the HW/SW I was communicating to (as you may have understood from the name of the variables I am accessing the socket stream of XSens body suit for body tracking), but I will check it next week. I've had so many issues with the data stream of this component that I am suspecting it might be the server's fault something goes haywire.Endplay
M
0

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

Molar answered 24/5, 2014 at 8:35 Comment(1)
Rather than setting the object to null and wait for the GC to free resources, I forced the release of resources with Close() / Dispose() , but didn't work either.Endplay
K
0

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 )

Kittykitwe answered 11/8, 2014 at 14:3 Comment(0)
U
0

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.

Unchaste answered 9/3, 2015 at 7:8 Comment(1)
Is this an answer or a comment?Predator
R
0

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();
                }
            }
Rurik answered 23/5, 2017 at 8:12 Comment(0)
J
0

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));
Junta answered 16/5, 2018 at 7:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.