异步TCP System.Net.Socket发送(Begin/End Send)实际上没有发送任何内容。

3
背景: 我在一台机器上运行TCP服务器,多个客户端连接到不同的机器上进行TCP通信。我使用Wireshark监视网络流量,同时也通过我的服务器应用程序内部的日志记录和System.Net.Sockets中的System.Diagnostics跟踪侦听器以详细模式来监视网络流量。
问题: 由于一些意外的断开连接,我被提示检查我的日志,发现了一些非常奇怪的行为。根据服务器应用程序日志和System.Diagnostic输出日志,我的服务器正在使用begin/end send向客户端发送4字节数据包。BeginSend完成后,EndSend也完成,表示它成功地发送了4字节数据包。
然而,当我查看Wireshark日志时,该数据包从未出现过。我在服务器机器上运行Wireshark,因此没有理由让数据包在我的服务器和跟踪日志中出现,但是在同一台机器上的Wireshark日志中却没有出现该数据包。
此外,很快就会发生意外的断开连接(约30秒后),这是由于我的服务器的EndReceive方法上发生了SocketException。但是,在服务器尝试发送数据之间的时间内,服务器正在确认从客户端接收到的数据包,因此我知道连接仍然处于活动状态。
有没有人有类似的经历,或者知道可能导致这种情况的错误或其他问题?
我不想认为这是在套接字级别发生的,因为TCP说我的数据包已经发送成功,但它甚至没有到达网络,这意味着我不能依赖TCP作为可靠的传输方式(当然,这就是TCP的全部意义)。
日志样本: 来自我的服务器应用程序:
    2011-09-07 10:41:38,812 Attempting to send Packet (BeginSend - 4 bytes)
    2011-09-07 10:41:38,812 Sent Packet (EndSend - 4 bytes)

从System.Diagnostics跟踪日志中:
System.Net.Sockets Verbose: 0 : [4376] Socket#19699911::BeginSend()
    DateTime=2011-09-07T17:41:38.8125000Z
System.Net.Sockets Verbose: 0 : [0980] Data from Socket#19699911::PostCompletion
    DateTime=2011-09-07T17:41:38.8125000Z
System.Net.Sockets Verbose: 0 : [0980] 00000000 : 02 04 00 00                                     : ....
    DateTime=2011-09-07T17:41:38.8125000Z
System.Net.Sockets Verbose: 0 : [4376] Socket#19699911::EndSend(OverlappedAsyncResult#44209720)
    DateTime=2011-09-07T17:41:38.8125000Z
System.Net.Sockets Verbose: 0 : [4376] Exiting Socket#19699911::EndSend()   -> 4#4
    DateTime=2011-09-07T17:41:38.8125000Z
System.Net.Sockets Verbose: 0 : [4376] Exiting Socket#19699911::BeginSend()     -> OverlappedAsyncResult#44209720
    DateTime=2011-09-07T17:41:38.8125000Z

我也会贴上Wireshark日志,但本质上,在那个时间段内,除了来自客户端的数据包和服务器的相应确认之外,该设备接口上没有任何注册信息。

编辑: 根据要求,这是发送代码(由于空间和其他原因而缩短)。非常简单,没有什么可能出错的东西。

在我的BeginSend方法中:

socket.BeginSend(data, 0, data.Length, SocketFlags.None, EndSend, state);

在我的 EndSend 方法中:
bytesSent = socket.EndSend(ar);

注意:这不是我的第一次尝试,就像他们所说的...我过去15年一直在使用TCP套接字编写服务器和客户端,以前从未遇到过这种情况。
此外,我使用的.NET版本是4.0...如果有任何相关性,请告诉我。
救命啊!

添加了一些代码...但实际上,没有什么特别的事情发生。这是一个简单的BeginSend和EndSend...你可以从MSDN或其他地方获取任何示例代码并重现我所看到的。 - Aron
也许我对于.NET平台下TCP工作原理的理解是错误的...当消息被传输并被接收方确认后,Socket.EndSend调用是否会成功返回?还是说只要.NET成功将消息发送到网络接口,EndSend就算成功?如果是后者,那么一个有故障的网络适配器可能可以解释我所看到的情况。 - Aron
客户端是否看到了这个4字节的传输?你的帖子暗示着客户端和服务器之间的对话仍在继续。 - anton.burger
客户端看不到我发送的4字节数据包。数据包甚至没有到达适配器层,否则在Wireshark中可能会记录一些信息。SocketException如下:System.Net.Sockets.SocketException (0x80004005):连接尝试失败,因为连接方在一段时间后未正确响应,或者已建立的连接因连接的主机未能响应而失败。 - Aron
我在4.5.x和4.6.x框架下看到了相同的行为,小数据包似乎永远无法通过网络传输,而所有与通信相关的代码都报告成功发送。我尝试修改了许多套接字选项。当使用一个未经管理的(C++)测试工具执行相同的代码(从功能上讲)时,所有数据都以迅速的方式传输。如果我用许多小写入“轰炸”套接字(这会破坏我们的协议),每个发送都会及时完成。荒谬。 - Shaun Wilson
显示剩余2条评论
4个回答

0

这个问题似乎很久以前就提出了,所以你可能已经解决了...但如果其他人也遇到了同样的问题,你可以检查一下Windows的内存压力保护是否在起作用。当有多个同时连接时,这会导致数据包被丢弃,这就是你看到的情况。

另一个实验是暂时关闭服务器机器上的防火墙。这也会关闭所有这些保护机制(如果我没记错的话),如果事情能够正常运行,那么你就知道其中一个是罪魁祸首了。


0

我在使用.NET进行套接字编程时遇到的一个问题与垃圾回收有关。 我的代码在方法中创建了一个套接字并分配给一个局部变量,而不是分配给成员变量。 然后垃圾回收器回收了套接字,导致SocketException异常。 显然,只执行BeginSend不能保持对该套接字的引用。

将套接字移动到成员变量中解决了我的问题。 希望这对您遇到的问题有所帮助。


不幸的是这不是问题...但是评论仍然适用于套接字编程。任何跨多个线程或方法进行的套接字交互都应该参考一个不会被这样回收的变量。 - Aron

0
我不愿意认为这是在套接字层发生的,TCP 说我的数据包已经发送了,但实际上它甚至没有传输到网络上,这意味着我不能依赖 TCP 作为可靠的传输协议(当然,这正是 TCP 的全部意义所在)。
这种说法是不正确的。仅仅因为你成功地写入了套接字并不意味着数据已被客户端接收或离开了你的机器。它只是成功地写入了内部 TCP/IP 缓冲区。TCP/IP 将根据需要将该数据发送为多个块/帧或少量块/帧。如果你发送的数据很小,TCP/IP 将延迟尝试将其他数据聚合到帧中。
如果你在 Wireshark 中看不到它,那么你可能没有监听正确的接口,设置了过滤器,或者某种防火墙阻止了你发送数据。查看一下是否有任何杀毒软件或其他层服务提供商来确定谁可能会干扰你。

我在界面上能看到我的其他数据,而且实际上只有一个可用的接口。我认为我看到的问题必须存在于我的应用程序下面某个地方,在Windows的套接字层中。作为后续,当你说从.NET发送的内容只到达了内部TCP/IP缓冲区时,我想知道Wireshark进行捕获的位置。可能不是在那个层次,而是在网络接口实际发送数据时?如果是这种情况,那么我真的不知道该怎么排除故障。 - Aron
Wireshark的速度会比tcp/ip缓冲区低...例如,它会在数据聚合之后才看到数据。您确定没有任何防病毒软件或过滤器在您的Wireshark捕获中生效吗? - tcarvin
1
当你说你看到来自客户端的数据包并且它们得到了ACK确认,这些数据包是否在你无法发送的同一套接字连接上? - tcarvin
我对VM混合使用引起的其他问题并不是特别熟悉。但我知道你可以配置VM实例的带宽限制。关于在这个问题发生时正在发生什么,你是否只看到了你发送的数据的黑屏,或者如果你仔细查看Wireshark日志,你是否看到任何其他应该已经发送但现在缺失的传出ACK?我很好奇这个时间段是否会导致你的连接完全中断或只是你的数据帧。 - tcarvin
我看到其他连接正常运行。在这个失败的连接同时,还有其他确认和数据进出。 - Aron
显示剩余2条评论

0

如果可以忽略其限制,您是否可以重写应用程序以使用TcpClient而不是Socket?如果您的应用程序无论如何都绑定到TCP/IP,至少对于这个特定的套接字,您可以使用抽象层--这可能会有所帮助。此外,您可以尝试使用EndSend(IAsyncResult, SocketError)重载,看看SocketError对象是否包含任何有用信息。

如果这些软件调整都没有帮助,我必须同意问题必须在.NET软件层以下,即防火墙软件(或硬件),杀毒软件等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接