套接字发送需要本地主机目标的延迟

Socket send requires delay for localhost target

我目前正在做一个 C# server/client 项目,遇到了一个奇怪的问题,我花了一段时间才解决,但对解决方案并不完全满意,或者更确切地说,我为什么需要它.本质上,我发现将超过 10kbytes 的 tcp 套接字消息发送到本地主机目标需要 1ms 的时间延迟。

作为背景,我在一台机器上有一个服务器,许多客户端连接到该服务器并来回传递信息,这一切似乎都运行良好。我也有连接到服务器本地的客户端。我面临的问题是,当消息大小超过 ~10kbytes 时,消息将无法通过。我只是注意到这一点,因为发送的大多数消息的大小约为 1-2kbytes,但与服务器 (localhost) 连接在同一台机器上的客户端更像是一个管理客户端,因此 sends/receives 更多数据。

真正的问题是服务器(发送方)从 C# 发送命令返回真/成功,表明正在发送数据,但是,接收方指示只有第一个块(如果我缓冲)会通过或什么都没有根本。所以我最终打开了 wireshark 并看到即使发送调用成功完成,实际上也没有数据通过线路发送(记住这是在本地环回接口上)。

我尝试尝试发送数据的方式并尝试所有不同的调用 (NetworkStream.Write / NetworkStream.WriteAsync / Socket.Send / Socket.SendAsync / Socket.BeginSend) 以及缓冲数据或一次性发送所有数据。似乎没有什么不同,直到我在循环中的发送调用之间设置一个时间延迟然后一切正常(我测试了高达 1.5GB 的数据流没有问题)。

我还发现任何低于 1 毫秒/10,000 滴答的延迟都会再次导致问题,我会通过这么多的发送调用来工作,然后它们会再次停止。将 TcpClient.NoDelay 设置为 true 似乎也没有太大影响。

下面是从我的发送代码中截取的示例,其中包含我尝试过的不同发送命令,它们都具有完全相同的行为。

// _client is an abstracted/based off a TcpClient object (the target)
byte[] dataBytes = Serializer.SerializeMessage(message); 
int    bytesSent = 0;

while (bytesSent < dataBytes.Length) {
   int bytesToSend = ((dataBytes.Length - bytesSent) < 8192) ? (dataBytes.Length - bytesSent) : 8192;
   //_client.Socket.Send(dataBytes, bytesSent, bytesToSend, SocketFlags.Partial);
   //_client.NetworkStream.Write(dataBytes, bytesSent, bytesToSend);
   //_client.Socket.BeginSend(dataBytes, bytesSent,bytesToSend,SocketFlags.None, ar => {int bytes = ((Socket)ar.AsyncState).EndSend(ar);}, _client.Socket);
   _ = _client.NetworkStream.WriteAsync(dataBytes, bytesSent, bytesToSend);

   bytesSent += bytesToSend;
   Thread.Sleep(TimeSpan.FromTicks(10000));
}

因此,虽然这现在工作正常,但我的问题是为什么甚至需要它,根据以前的经验,我总是发现如果您需要 "delay",则还有其他错误。此外,没有延迟,没有任何错误,所有调用都报告他们成功发送了字节,但 wireshark 显示没有数据正在传输。我唯一的想法是库中的某些东西检测到它是本地目标并使用从我那里抽象出来的一些内部命名管道并且缓冲区在某处填满?

我已经尝试搜索任何类似的问题一个多星期了,但找不到任何明显的问题,因此非常感谢任何见解。

from previous experience I always find if you need to "delay" there is something else that is wrong.

没错。在这里,我怀疑您假设套接字上的 Read 将 return 一条完整的消息。

但是TCP/IP是一个流协议,不包含任何消息的概念。要成功地直接使用 TCP/IP,您必须定义一个 "message framing protocol",以向远程主机传达期望的字节数。我没有看到你在消息之前写任何东西,所以我怀疑那是缺少的内容。

许多人使用的简单解决方案是始终在消息开头发送一个 4 字节整数,指示将发送的字节数。然后 reader 可以执行 4 字节读取,然后是读取循环,直到读取预期的字节数。

所以这个问题原来是计算机本身(或者更确切地说是我们的许多开发箱)上的某个问题影响了本地主机绑定上 hosted/connected 的任何服务。这包括许多网络服务,如果托管在本地,甚至包括 IISExpress 和 IIS。这导致任何超过 ~10kbytes 大小的数据永远不会 sent/received.

经过几天的努力找出影响本地主机连接的原因后,我们最终重建了开发箱,现在问题已经解决(尽管我们仍然不知道 why/what 导致了这个问题)。