有没有办法配置套接字期望在发送数据确定连接失败之前收到ACK的超时?
我知道这也可以在应用程序级别完成,但是因为我发送的每个数据包都是ACK'd,我只是想知道我的数据是否被接收,在应用程序级别使用其他数据完成同样的事情似乎很浪费。 (更不用说,我的特定应用程序使用每字节带电的蜂窝链接。)
注意:根据我之前的问题 -
What conditions cause NetworkStream.Write to block? - 您无法依赖.Write
抛出异常以确定数据未正确发送。
答案 0 :(得分:6)
在一些IETF RFC(5482 793)中提到“用户超时”,它会执行所要求的操作。
其他一些操作系统支持这个作为套接字选项,但不幸的是不支持Windows。
如果没有此选项,在此方案中减少中止之前的时间的唯一方法是减少重新传输尝试次数,或减少初始RTT。
在Windows上,前者可以通过netsh / registry进行控制(机器范围..):Tcp Max Data Retransmissions。
通过你自己的超时放弃当前连接是否可行,如果需要可以再做一次?
答案 1 :(得分:3)
我不是C#专家,但我想我可以帮忙回应。 您正尝试从应用程序获取TCP层控制数据。这并不容易,与任何应用层协议一样,您需要某种应用层响应,如HTTP中的Request-Response。
知道所有写入的数据实际上是由另一端接收的问题是TCP是面向流的。这意味着您可以通过套接字发送1KB数据,该KB存储在TCP snd缓冲区中,并且该KB可能与3个TCP段一起发送,这些TCP段可以完全或单独确认(TCP ACK)。这是异步的。因此,在某些时候,TCP可能只发送了1000 KB数据的300字节,这只是一个例子。
现在另一个问题是,每次发送大量数据(A)时是打开连接还是关闭连接,或者连接始终打开(B)。
在(A)中它更简单,因为如果连接失败打开,那就是它。超时可能需要一分多钟,但您不会发送超过20个字节的IP和(20个字节)TCP标头(有时IP和TCP选项的字节数超过20个字节)。
在(B)中,当您想要发送数据时,您将意识到成功或失败。我会考虑3个案例:
1 - 套接字的另一端关闭或重置TCP连接。在这种情况下,你应该立即收到和错误响应,或者,在C中,一个指示管道损坏的信号,我想它将成为C#中的一个例外。
2 - 另一端无法访问,并且未关闭/重置套接字。这很难检测,因为TCP会发送超时的消息,经过几次重试/超时后,它会决定连接是否中断。超时和重试次数可以是可配置的,但是在OS级别(对于所有应用程序)。我不认为您可以通过套接字配置。 在这种情况下,您的应用程序在发送数据时将无法实现。
3-Data被另一端成功接收并在TCP层确认。
复杂的部分是尽可能快地区分(2)和(3)。我会假设你在问这个问题。 除非你破解内核,否则我认为没有任何可能做到这一点。
无论如何,在应用层从服务器获取ACK可能意味着只有1或2个字节表示接收到的数据量。除了IP和TCP基本标头的20 + 20字节外。
如果有可能做你说的话,我会尝试这个,但我从未测试过:
您可以使用发送缓冲区大小和选择功能。您可以使用setsockopt和OS_SNDBUF套接字选项设置套接字的发送缓冲区大小。 http://msdn.microsoft.com/en-us/library/system.net.sockets.socket_methods(v=vs.110).aspx
如果您知道总是要发送2 KB,则将发送缓冲区大小设置为2 KB。通常您只能在连接后进行更改。 http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.sendbuffersize(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
然后在Socket上调用Select或Poll方法来检查它是否可写。
只要有一条TCP消息被激活,Select或Poll就会指示套接字是可写的,因为发送的数据已从发送缓冲区中删除。
请注意,此算法有局限性:
如果您不能应用上述算法,则可能需要使用应用层简单ACK来支付额外的带有~42字节的TCP消息的额外费用。
很抱歉无法提供明确的解决方案。也许操作系统应该实现告诉您可用缓冲区字节的功能,这将解决您的问题。
编辑:我在评论中添加了另一条建议。如果你有可能使用Winpcap进行其他进程,你可以从另一端捕获TCP响应!例如,使用本地IPC如共享内存或仅使用套接字,一个应用程序可以告诉另一个关于socekt(src IP,src端口,dst IP,dst端口)的数据。另一个称为监控过程的第二个过程可以通过嗅探连接来检测从另一个端点接收的ACK。也可以使用winpcap链接到本机代码......
答案 2 :(得分:3)
这是一个古老的问题,但它与我一起回家......正如您在原始问题中所提到的,这应该在应用层完成。
我希望我的经验可能会有所帮助,因为我和你有着完全相同的想法(甚至与我的团队中的其他开发人员争夺这个坚持的TCP应该完成工作)。实际上,很容易弄乱TCP与无线连接,冲突的网络MTU以及有时执行不良的路由器/接入点,这些路由器/接入点会过早地或在故障情况下发出错误信号。但也因为TCP旨在从一个源流到一个目的地,而不是真正确保全双工交易通信。
我花了很多年时间为一家嵌入式设备制造商工作,并为仓库中的无线条码终端编写了一个完整的客户端 - 服务器系统。在这种情况下不是蜂窝电话,但是wifi可能同样糟糕(但即使是WiFi也会证明所需的任务无用)。仅供参考,我的系统在将近7年后仍然可靠地投入生产,因此我认为我的实施相当稳健(它经常受到工业制造机器/焊工/空气压缩机/鼠标咀嚼网线等的干扰)。
了解问题
@rodolk发布了一些很好的信息。 TCP级别的ACK不一定与您的每个应用程序网络传输相对应(如果您发送的信息超过网络的MTU或最大数据包大小,即使Nagle被禁用,也不会是1-1)。 / p>
最终是TCP& amp; amp; amp; IP(Transport and Network layers)用于确保在一个方向(从源到目的地)传输您的流量,并对最大重试次数等有一些限制。应用程序通信最终涉及位于TCP / IP之上的全双工(双向)Application layer通信。混合这些层不是一个好策略。考虑TCP / IP之上的HTTP请求 - 响应。 HTTP不依赖TCP ACKS来实现自己的超时等。如果你感兴趣,HTTP将是一个很好的研究规范。
但是,让我们甚至假装它正在做你想要的。您总是在1次传输中发送少于1 MTU(或最大数据包大小)并正好接收1个ACK。介绍您的无线环境,一切都变得更加复杂。您可以在成功传输和相应的ACK之间出现故障!
问题在于无线通信流的每个方向不一定具有相同的质量或可靠性,并且可以基于本地环境因素和无线设备的移动而随时间改变。
设备通常比他们传输的更好。设备通常可以完美地接收您的传输,并回复某种类型的" ACK"由于信号质量,传输距离,射频干扰,信号衰减,信号反射等原因,无线ACK从未到达目的地。在工业应用中,这可能是重型机械开启,焊接机,冰箱/冰柜,荧光灯在城市环境中,它可以是结构,停车场,钢结构建筑等的移动性。
在这种情况下,客户端在什么时候采取行动(保存/提交数据或更改状态)以及服务器在什么时候认为操作成功(保存/提交数据或更改状态)?如果没有在应用层进行额外的通信检查,这很难可靠地解决(有时包括事务的双向ACK,即:客户端发送,服务器ACKS,客户端确认ACK :-)您不应该依赖TCP级别的ACK,因为它们不能可靠地等同于成功的全双工通信,也不会为您的应用提供可靠的重试机制。
嵌入式设备上不可靠无线通信的应用层技术
我们的技术是每个应用程序级别的消息都发送一个字节的应用程序级别标头,其中包括一个数据包ID#(只是一个递增的整数),整个消息的长度(以字节为单位)和整个消息的CRC32校验和。我无法确定,但我相信我们在8字节,2 |中完成了这项工作2 | 4.(取决于您要支持的最大消息长度)。
所以,让我们假设您在计算仓库中的库存,计算一个项目并计算5个单位,条形码终端向服务器发送一条消息,说明" Ben计算5个单位的项目1234&#34 ;。当服务器收到消息时,它将等待直到收到完整消息,首先验证消息长度,然后验证CRC32校验和(如果长度匹配)。如果全部通过,我们会向此消息发回应用程序响应(类似于应用程序的ACK)。在此期间,条形码终端正在等待来自服务器的ACK,并且如果它没有从服务器收到回复则将重新发送。如果服务器收到相同数据包ID的多个副本,则可以通过放弃未提交的事务来进行重复数据删除。但是,如果条形码扫描器确实从服务器接收到它的ACK,那么它将再回复一个最终的" COMMIT"命令到服务器。因为前两条消息只是验证了一个有效的全双工连接,所以在这几毫秒的时间内,提交几乎不可能失败。仅供参考,这种失败情况在您的WiFi覆盖范围边很容易复制,所以请带上您的笔记本电脑/设备,然后去散步,直到无线网络只是" 1 bar"或最低连接速度通常为1 mbps。
因此,如果只需要无线通信的一端可能失败时需要事务请求/响应,则在消息开头添加8个字节的标头,并可选择添加一个额外的最终COMMIT消息传输。
将传输层挂钩系统(例如挂钩到winpcap
)的复杂应用层保存为每个消息8个字节是非常困难的。此外,您可能会或可能无法复制此传输层挂钩在其他设备上(可能您的系统将来会在其他设备上运行?Android,iOS,Windows Phone,Linux,您是否可以为所有这些设备实现相同的应用层通信平台?我认为你应该能够在每个设备上实现你的应用程序,无论TCP堆栈是如何实现的。)
我建议您将应用程序层与传输层和网络层分开,以便更好地分离关注点,严格控制重试条件,超时和可能交易的应用程序状态更改。
答案 3 :(得分:-3)
您可以使用TcpClient.SendTimeout
执行此操作。如果指定的超时在操作成功完成之前到期,则会导致写操作抛出SocketException
。
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.sendtimeout.aspx
另外,请参阅此页面以获取有关如何设置具有更多可自定义和可靠超时的套接字的更多信息: