当前位置:  开发笔记 > 编程语言 > 正文

调用BeginAcceptTcpClient后停止TcpListener

如何解决《调用BeginAcceptTcpClient后停止TcpListener》经验,为你挑选了2个好方法。

我有这个代码......

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,但我想知道我是否遗漏了一些东西.

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

更新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);
    }
}

David Pope.. 17

我自己也遇到了这个问题,我相信你当前的解决方案是不完整/不正确的.检查IsBound和后续调用之间无法保证原子性EndAcceptTcpClient().如果侦听器Stop()位于这两个语句之间,您仍然可以获得异常.你没有说你得到了什么异常,但我认为它与我得到的相同,ObjectDisposedException(抱怨底层套接字已被处理掉).

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

IsBound检查回调后在行上设置断点

冻结到达断点的线程(线程窗口 - >右击,"冻结")

运行/触发调用的代码 TcpListener.Stop()

打破并逐步通过EndAcceptTcpClient()电话.你应该看到了ObjectDisposedException.

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

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

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

这是我的代码.这是早期/原型的东西,忽略Console调用.

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


Spencer Rupo.. 8

不,你没有遗漏任何东西.您可以检查Socket对象的IsBound属性.至少对于TCP连接,当套接字正在侦听时,它将被设置为true,并且在您调用close之后,它的值将为false.但是,您自己的实现也可以正常工作.



1> David Pope..:

我自己也遇到了这个问题,我相信你当前的解决方案是不完整/不正确的.检查IsBound和后续调用之间无法保证原子性EndAcceptTcpClient().如果侦听器Stop()位于这两个语句之间,您仍然可以获得异常.你没有说你得到了什么异常,但我认为它与我得到的相同,ObjectDisposedException(抱怨底层套接字已被处理掉).

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

IsBound检查回调后在行上设置断点

冻结到达断点的线程(线程窗口 - >右击,"冻结")

运行/触发调用的代码 TcpListener.Stop()

打破并逐步通过EndAcceptTcpClient()电话.你应该看到了ObjectDisposedException.

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

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

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

这是我的代码.这是早期/原型的东西,忽略Console调用.

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



2> Spencer Rupo..:

不,你没有遗漏任何东西.您可以检查Socket对象的IsBound属性.至少对于TCP连接,当套接字正在侦听时,它将被设置为true,并且在您调用close之后,它的值将为false.但是,您自己的实现也可以正常工作.

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有