首页 > 解决方案 > Windows 服务在执行 EndAcceptTcpClient 时意外终止 - 即在接受新的套接字客户端时

问题描述

我有一个正在侦听端口 25101 的 Windows 服务 ( Server )。客户端可以通过此端口连接到服务器。这是一个安全端口,因此一旦建立 TCP/IP/Socket 级别的连接,客户端和服务器就需要 TLS 握手。

Windows 服务是使用 C# 在 .NET 框架中编写的。

源代码的摘要写在下面。


服务器代码


TcpClient client = null;

try
{
// This is where the service process is terminating  
client = m_listener.EndAcceptTcpClient(ar); 
}
catch (Exception ex)
{
  s_log.Error(ex, "Exception EndAcceptTcpClient");
}

try
{
  m_listener.BeginAcceptTcpClient(DoAcceptTcpClientCallback, m_listener);
}
catch (Exception ex)
{
  s_log.Error(ex, "Exception BeginAccept");
}

在这里,它将验证客户端/服务器 TLS 握手并获取 SSL 流。然后它将用于发送和接收字节。在下面的代码摘录中,我只是编写代码行来获取 SSL 流。

int timeout = 15000;
 // Create the SslStream using the client's network stream.
 var sslStream = new SslStream(client.GetStream(), false);

 // Authenticate the server but don't require the client to authenticate.
 Task serverAuth = sslStream.AuthenticateAsServerAsync(serverCertificate, false, SslProtocols.None, true);

 if (await Task.WhenAny(serverAuth, Task.Delay(timeout)) == serverAuth)
 {
   // task completed within timeout
   return sslStream;
 }
 else
 {
   // timeout logic
   throw new System.Exception("AuthenticateAsServerAsync timeout after " + timeout + " millisoconds");
 }

问题陈述


在正常情况下,它工作正常。服务器保持运行数周和数月没有任何问题,数以百万计的套接字客户端连接打开然后关闭,没有任何问题。

在极少数情况下, AuthenticateAsServerAsync函数不会获取 SSL 流。所以15秒后我超时了。但在此之后,侦听器也停止接受新连接,即使服务器启动并运行,也没有新客户端可以与服务器连接。这是我原来的问题。

由于这种情况非常罕见,所以我在 .NET C# 中编写了一个测试应用程序,它是 TCP/IP 客户端,向服务器发出大量连接请求。大约每秒 10 次。我正在通过批处理文件停止和启动这个测试应用程序。使用这个测试应用程序(运行大约 3-4 小时后) - 我能够终止服务器进程。

这是正在发生的事情

client = m_listener.EndAcceptTcpClient(ar);

上面的行产生以下异常

2021-04-20 19:48:19.4724|ERROR|OvationServerLib.RelayConnections.DeviceServer.DoAcceptTcpClientCallback: Exception EndAcceptTcpClient System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.EndAccept(Byte[]& buffer, Int32& bytesTransferred, IAsyncResult asyncResult)
   at System.Net.Sockets.Socket.EndAccept(IAsyncResult asyncResult)
   at System.Net.Sockets.TcpListener.EndAcceptTcpClient(IAsyncResult asyncResult)
   at OvationServerLib.RelayConnections.DeviceServer.DoAcceptTcpClientCallback(IAsyncResult ar) in C:\Development\Relay Server\fb4_relay_server\FB4_Relay_Server\Fb4RelayServer\OvationServerLib\RelayConnections\DeviceServer.cs:line 54

但是,在事件查看器中,我看到的异常略有不同,见下文

Application: OvationServerService.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException
   at OvationServerLib.RelayConnections.DeviceServer.DoAcceptTcpClientCallback(System.IAsyncResult)
   at System.Net.LazyAsyncResult.Complete(IntPtr)
   at System.Net.ContextAwareResult.CompleteCallback(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Net.ContextAwareResult.Complete(IntPtr)
   at System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr)
   at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)

所以我的问题是:

1- 为什么我的服务器正在终止,即使EndAcceptTcpClient正在产生异常并且我已经在处理异常。

2- 为什么AuthenticateAsServerAsync在 15 秒后超时会阻止侦听器接受新连接。我必须设置一些超时时间,因为我们不能永远等待这个 API。

标签: c#multithreadingsocketsssltcpclient

解决方案


为什么我的服务器正在终止,即使 EndAcceptTcpClient 正在产生异常并且我已经在处理异常。

您正在处理(和记录)SocketException. 但是然后你DoAcceptTcpClientCallback只是继续运行clientset to null。而不是继续,您应该从回调中返回(因为套接字不再可行)。

为什么在 15 秒后超时 AuthenticateAsServerAsync 会阻止侦听器接受新连接。我必须设置一些超时时间,因为我们不能永远等待这个 API。

每当您的套接字操作超时时,您都应该关闭套接字。对于像Task.WhenAny. 这种“超时”实际上并没有停止通话AuthenticateAsServerAsync。您需要关闭套接字以强制AuthenticateAsServerAsync停止操作。


推荐阅读