(显然)Gracefully Closed UDPClient 使套接字阻塞

Posted

技术标签:

【中文标题】(显然)Gracefully Closed UDPClient 使套接字阻塞【英文标题】:(Apparently) Gracefully Closed UDPClient leaves the socket blocked 【发布时间】:2014-06-27 05:06:37 【问题描述】:

以下代码尽管显然关闭了 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 时不让它阻塞):

    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 表示通常只允许使用一种端口/地址。

【问题讨论】:

会不会,当您单击按钮时,listener.Client 是什么null?那时你不会打电话给listener.Close() @Sinatr 我在调试中检查过,但事实并非如此。 .Close() 应该完全处理该对象。它必须是别的东西,比如实际上没有正确关闭套接字(可能是由于一些异常没有处理而只是跳过)。我不知道,因为我刚刚使用 .BeginReceive 异步方法来处理数据。 ;p 【参考方案1】:

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

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

    运行两个应用程序实例并在两者中单击开始。 删除两次出现的 Shutdown 和 Close(Stop 不会关闭套接字)。 正在运行应用程序,打开套接字,关闭应用程序而不关闭套接字,再次运行应用程序,尝试打开套接字。

我在您的代码中所做的唯一更改是删除 ParseData(...) 行并将一些端口添加到 XSensAvailablePorts 列表。

你能在你明显关闭它之后检查端口是否仍然打开吗?你可以使用netstat -an,或者一个优秀的工具ProcessExplorer。您还可以检查 t_listener 线程是否正确终止(标准任务管理器或 ProcessExplorer 可以帮助您)。

【讨论】:

我无法访问我正在与之通信的硬件/软件(您可能已经从我正在访问 XSens 紧身衣的套接字流以进行身体跟踪的变量名称中了解到),但我下周将检查它。我在这个组件的数据流上遇到了很多问题,我怀疑这可能是服务器的问题出了问题。【参考方案2】:

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

【讨论】:

我没有将对象设置为 null 并等待 GC 释放资源,而是使用 Close() / Dispose() 强制释放资源,但也没有用。【参考方案3】:

我有同样的问题,问题出在 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));

`

安全关闭套接字和线程 (http://msdn.microsoft.com/en-us/library/system.threading.threadstart(v=vs.110).aspx)

【讨论】:

【参考方案4】:

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

【讨论】:

这是答案还是评论?【参考方案5】:

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

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

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

以上是关于(显然)Gracefully Closed UDPClient 使套接字阻塞的主要内容,如果未能解决你的问题,请参考以下文章

FlinkFLink Task did not exit gracefully within 180 + seconds 未解决

UDP的connect

UDP打孔到期[关闭]

我应该使用 TCP 还是 UDP? [关闭]

混合 TCP 和 UDP

如何打开一个UDP端口? [关闭]