c# - 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。
解决方案
为什么我的服务器正在终止,即使 EndAcceptTcpClient 正在产生异常并且我已经在处理异常。
您正在处理(和记录)SocketException
. 但是然后你DoAcceptTcpClientCallback
只是继续运行client
set to null
。而不是继续,您应该从回调中返回(因为套接字不再可行)。
为什么在 15 秒后超时 AuthenticateAsServerAsync 会阻止侦听器接受新连接。我必须设置一些超时时间,因为我们不能永远等待这个 API。
每当您的套接字操作超时时,您都应该关闭套接字。对于像Task.WhenAny
. 这种“超时”实际上并没有停止通话AuthenticateAsServerAsync
。您需要关闭套接字以强制AuthenticateAsServerAsync
停止操作。
推荐阅读
- nginx - 通过 nginx 代理请求不起作用,错误 404
- javascript - 表格提交在切换点击
- excel - Excel 加载为给定单元格值分配的上一条记录
- python - 如何使用 Python 从 json 文档中提取多个字段?
- macos - PyCharm 在 MacBook Air 上意外退出
- javascript - 如何首先给 Anime.js 元素一个打开动画,然后是一个循环动画?
- reactjs - 哨兵/浏览器是否有包含参数以仅包含特定文件?
- sonarqube - 了解 SonarQube 及其测试覆盖率
- javascript - 使用 javascript 从 url 获取后更改 html 内容
- python - 如何将列的类型从字符串转换为日期