c# - (显然)优雅地关闭 UDPClient 使套接字被阻塞

标签 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正在发行):

    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 表示只有一个端口/地址通常是允许的。

最佳答案

我把你提供的代码放在一个简单的表格中运行它......我不能直接重现你的问题。虽然我没有向客户端发送任何数据,但据我所知,它不应该改变任何东西,因为它是 UDP,我们正在调查(重新)打开套接字,而不是传输数据。

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

  1. 运行两个应用程序实例,然后在两个实例中单击启动。
  2. 同时删除 Shutdown 和 Close(Stop 不会关闭套接字)。
  3. 运行应用程序,打开套接字,关闭应用程序而不关闭套接字,再次运行应用程序,尝试打开套接字。

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

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

关于c# - (显然)优雅地关闭 UDPClient 使套接字被阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23564939/

相关文章:

.net - 修改 System.Drawing.Graphics.DrawString() 中的字距调整

c# - 输入模拟器 C# WPF 在尝试使用它时不断向我抛出错误

c# - 您如何正确使用 UpdatePanel?

c# - 不在引用的程序集中键入 Foo,但我使用的是扩展类型 Foo 的类型 Bar

.net - 如何将 MS Chart for 3.5 更新到 MS Chart 4?

windows - 只要线程的任何句柄打开,线程 ID 是否保持唯一/有效?

c++ - MFC:从另一个线程调用 CWnd 方法是否安全?

c# - 无法在 Silverlight DataGrid 列中应用字符串格式

c# - 嵌套 ItemsControl 的拉伸(stretch)高度

java - 将 ConcurrentHashMap 安全发布到类字段中