我有这个代码......
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.但是,您自己的实现也可以正常工作.
我自己也遇到了这个问题,我相信你当前的解决方案是不完整/不正确的.检查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); }
不,你没有遗漏任何东西.您可以检查Socket对象的IsBound属性.至少对于TCP连接,当套接字正在侦听时,它将被设置为true,并且在您调用close之后,它的值将为false.但是,您自己的实现也可以正常工作.