在发送请求的数据后需要关闭连接时,使用IOCP(重叠的IO模式)的Winsock2出现问题。
我发现如果我发送了一些数据并在发送后立即关闭套接字,那么将不会(或部分发送)数据,因为在关闭之前没有时间发送数据包。
我还发现我现在使用了多种断开连接方法,例如WSA_SendDisconnect()
。
现在,当前的实现方式如下:
发送数据
设置标志,该标志表示发送后关闭连接的意图
当关于发送事件的IOCP事件发生时如果设置了标志并bytecount > 0
,则我再次发送一个空缓冲区,只是为了确保已发送所有数据
当与发送事件有关的IOCP事件发生时如果设置了标志并bytecount == 0
,那么我清除标志并调用
WSA_SendDisconnect()
(缓冲区为空),以发送断开连接消息
bytecount == 0
时,我将调用 closesocket()
并破坏上下文。通过此过程,我几乎可以确保仅在发送完所有数据后套接字才会断开连接,实际上它可以很好地工作,但是当我在极少数情况下对程序进行压力测试(在1000000次测试中有10-15次)出了点问题。 (很难确定确切的问题,该程序可以作为Web服务器使用,并且我正在用apachebench和siege进行压力测试,测试后我得到了一个摘要,有时我看到10-15个失败的请求)
我非常确定失败是因为套接字无法在关闭前发送整个数据包,因此我在socketclose()
之前放置了一些延迟,并且因为我收到的失败请求为零,所以当然并不是解决方案,只是一种过滤掉可能导致问题的原因的方法。
即使我仅在最后一次虚拟写入完成后才开始关闭,我实现的方法显然缺乏一种正确的方法来检测套接字在关闭之前是否已发送所有内容。
仅在将所有发送缓冲区实际写入网络之后,关闭套接字的最佳解决方案是什么?
Ps:我尝试过使用 NoDelay 并设置 LingerState ,它们没有帮助。
答案 0 :(得分:0)
好的,我可以肯定地说,经过大量测试,此问题已解决,该解决方案在我的Web服务器上可以正常工作数月,在任何情况下都不会出现任何问题。答案是有帮助的,但是最终的解决方案如下:
try
{
socket.Shutdown(SocketShutdown.Both);
}
catch (SocketException ex)
{
// handle exception
}
finally
{
socket?.Close();
}
“技巧”是通过SocketShutdown.Both
发起断开连接,并在套接字仍然存在的情况下将其关闭。我注意到有时(我真的不知道为什么)shutdown方法会自动处理套接字,因此socket.Close()
抛出了NullReferenceException异常,但有时没有。我在套接字对象引用之后用?
解决了这个问题,因此,如果它为null,则将不会调用Close()
方法。
它在Windows以及dotnet核心的Linux(debian)中都可以正常工作。
为什么只有这个有效?不知道,但是与此同时,我没有失败的请求,数据包丢失或不正确的关闭连接。可以。