ASP.NET WebMethod 和半开连接

ASP.NET WebMethod and half-open connections

考虑一些客户端调用 WebMethod 并且在建立连接后主机进程崩溃并且连接变为半打开的情况。 ASP.NET WebMethod 代理会自动检测吗?

我们假设 WebMethod 超时设置为无穷大或某个较长的时间。是否仅在 TCP 保持活动数据包发送时检测到连接错误(默认情况下 Windows 闲置 2 小时后)?

[WebService(Namespace = "http://www.example.com/TestService/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class TestService : System.Web.Services.WebService
{
    [WebMethod]
    public void DoLongWork()
    {
        // long work here
    }
}

Web 引用代理调用:

public void ServiceClient()
{
    var serviceProxy = new MyNamespace.TestService();
    // serviceProxy.Url = ...
    serviceProxy.Timeout = -1; // do not use timeout

    // will it wait for 2 hours (when TCP keep-alive will be sent) if connection was lost after handshake?
    serviceProxy.DoLongWork();
}

更新。

我已经尝试了多种方案,这里是我使用 Wireshark 了解幕后情况的结果。

  1. 通过 IIS 管理器停止 托管 Web 服务器 (IIS) 或终止主机进程 (w3wp.exe):

在这两种情况下,客户端都通过发送 TCP RST 标志进行通知。似乎 Windows 负责关闭 crashed/killed 进程的套接字。在 .NET 中,将抛出 System.Net.WebException

未处理的异常:System.Net.WebException:底层连接已关闭:接收时发生意外错误。 ---> System.IO.IOException: 无法从传输连接读取数据: 现有连接被远程主机强行关闭。 ---> System.Net.Sockets.SocketException: 现有连接被远程主机强行关闭
   在 System.Net.Sockets.Socket.Receive(Byte[] 缓冲区,Int32 偏移量,Int32 大小,SocketFlags socketFlags)
   在 System.Net.Sockets.NetworkStream.Read(字节 [] 缓冲区,Int32 偏移量,Int32 大小)
   --- 内部异常堆栈跟踪结束 ---
   在 System.Net.Sockets.NetworkStream.Read(字节 [] 缓冲区,Int32 偏移量,Int32 大小)
   在 System.Net.PooledStream.Read(字节 [] 缓冲区,Int32 偏移量,Int32 大小)
   在 System.Net.Connection.SyncRead(HttpWebRequest 请求,布尔 userRetrievedStream,布尔 probeRead)
   --- 内部异常堆栈跟踪结束 ---
   在 System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(WebRequest 请求)
   在 System.Web.Services.Protocols.HttpWebClientProtocol.GetWebResponse(WebRequest 请求)
   在 System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(字符串方法名称,对象 [] 参数)
   在 WebMethodAndTCPHalfOpenTest.TestWebReference.TestService.DoLongWork(Int32 秒)
   在 WebMethodAndTCPHalfOpenTest.Program.Main(字符串 [] 参数)
  1. 终止主机进程连接(到主机)丢失后,因此 TCP RST 无法到达客户端: 这里的结果对我来说非常纠结,因为2.5 小时后未检测到断开的连接(半开)并且客户端仍然认为底层 TCP 连接是活动的(6小时后情况相同)。这里是 Sysinternals TCPView 的输出:
TCPView v3.01 - TCP/UDP endpoint viewer Copyright (C) 1998-2010
Mark Russinovich and Bryce Cogswell Sysinternals -
www.sysinternals.com

[TCP] WebMethodAndTCPHalfOpenTest.exe
        PID:    8180
        State:  ESTABLISHED
        Local:  192.168.1.2
        Remote: 5.167.159.81
Windows Version: Microsoft Windows [Version 6.1.7601]

Wireshark 输出:

我在这里检查了我系统上的 KeepAliveTime 注册表设置:

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
但它丢失了,所以应该使用默认的 2 小时( ?).

任何人都可以解释第二点中描述的结果吗?为什么没有发送 TCP keepalive?代理是否为底层套接字 (https://msdn.microsoft.com/en-us/library/windows/desktop/ee470551(v=vs.85).aspx) 明确设置了不同的保持活动行为?

如果主机进程崩溃,连接将关闭。它不会等待 TCP keep-alive 启动。您可以通过旋转您的 WebService 并使其自愿崩溃来轻松测试这种情况。

看起来 keep alive 并没有得到保证,因为 windows 中的设置可能会搞砸(没有注册表值不是全部)。

据此: Windows TCP socket has SO_KEEPALIVE enabled by default?

看来您不应依赖此行为并设置超时。如果您使用的是 soap 请求,您始终可以调用异步请求,然后根据客户端可配置值等待请求,并对超时进行良好的逻辑处理。