我正在使用相当基本的服务器/客户端设置,两者都位于同一网络上。它们通过TCP / IP通过Winsock2 阻塞套接字进行通信,并且完全正常。
但是,对于下面描述的方案,客户端有时会看到中止连接终止(RST)。它大约在100次中的99次,但最后一次令人烦恼地失败了一些测试,因此,我的整个构建。它在何时何地发生是完全不可预测的,因此复制问题迄今为止还没有找到。
如果我正确理解the relevant MSDN page,阻止套接字的名义连接终止序列可以概括为:
Client | Server
-----------------------------
shutdown(SD_SEND) |
| send() response data
i=recv() until i==0 | shutdown(SD_SEND)
closesocket() | closesocket()
在我的设置中,有必要
expensive_operation()
)取决于接收数据的一部分(比如512字节)是否包含触发值。服务器是单线程的,因此expensive_operation()
有效地阻止recv()
数据流直到expensive_operation()
完成0xDEADBEEF
。 我的客户端已实施,以便始终发送最后的sentinel值,因此在发送后,不会发送其他数据:
send( "data data data 0xDEADBEEF" )
到服务器shutdown(SD_SEND)
< -------失败发生在这里 recv()
直到收到0字节closesocket()
每当服务器收到0xDEADBEEF
时,它都会确认关机请求并继续终止:
recv()
512字节数据或返回0字节expensive_operation()
并返回步骤1,否则继续send( confirmation )
到客户shutdown(SD_SEND)
closesocket()
我可以理解,如果客户端打算在标记之后发送更多数据,这将导致中断连接终止 - 因为服务器主动终止连接。这完全是预期的,并且在设计上,我确实可以可靠地重现这种行为。
然而,在名义上的情况下,序列中的前哨总是最后,确实始终发生,如相关日志条目所证明的那样,确实是优雅的连接终止大部分时间都按预期发生。但并不总是 ......
正如我所说,它是随机发生的,所以我无法生成可靠地重现问题的代码片段。唯一一致的是,在客户端调用shutdown()
时总是会发生失败......
我怀疑它更多的是设计缺陷,或者我尚未处理的某些同步问题,而不是代码问题(尽管我很乐意提供相关代码片段)。
有什么明显的东西我可以俯瞰这里吗?
答案 0 :(得分:1)
有几种方法可以激发RST在发送端故意这样做,我不会在这里透露:
ECONNRESET.
ECONNRESET.
这两个都表明应用程序协议错误。
在你的情况下,我会摆脱哨兵。这是多余的。只需关闭套接字以进行输出,或者只是在知道没有更多数据进入时关闭套接字。这会向对等方发送一个完全明确的指示,即没有更多数据,而不要求对等方精确地同步字节为与本地应用程序的字节,这是当前代码中此错误的弱点和可能来源。
您需要发布一些代码才能获得更具体的帮助。
答案 1 :(得分:0)
我无法重现,但我可以想象客户端看到无效终止的用例
client server
send sentinel
send confirmation
shutdown
close socket
shutdown => error writing on closedsocket !
如果客户端进程在发送其sentinel之后被抢占,并且如果服务器很快,则可以落入该场景。这是因为服务器端在关机后立即关闭套接字而不确定客户端是否已完成关闭。恕我直言,你应该做
send( confirmation ) to client
shutdown(SD_SEND)
read until 0 or error
closesocket()
all the normal server shutdown stuff
然后,上部的顺序是确定性的:
client server
send sentinel
send confirmation
shutdown shutdown
recv 0
close socket => cannot happen before client shutdown
recv 0 => socket may be closed server side but it would be harmless