Windows 服务在执行 EndAcceptTcpClient 时意外终止 - 即在接受新的套接字客户端时

Posted

技术标签:

【中文标题】Windows 服务在执行 EndAcceptTcpClient 时意外终止 - 即在接受新的套接字客户端时【英文标题】:Windows Service terminated unexpectedly while executing EndAcceptTcpClient - i.e while accepting new socket client 【发布时间】:2021-07-15 14:46:22 【问题描述】:

我有一个正在侦听端口 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- 为什么在 15 秒后超时 AuthenticateAsServerAsync 会阻止侦听器接受新连接。我必须设置一些超时,因为我们不能永远等待这个 API。

【问题讨论】:

我猜,如果EndAcceptTcpClient 抛出,它不会设置client。所以它可能是null。如果您随后捕获 SocketException 并继续使用client,您将遇到NRE。你能以某种方式确认/排除吗? 你是对的,这是一个有效的观点。我会检查并试一试。谢谢 【参考方案1】:

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

您正在处理(和记录)SocketException。但随后您的DoAcceptTcpClientCallback 继续运行,client 设置为null。而不是继续,您应该从回调中返回(因为套接字不再可行)。

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

每当您的套接字操作超时时,您都应该关闭该套接字。对于像Task.WhenAny 这样的“悲观”超时尤其如此。这种“超时”实际上并没有停止AuthenticateAsServerAsync 调用;您需要关闭套接字以强制停止AuthenticateAsServerAsync 操作。

【讨论】:

在 AuthenticateAsServerAsync 操作超时后,我已经关闭了套接字连接。这是一个非常有效的观点。我没有在上面的问题中包含那部分代码,但我肯定会关闭套接字。我也将 ReceiveTimeout 放在套接字上 15 秒,但我仍然需要对此进行测试。

以上是关于Windows 服务在执行 EndAcceptTcpClient 时意外终止 - 即在接受新的套接字客户端时的主要内容,如果未能解决你的问题,请参考以下文章

WAMPServer 在 Windows 10 上无法正常工作,“无法执行服务操作”

windows服务仍在运行但任务没有连续执行

如何安排 C# Windows 服务每天执行任务?

Windows服务器上使用bat定时执行php

c#带有Timer的Windows服务不执行代码

在后台模式下从 Nodejs 执行 VBS(任务计划程序或 Windows 服务)