调用 BeginAcceptTcpClient 后停止 TcpListener

2024-03-13

我有这个代码...

internal static void Start()
{
    TcpListener listenerSocket = new TcpListener(IPAddress.Any, 32599);
    listenerSocket.Start();
    listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}

然后我的回调函数看起来像这样......

private static void AcceptClient(IAsyncResult asyncResult)
{
    MessageHandler handler = new MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
    ThreadPool.QueueUserWorkItem((object state) => handler.Process());
    listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}

现在,我调用 BeginAcceptTcpClient,然后一段时间后我想停止服务器。为此,我一直在调用 TcpListener.Stop() 或 TcpListener.Server.Close()。然而,这两个都执行我的 AcceptClient 函数。当我调用 EndAcceptTcpClient 时,这会引发异常。解决这个问题的最佳实践方法是什么?我可以在调用 stop 后放置一个标志来停止 AcceptClient 的执行,但我想知道我是否遗漏了一些东西。

Update 1

目前我已经通过将代码更改为如下所示来修补它。

private static void AcceptClient(IAsyncResult asyncResult)
{
     if (!shutdown)
     {
          MessageHandler handler = new MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
          ThreadPool.QueueUserWorkItem((object state) => handler.Process());
          listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
     }
}

private static bool shutdown = false;
internal static void Stop()
{
     shutdown = true;
     listenerSocket.Stop();
}

Update 2

我更改了它以实现 Spencer Ruport 的答案。

private static void AcceptClient(IAsyncResult asyncResult)
{
    if (listenerSocket.Server.IsBound)
    {
            MessageHandler handler = new MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
            ThreadPool.QueueUserWorkItem((object state) => handler.Process());
            listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
    }
}

我自己刚刚遇到这个问题,我相信您当前的解决方案不完整/不正确。不保证检查之间的原子性IsBound以及随后的调用EndAcceptTcpClient()。如果侦听器是,您仍然可以获得异常Stop()介于这两个陈述之间。你没有说你得到了什么例外,但我认为这和我得到的例外是一样的,ObjectDisposedException(抱怨底层套接字已被释放)。

您应该能够通过模拟线程调度来检查这一点:

  • 在后面的行上设置断点IsBound检查你的回调
  • 冻结遇到断点的线程(“线程”窗口 -> 右键单击​​“冻结”)
  • 运行/触发调用的代码TcpListener.Stop()
  • 闯入并逐步通过EndAcceptTcpClient()称呼。你应该看到ObjectDisposedException.

IMO 理想的解决方案是微软抛出一个不同的异常EndAcceptTcpClient在这种情况下,例如ListenCanceledException或类似的东西。

事实上,我们必须从以下内容推断发生了什么ObjectDisposedException。只需捕获异常并采取相应的行为即可。在我的代码中,我默默地吃掉了异常,因为我在其他地方有代码正在执行真正的关闭工作(即调用的代码)TcpListener.Stop()首先)。无论如何,您应该已经在该区域进行了异常处理,因为您可以获得各种SocketExceptions。这只是将另一个 catch 处理程序添加到该 try 块上。

我承认我对这种方法感到不舒服,因为原则上捕获可能是误报,其中存在真正的“坏”对象访问。但另一方面,没有太多的对象访问EndAcceptTcpClient()否则可能会触发此异常的调用。我希望。

这是我的代码。这是早期/原型的东西,忽略控制台调用。

    private void OnAccept(IAsyncResult iar)
    {
        TcpListener l = (TcpListener) iar.AsyncState;
        TcpClient c;
        try
        {
            c = l.EndAcceptTcpClient(iar);
            // keep listening
            l.BeginAcceptTcpClient(new AsyncCallback(OnAccept), l);
        }
        catch (SocketException ex)
        {
            Console.WriteLine("Error accepting TCP connection: {0}", ex.Message);

            // unrecoverable
            _doneEvent.Set();
            return;
        }
        catch (ObjectDisposedException)
        {
            // The listener was Stop()'d, disposing the underlying socket and
            // triggering the completion of the callback. We're already exiting,
            // so just return.
            Console.WriteLine("Listen canceled.");
            return;
        }

        // meanwhile...
        SslStream s = new SslStream(c.GetStream());
        Console.WriteLine("Authenticating...");
        s.BeginAuthenticateAsServer(_cert, new AsyncCallback(OnAuthenticate), s);
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

调用 BeginAcceptTcpClient 后停止 TcpListener 的相关文章

  • 如何尝试/捕获所有异常

    我正在完成由其他人启动的 UWP 应用程序 该应用程序经常崩溃 我总是陷入困境应用程序 at if global System Diagnostics Debugger IsAttached global System Diagnostic
  • C# 正则表达式用于查找 中具有特定结尾的链接

    我需要一个正则表达式模式来查找字符串 带有 HTML 代码 中的链接 以获取文件结尾如 gif 或 png 的链接 示例字符串 a href site com folder picture png target blank picture
  • 从 C 结构生成 C# 结构

    我有几十个 C 结构 我需要在 C 中使用它们 典型的 C 结构如下所示 typedef struct UM EVENT ULONG32 Id ULONG32 Orgin ULONG32 OperationType ULONG32 Size
  • mprotect 之后 malloc 导致分段错误

    在使用 mprotect 保护内存区域后第一次调用 malloc 时 我遇到分段错误 这是执行内存分配和保护的代码片段 define PAGESIZE 4096 void paalloc int size Allocates and ali
  • SFINAE 如何使用省略号?

    过去 当使用 SFINAE 选择构造函数重载时 我通常使用以下内容 template
  • 如何使用MySqlCommand和prepare语句进行多行插入?(#C)

    Mysql 给出了如何使用准备语句和 NET 插入行的示例 http dev mysql com doc refman 5 5 en connector net programming prepared html http dev mysq
  • 如何生成 appsettings..json 文件?

    我有一个 ASP NET Core 2 WebAPI 它将部署在以下环境中 INT QA STAGE 生产环境 基于上述 我需要有appsettings
  • 对 boost 库的依赖项没有完整路径

    我已经成功构建了动态库 依赖于使用自定义前缀构建和安装的 boost 库 b2 install prefix PREFIX 然而 当我跑步时otool L在我的库中 我得到如下输出 libboost regex dylib compatib
  • 将带有 glut 的点击坐标添加到向量链接列表中

    我想创建一个向量链接列表 并在 GLUT 库的帮助下获取点击的位置并将它们附加到链接列表中 这些是我写的结构 typedef struct vector int x int y Vector typedef struct VectorLis
  • libxml2 xmlChar * 到 std::wstring

    libxml2似乎将所有字符串存储在 UTF 8 中 如xmlChar xmlChar This is a basic byte in an UTF 8 encoded string It s unsigned allowing to pi
  • 预处理后解析 C++ 源文件

    我正在尝试分析c 使用我定制的解析器的文件 写在c 在开始解析之前 我想摆脱所有 define 我希望源文件在预处理后可以编译 所以最好的方法是运行C Preprocessor在文件上 cpp myfile cpp temp cpp or
  • 分配器感知容器和propagate_on_container_swap

    The std allocator traits模板定义了一些常量 例如propagate on container copy move assign让其他容器知道它们是否应该在复制或移动操作期间复制第二个容器的分配器 我们还有propag
  • C++11 动态线程池

    最近 我一直在尝试寻找一个用于线程并发任务的库 理想情况下 是一个在线程上调用函数的简单接口 任何时候都有 n 个线程 有些线程比其他线程完成得更快 并且到达的时间不同 首先我尝试了 Rx 它在 C 中非常棒 我还研究了 Blocks 和
  • C# 中的常量和只读? [复制]

    这个问题在这里已经有答案了 可能的重复 const 和 readonly 之间有什么区别 https stackoverflow com questions 55984 what is the difference between cons
  • ASP.NET JQuery AJAX POST 返回数据,但在 401 响应内

    我的应用程序中有一个网页 需要调用我设置的 Web 服务来返回对象列表 这个调用是这样设置的 document ready function var response ajax type POST contentType applicati
  • 从 Delphi 调用 C# dll

    我用单一方法编写了 Net 3 5 dll 由Delphi exe调用 不幸的是它不起作用 步骤 1 使用以下代码创建 C 3 5 dll public class MyDllClass public static int MyDllMet
  • C 中带有指针的结构的内存开销[重复]

    这个问题在这里已经有答案了 我意识到当我的结构包含指针时 它们会产生内存开销 这里有一个例子 typedef struct int num1 int num2 myStruct1 typedef struct int p int num2
  • Visual Studio 2017 完全支持 C99 吗?

    Visual Studio 的最新版本改进了对 C99 的支持 最新版本VS2017现在支持所有C99吗 如果没有 C99 还缺少哪些功能 No https learn microsoft com en us cpp visual cpp
  • 受限 AppDomain 中的代码访问安全异常

    Goal 我需要在权限非常有限的 AppDomain 中运行一些代码 它不应该访问任何花哨或不安全的内容 except对于我在其他地方定义的一些辅助方法 我做了什么 我正在创建一个具有所需基本权限的沙箱 AppDomain 并创建一个运行代
  • 类中不允许使用不完整类型,但类模板中允许使用不完整类型

    以下为无效代码 struct foo struct bar bar x error field x has incomplete type struct bar int value 42 int main return foo x valu

随机推荐