我有这段代码......
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);
}
}
答案 0 :(得分:16)
我自己刚遇到这个问题,我相信你当前的解决方案是不完整/不正确的。对IsBound
的检查与随后对EndAcceptTcpClient()
的调用之间无法保证原子性。如果侦听器在这两个语句之间Stop()
',您仍然可以获得异常。你没有说你得到了什么异常,但我认为它与我得到的是同一个,ObjectDisposedException
(抱怨底层套接字已被处理掉)。
您应该可以通过模拟线程安排来检查:
IsBound
检查后的行上设置断点TcpListener.Stop()
EndAcceptTcpClient()
来电。您应该看到ObjectDisposedException
。 IMO理想的解决方案是,在这种情况下,Microsoft会向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);
}
答案 1 :(得分:8)
不,你没有遗漏任何东西。您可以检查Socket对象的IsBound属性。至少对于TCP连接,当套接字正在侦听时,它将被设置为true,并且在您调用close之后,它的值将为false。但是,您自己的实现也可以正常工作。
答案 2 :(得分:1)
试试这个。它没有捕获异常,对我来说很好。
private void OnAccept(IAsyncResult pAsyncResult)
{
TcpListener listener = (TcpListener) pAsyncResult.AsyncState;
if(listener.Server == null)
{
//stop method was called
return;
}
...
}
答案 3 :(得分:0)
我认为所有树都需要,并且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();
}
}
答案 4 :(得分:0)