假设我有两个互相连接的插座(Socket A
和Socket B
)。
如果计算机已Socket B
拔掉电源,则Socket A
尝试向发送某些数据Socket B
,数据将得不到确认,因此 TCP 会一次又一次地重新传输数据以期获得确认,直到 TCP 放弃并决定不再重新传输数据并告知发生了Socket A
套接字错误。WSAECONNABORTED (10053)
我的问题是:
- 是否可以保证
WSAECONNABORTED (10053)
在多次重传重试之后始终会收到套接字错误(我相信是的,因为否则 TCP 将永远继续重传!)? - TCP 需要重传多少次才会决定放弃并导致
WSAECONNABORTED (10053)
套接字错误? - 这个重传重试次数可以配置吗?
答案1
TCP 的 Windows 计时器使用一个称为 重传超时(RTO)基于估计往返时间发送方和接收方之间的往返时间(或 RTT),以及往返时间的变化。此计时器的行为在RFC 6298. 更多信息请参阅维基百科文章传输控制协议。
其在Windows中的工作方式如下:
- 首先建立估计的 RTO
- TCP 消息已发送,我们等待 ACK(确认)数据包
- 如果 ACK 尚未到达,我们将等待时间加倍并返回步骤 2
- 如果收到 ACK,则计算新的 RTO
- 如果从未收到 ACK,则连接将中止,并出现错误 WSAECONNABORTED。
Windows 针对此协议使用了两个注册表参数,详见 Microsoft 文章
如何修改 TCP/IP 最大重新传输超时时间。
TcpMaxDataRetransmissions
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
Value Name: TcpMaxDataRetransmissions
Data Type: REG_DWORD - Number
Valid Range: 0 - 0xFFFFFFFF
Default: 5
描述 :
此参数控制 TCP 在终止连接之前重新传输单个数据段(非连接段)的次数。连接上每次连续重新传输时,重新传输超时时间都会加倍。响应恢复时会重置该超时时间。基本超时值由连接上测量的往返时间动态确定。
TCPInitialRtt
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\ID for Adapter
Value Name: TCPInitialRtt
Data Type: REG_DWORD
Valid Range: 300-65535 (milliseconds in decimal)
Default: 0xBB8 (3000 milliseconds expressed in hexadecimal)
描述:
此参数控制 TCP 在每个新连接上使用的初始重传超时。它适用于连接请求 (SYN) 以及在每个连接上发送的第一个数据段。例如,数值数据“5000 十进制”将初始重传时间设置为五秒。
注意:您只能在初始超时时增加该值。不支持减少该值。
尽管 TCPInitialRtt 的初始超时时间为 3 秒,但当数据包正确传输时,它将平滑为更合理的值。
例如,如果我们采用默认值 3 秒 RTO 和 5 次重试,则总等待时间将为:
- 第一次超时时间:3秒
- 第二次超时:6秒
- 第三次暂停:12秒
- 第四次暂停:24秒
- 第五次也是最后一次暂停:48秒
这使得在连接中止之前总共需要等待 93 秒。在大多数情况下,如果连接曾经正常工作,则超时时间会短得多。
答案2
通常情况下时间用来 (继续重试,直到达到超时值),但这取决于客户端/服务器软件的编写方式。
如果使用时间,那么为了知道重试次数,你必须知道多常重试发生。
如果程序使用 Winsock,请参见下文了解设置超时的可能方法。
该软件还可以编写为使用重试次数在失败之前或时间失败之前或两者兼而有之。
2.15 - 如何更改 Winsock 函数的超时时间?
一些阻塞 Winsock 函数(例如 connect())嵌入了超时。其背后的理论是,只有堆栈才具有设置适当超时所需的所有信息。然而,有些人发现堆栈使用的值对于他们的应用程序来说太长了;它可能长达一分钟或更长时间。
您可以使用 SO_SNDTIMEO 和 SO_RCVTIMEO setsockopt() 选项调整 send() 和 recv() 超时。
对于其他 Winsock 函数,最好的解决方案是完全避免阻塞套接字。所有非阻塞套接字方法都为您提供了构建自定义超时的方法:
带有 select() 的非阻塞套接字 – select() 函数的第五个参数是超时值。
异步套接字 - 使用 Windows API SetTimer()。
事件对象 – WSAWaitForMultipleEvents() 有一个超时参数。
可等待计时器——调用 CreateWaitableTimers() 来创建一个可等待计时器,然后您可以将其与套接字一起传递给 WSAEventSelect() 之类的函数:如果在计时器关闭之前没有任何套接字发出信号,则阻塞函数无论如何都会返回。
请注意,使用异步和非阻塞套接字,您可能能够完全避免处理超时。即使 Winsock 很忙,您的程序仍会继续工作。因此,您可以让用户自行取消耗时过长的操作,或者让 Winsock 的自然超时到期,而不是在您的代码中接管此功能。