(显然)优雅地关闭UDPClient使套接字被阻止

时间:2014-05-09 12:41:11

标签: c# .net multithreading sockets udpclient

以下代码尽管显然已关闭UDP套接字,但仍然无法重新连接到同一地址/端口。

这些是我使用的类变量:

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

我用一个处理Socket连接并监听的方法创建并启动一个新线程:

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();
        }

方法如下(我在Receive上使用超时,以便在套接字上没有发送任何内容并且发出Stop时不会阻止它:< / p>

    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;
    }

我使用此方法命令线程/套接字停止:

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";
        }

尽管在调试中检查了套接字是否已关闭,但如果我重试连接到同一端口,我无法这样做,因为它引发SocketException说只有一次使用端口/地址是正常的允许的。

6 个答案:

答案 0 :(得分:0)

我将您提供的代码以简单的形式运行并运行...我无法直接重现您的问题。我没有向客户端发送任何数据,但据我所知,它不应该改变任何东西,因为它是UDP,我们正在调查(重新)打开套接字,而不是传输数据。

单击开始/停止按钮时,套接字始终正确打开和关闭,重新打开按预期工作。 对我来说,强制你提到的SocketException的唯一方法是引入一些明显的套接字逻辑误用:

  1. 运行两个应用程序实例,然后单击两者中的“开始”。
  2. 删除关闭和关闭的两次出现(停止不关闭套接字)。
  3. 运行应用程序,打开套接字,关闭应用程序而不关闭套接字,再次运行应用程序,尝试打开套接字。
  4. 我在代码中所做的更改只是删除ParseData(...)行并将一些端口添加到XSensAvailablePorts列表。

    您是否可以在显然关闭后检查端口是否仍处于打开状态?您可以使用netstat -an或一个出色的工具ProcessExplorer。您还可以检查t_listener线程是否正确终止(标准任务管理器或ProcessExplorer可以帮助您)。

答案 1 :(得分:0)

将侦听器对象设置为NULL,以便释放资源,该资源也应释放连接。

答案 2 :(得分:0)

我有同样的问题,问题出在UDPClient.Receive()中,即使你调用Close / shutdown / ...,也要保持套接字处于使用状态。“

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

编辑:

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

`

安全关闭socket&amp;线程(http://msdn.microsoft.com/en-us/library/system.threading.threadstart(v=vs.110).aspx

答案 3 :(得分:0)

我有同样的问题,我是最安全的程序员,我总是很好地关闭一切。但我发现.net类没有足够快地关闭套接字。因为如果我走得慢,它就不会发生,但是如果我打开和关闭(完全清理)并快速打开它,我会得到同样的错误。特别是如果用户想要再次运行相同的代码并再次打开端口。

答案 4 :(得分:0)

可能是一个旧答案,但在您尝试找到可用端口但失败时,我会在下一次迭代之前处置您测试的侦听器实例。

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

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

答案 5 :(得分:0)

我认为绑定或重用可以解决这个问题(即使套接字没有关闭但它可以重用但不会抛出错误) 示例代码:

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