调用BeginAcceptTcpClient后停止TcpListener

我有这个代码……

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

然后我的回叫function看起来像这样……

 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时抛出exception。 围绕这个的最佳做法是什么? 一旦我调用了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); } } 

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

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

  • 在回调中的IsBound检查后,在行上设置断点
  • 冻结到达断点的线程(线程窗口 – >右键单击,“冻结”)
  • 运行/触发调用TcpListener.Stop()的代码
  • 中断并逐步执行EndAcceptTcpClient()调用。 您应该看到ObjectDisposedException

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

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

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

这是我的代码。 这是早期/原型的东西,忽略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。 但是,您自己的实现也可以正常工作。

试试这个。 它没有捕获exception,对我来说很好。

 private void OnAccept(IAsyncResult pAsyncResult) { TcpListener listener = (TcpListener) pAsyncResult.AsyncState; if(listener.Server == null) { //stop method was called return; } ... } 

我认为所有树都需要,并且BeginAcceptTcpClient的重启应该放在EndAcceptTcpClient的tryctach之外。

  private void AcceptTcpClientCallback(IAsyncResult ar) { var listener = (TcpListener)ar.AsyncState; //Sometimes the socket is null and somethimes the socket was set if (listener.Server == null || !listener.Server.IsBound) return; TcpClient client = null; try { client = listener.EndAcceptTcpClient(ar); } catch (SocketException ex) { //the client is corrupt OnError(ex); } catch (ObjectDisposedException) { //Listener canceled return; } //Get the next Client listener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), listener); if (client == null) return; //Abort if there was an error with the client MyConnection connection = null; try { //Client-Protocoll init connection = Connect(client.GetStream()); } catch (Exception ex) { //The client is corrupt/invalid OnError(ex); client.Close(); } } 

这是一个简单的示例,如何开始监听,如何异步处理请求,以及如何停止监听。

完整的例子。

 public class TcpServer { #region Public. // Create new instance of TcpServer. public TcpServer(string ip, int port) { _listener = new TcpListener(IPAddress.Parse(ip), port); } // Starts receiving incoming requests. public void Start() { _listener.Start(); _ct = _cts.Token; _listener.BeginAcceptTcpClient(ProcessRequest, _listener); } // Stops receiving incoming requests. public void Stop() { // If listening has been cancelled, simply go out from method. if(_ct.IsCancellationRequested) { return; } // Cancels listening. _cts.Cancel(); // Waits a little, to guarantee // that all operation receive information about cancellation. Thread.Sleep(100); _listener.Stop(); } #endregion #region Private. // Process single request. private void ProcessRequest(IAsyncResult ar) { //Stop if operation was cancelled. if(_ct.IsCancellationRequested) { return; } var listener = ar.AsyncState as TcpListener; if(listener == null) { return; } // Check cancellation again. Stop if operation was cancelled. if(_ct.IsCancellationRequested) { return; } // Starts waiting for the next request. listener.BeginAcceptTcpClient(ProcessRequest, listener); // Gets client and starts processing received request. using(TcpClient client = listener.EndAcceptTcpClient(ar)) { var rp = new RequestProcessor(); rp.Proccess(client); } } #endregion #region Fields. private CancellationToken _ct; private CancellationTokenSource _cts = new CancellationTokenSource(); private TcpListener _listener; #endregion }