MSDN声明Socket.Shutdown可以抛出SocketException
。在我的客户端和服务器之间引入负载均衡器后,我最近在生产中遇到过这种情况。但是如果没有负载均衡器,我无法在测试中重现它。你能吗?
一些背景 - 我有一个用C#编写的服务器应用程序,它使用TCP
个套接字与客户端通信。应用程序协议对于服务器来说非常简单:接受连接,读取请求,发送响应,等待客户端关闭(读取期望0字节),关闭。
这段代码多年来一直没有问题。但是,在多台服务器计算机前引入负载均衡器之后,由于服务器调用SocketException
时引发的未处理Socket.Shutdown
,其中一个服务器进程崩溃。特定客户端在等待服务器响应并尝试提前关闭连接时超时。服务器上的异常消息是“远程主机强制关闭现有连接”。客户端执行此操作并不罕见,但显然在负载平衡器之前,服务器在代码中的不同位置引发此错误。它显然是一个服务器错误,修复很明显 - 处理异常。
然而,使用测试客户端应用程序(也用C#编写),我找不到一系列操作,导致服务器在Socket.Shutdown
期间引发异常。似乎负载均衡器对TCP
数据包做了一些不寻常的事情,但是,我仍然不喜欢使用它作为未能重现问题的借口。
我可以在调试中运行服务器和客户端代码,并让WireShark监视数据包。
在客户端,建立连接后,操作为:
Socket.Send() // single call
Socket.Receive() // this one times out in our scenario
Socket.XXX() // various choices as described below
在服务器端,建立连接后,操作为:
1) Socket.Receive() //multiple calls until complete message is received
2) // Processing...
3) Socket.Write() //single call
4) Socket.Receive() // single call expecting 0 bytes
5) Socket.Shutdown()
假设每个电话都包含try..catch(SocketException)
A)如果我在步骤2中暂停服务器,等待客户端超时,并使用Socket.Shutdown(SocketShutDown.Send)
启动客户端关闭,将FIN数据包发送到服务器。当服务器恢复处理时,所有调用都将成功(3到5),因为这是一个完全可以接受的TCP流。
B)如果我在步骤2中暂停服务器,请等待客户端超时,然后再次使用Socket.Shutdown(SocketShutDown.Both)
或Socket.Close()
启动客户端关闭,将FIN数据包发送到服务器。当服务器恢复处理时,步骤3成功,但它导致客户端发送RST数据包作为响应,因为它不接受更多数据。如果此RST在步骤4之前到达,则Socket.Receive
抛出,步骤5成功。如果它在步骤4之后到达,则Socket.Receive
成功(返回0字节),然后步骤5成功。
C)如果客户端设置了“Dont Linger”(Linger启用0超时),并且我在处理过程中暂停服务器,请等待客户端超时,并使用Socket.Shutdown(SocketShutDown.Both)
启动客户端关闭或Socket.Close()
立即将“RST”数据包发送到服务器。当服务器恢复处理时,步骤3和4将失败但步骤5仍然成功。
我认为最令我困惑的是Socket.Shutdown
似乎忽略了我的测试客户端RST数据包,但显然我的负载均衡器能够发送一个未被忽略的RST数据包。我错过了什么?我还能尝试什么?