配置套接字ACK超时?

Dav*_*fer 15 c# sockets network-programming tcpclient

有没有办法配置套接字期望在发送数据确定连接失败之前收到ACK的超时?

我知道这也可以在应用程序级别完成,但是因为我发送的每个数据包都是ACK'd,我只是想知道我的数据是否被接收,使用应用程序级别的其他数据来完成相同的操作事情似乎很浪费.(更不用说,我的特定应用程序使用每字节带电的蜂窝链接.)

注意:根据我之前的问题 - 什么条件导致NetworkStream.Write被阻止?- 您不能依赖.Write抛出异常来确定数据未正确发送.

Pet*_*art 6

在一些IETF RFC(5482 793)中提到了"用户超时",它可以满足要求.

其他一些操作系统支持这个作为套接字选项但不幸的是不支持Windows.

如果没有此选项,在此方案中减少中止之前的时间的唯一方法是减少重新传输尝试次数或减少初始RTT.

在Windows上,前者可以通过netsh/registry控制(机器范围..):Tcp Max Data Retransmissions.

通过自己的超时放弃当前连接是否可行,如果需要则再制作另一个连接?

  • 应用程序必须确定何时放弃连接 - 可能是在TCP会话开始时建立的"生存时间",基于不活动时间或有效数据速率
  • 由于旧连接的重传,会有一些数据开销
  • 服务器应用程序可能需要更改为接受多个并发连接
  • 如果网络从未达到足够的超时速度,则客户端不应该无限期地重复此过程


Ben*_*yne 4

这是一个老问题,但它对我来说很重要......正如您在最初的问题中提到的,这应该在应用程序层完成。

我希望我的经验可能对您有所帮助,因为我的想法与您完全相同(甚至与我团队中的其他开发人员争论过坚持 TCP 应该完成工作)。实际上,很容易将 TCP 与无线连接、网络 MTU 冲突以及有时实施不当的路由器/接入点(过早或在故障情况下确认)搞乱。但也因为 TCP 旨在从一个源流向一个目的地,而不是真正确保全双工事务通信。

我在一家嵌入式设备制造商工作了多年,为仓库中的无线条形码终端编写了完整的客户端-服务器系统。在这种情况下不是蜂窝网络,但 WiFi 也可能同样糟糕(但即使 WiFi 也会证明所需的任务毫无用处)。仅供参考,我的系统在近 7 年后仍然在生产中可靠运行,所以我认为我的实现相当稳健(它经常受到工业制造机器/焊机/空气压缩机/老鼠咀嚼网络电线等的干扰)。

了解问题

@rodolk 发布了一些很好的信息。TCP 级别的 ACK 不一定与每个应用程序网络传输 1-1 对应(如果您发送的数据超过网络的 MTU 或最大数据包大小,即使禁用 Nagle,也始终不会是 1-1)。

最终,TCP 和 IP(传输层和网络层)的机制是确保在一个方向(从源到目的地)传输流量,并对最大重试次数等进行一些限制。应用程序通信最终是关于位于 TCP/IP 之上的全双工(双向)应用程序层通信。混合这些层并不是一个好的策略。考虑 TCP/IP 之上的 HTTP 请求-响应。HTTP 不依赖 TCP ACKS 来实现自己的超时等。如果您有兴趣,HTTP 将是一个很好的研究规范。

但我们甚至可以假装它正在做您想要的事情。您在 1 次传输中始终发送小于 1 MTU(或最大数据包大小)的数据,并且恰好收到 1 个 ACK​​。引入您的无线环境,一切都会变得更加复杂。在成功传输和相应的ACK之间可能会出现失败!

问题在于,无线通信流的每个方向不一定具有相同的质量或可靠性,并且可能会根据本地环境因素和无线设备的移动而随时间而变化。

设备的接收能力通常比其传输能力要好。设备完美接收您的传输,回复某种传输的“ACK”是很常见的,但由于信号质量、传输距离、射频干扰、信号衰减、信号反射等原因,无线 ACK 永远不会到达目的地在工业应用中,这可能是重型机械的开启、焊接机、冰箱/冰柜、荧光灯等。在城市环境中,这可能是建筑物、停车场、钢建筑结构等内的移动性。

在这种情况下,客户端在什么时候采取操作(保存/提交数据或更改状态)以及服务器在什么时候认为操作成功(保存/提交数据或更改状态)?如果没有在应用程序层中进行额外的通信检查(有时包括事务的 2 路 ACK,即:客户端传输、服务器 ACKS、客户端 ACKS ACK :-),则很难可靠地解决此问题。您不应该在这里依赖 TCP 级 ACK,因为它们不会可靠地等同于成功的全双工通信,并且不会为您的应用程序提供可靠的重试机制。

嵌入式设备上不可靠无线通信的应用层技术

我们的技术是,每个应用程序级消息都使用几个字节的应用程序级标头发送,其中包括数据包 ID #(只是一个递增整数)、整个消息的长度(以字节为单位)以及整个消息的 CRC32 校验和。我不太记得了,但我相信我们用 8 个字节完成了这个,2 | 2 | 4.(取决于您要支持的最大消息长度)。

假设您正在清点仓库中的库存,您清点了一件商品并清点了 5 件,条码终端向服务器发送一条消息“Ben 清点了 5 件商品 1234”。当服务器接收到消息时,它会等待,直到收到完整的消息,首先验证消息长度,然后验证CRC32校验和(如果长度匹配)。如果这一切都通过了,我们会发回对此消息的应用程序响应(类似于应用程序的 ACK)。在此期间,条形码终端正在等待服务器的 ACK,如果没有收到服务器的回复,则会重新传输。如果服务器收到同一数据包 ID 的多个副本,它可以通过放弃未提交的事务来消除重复。然而,如果条形码扫描仪确实从服务器收到了 ACK,那么它就会向服务器回复一个最终的“COMMIT”命令。由于前 2 条消息刚刚验证了工作的全双工连接,因此提交在这几毫秒的时间范围内不太可能失败。仅供参考,这种故障情况很容易在 WiFi 覆盖范围的边缘复制,因此请带上您的笔记本电脑/设备去散步,直到 WiFi 仅为“1 bar”或最低连接速度通常为 1 mbps。

因此,您需要在消息的开头添加 8 字节标头,并且如果您需要事务处理请求/响应(当无线通信的一侧可能失败时),则可以选择添加一个额外的最终 COMMIT 消息传输。

很难证明使用复杂的应用程序层到传输层挂钩系统(例如挂钩到winpcap)为每条消息节省 8 个字节是合理的。另外,您可能能够也可能无法在其他设备上复制此传输层挂钩(也许您的系统将来会在其他设备上运行?Android、iOS、Windows Phone、Linux,您能否为所有这些设备实现相同的应用程序层通信平台?我认为无论 TCP 堆栈如何实现,您都应该能够在每个设备上实现您的应用程序。)

我建议您将应用程序层与传输层和网络层分开,以便更好地分离关注点,并严格控制重试条件、超时和潜在的事务应用程序状态更改。