调用BeginAcceptTcpClient后停止的TcpListener [英] Stopping a TcpListener after calling BeginAcceptTcpClient

查看:2514
本文介绍了调用BeginAcceptTcpClient后停止的TcpListener的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这样的代码...

 内部静态无效的start()
{
的TcpListener listenerSocket =新的TcpListener(IPAddress.Any,32599);
listenerSocket.Start();
listenerSocket.BeginAcceptTcpClient(新的AsyncCallback(AcceptClient),NULL);
}



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

 私有静态无效AcceptClient(IAsyncResult的asyncResult)
{
的MessageHandler处理=新的MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
ThreadPool.QueueUserWorkItem((对象状态)=> handler.Process());
listenerSocket.BeginAcceptTcpClient(新的AsyncCallback(AcceptClient),NULL);
}

现在,我打电话BeginAcceptTcpClient,那么一段时间后,我想停止服务器。要做到这一点,我一直在呼吁TcpListener.Stop(),或TcpListener.Server.Close()。这两种但是执行我的AcceptClient功能。这就引发当我打电话EndAcceptTcpClient例外。什么是解决这个问题的最佳实践方法是什么?我可以把一个标志停止AcceptClient执行一旦我叫不停,但我不知道如果我失去了一些东西。



更新1



目前我已经改变代码看起来像这样修补它。

 私有静态无效AcceptClient(IAsyncResult的asyncResult)
{
如果
{
的MessageHandler处理=新的MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult))(关机!);
ThreadPool.QueueUserWorkItem((对象状态)=> handler.Process());
listenerSocket.BeginAcceptTcpClient(新的AsyncCallback(AcceptClient),NULL);
}
}

私人静态布尔关机= FALSE;
内部静态无效停止()
{
关机= TRUE;
listenerSocket.Stop();
}



更​​新2



我改成了impliment从斯宾塞Ruport答案。

 私有静态无效AcceptClient(IAsyncResult的asyncResult)
{
如果(listenerSocket.Server.IsBound)
{
的MessageHandler处理=新的MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
ThreadPool.QueueUserWorkItem((对象状态)=> handler.Process());
listenerSocket.BeginAcceptTcpClient(新的AsyncCallback(AcceptClient),NULL);
}
}


解决方案

我恰好碰到了这个问题我自己,我相信您当前的解决方案是不完整/不正确。没有为一个isBound 没有检查之间的原子性的保证,为 EndAcceptTcpClient后续调用()。你仍然可以得到一个异常,如果侦听器是停止()'这两个语句D之间。你没有说你得到什么异常,但我认为这是同一个,我得到,的ObjectDisposedException (抱怨基础套接字已被释放)。



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




  • 设置>右键 - 对一个isBound 检查在回调

  • 冻结命中断点(线程窗口的线程后,该行断点,冻结)

  • 运行/触发调用 TcpListener.Stop()

  • 代码
  • 打破,并通过 EndAcceptTcpClient()呼叫步骤。您应该看到的ObjectDisposedException



国际海事组织理想的解决方案将是微软从 EndAcceptTcpClient 在这种情况下抛出一个不同的异常,例如: ListenCanceledException 或类似的东西。



正因为如此,我们推断出什么情况发生在的ObjectDisposedException 。刚刚捕获异常,并相应的行为。在我的代码我默默地吃例外,因为我有一些代码在做真正的关机工作(即调用 TcpListener.Stop()摆在首位的代码)在其他地方。你应该已经有在这方面的异常处理无论如何,因为你可以得到各种 SocketExceptions 。这只是另一种套结catch处理程序到该try块。



我承认我这个方法不舒服,因为原则渔获可能是假阳性,带正版在有坏对象访问。但在另一方面也有没有太多的物体在 EndAcceptTcpClient()呼叫,否则会引发此异常的访问。我的希望。



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

 私人无效OnAccept(IAsyncResult的IAR)
{
的TcpListener L =(的TcpListener)iar.AsyncState;
的TcpClient℃;

{
C = l.EndAcceptTcpClient(IAR);
//继续听
l.BeginAcceptTcpClient(新的AsyncCallback(OnAccept),L);
}
赶上(SocketException前)
{
Console.WriteLine(错误接受的TCP连接:​​{0},ex.Message);

//不可恢复
_doneEvent.Set();
的回报;
}
赶上(的ObjectDisposedException)
{
//监听器是停止()处理,处置底层套接字和
//触发回调完成。我们已经退出,
//所以才返回。
Console.WriteLine(听取消。);
的回报;
}

// ...其间
Ss​​lStream S =新的SslStream(c.GetStream());
Console.WriteLine(认证...);
s.BeginAuthenticateAsServer(_​​cert,新的AsyncCallback(OnAuthenticate),S);
}


I have this code...

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

Then my call back function looks like this...

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

Now, I call BeginAcceptTcpClient, then some time later I want to stop the server. To do this I have been calling TcpListener.Stop(), or TcpListener.Server.Close(). Both of these however execute my AcceptClient function. This then throws an exception when I call EndAcceptTcpClient. What is the best practice way around this? I could just put a flag in to stop the execution of AcceptClient once I have called stop, but I wonder if I am missing something.

Update 1

Currently I have patched it by changing the code to look like this.

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

I changed it to impliment the answer from 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);
    }
}

解决方案

I just ran into this issue myself, and I believe your current solution is incomplete/incorrect. There is no guarantee of atomicity between the check for IsBound and the subsequent call to EndAcceptTcpClient(). You can still get an exception if the listener is Stop()'d between those two statements. You didn't say what exception you're getting but I assume it's the same one I'm getting, ObjectDisposedException (complaining that the underlying socket has already been disposed).

You should be able to check this by simulating the thread scheduling:

  • Set a breakpoint on the line after the IsBound check in your callback
  • Freeze the thread that hits the breakpoint (Threads window -> right click, "Freeze")
  • Run/trigger the code that calls TcpListener.Stop()
  • Break in and step through the EndAcceptTcpClient() call. You should see the ObjectDisposedException.

IMO the ideal solution would be for Microsoft to throw a different exception from EndAcceptTcpClient in this case, e.g. ListenCanceledException or something like that.

As it is, we have to infer what's happening from the ObjectDisposedException. Just catch the exception and behave accordingly. In my code I silently eat the exception, since I have code elsewhere that's doing the real shutdown work (i.e. the code that called TcpListener.Stop() in the first place). You should already have exception handling in that area anyway, since you can get various SocketExceptions. This is just tacking another catch handler onto that try block.

I admit I'm uncomfortable with this approach since in principle the catch could be a false positive, with a genuine "bad" object access in there. But on the other hand there aren't too many object accesses in the EndAcceptTcpClient() call that could otherwise trigger this exception. I hope.

Here's my code. This is early/prototype stuff, ignore the Console calls.

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

这篇关于调用BeginAcceptTcpClient后停止的TcpListener的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆