问题描述
我正在用 C# 编写一个客户端/服务器应用程序,它运行良好.目前,一切正常,而且非常强大.我的问题是我在通过连接发送数据包时遇到了一些延迟.
I'm writing a client/server application in C#, and it's going great. For now, everything works and it's all pretty robust. My problem is that I run into some delays when sending packets across the connection.
在客户端我这样做:
NetworkStream ns = tcpClient.GetStream();
// Send packet
byte[] sizePacket = BitConverter.GetBytes(request.Length);
byte[] requestWithHeader = new byte[sizePacket.Length + request.Length];
sizePacket.CopyTo(requestWithHeader, 0);
request.CopyTo(requestWithHeader, sizePacket.Length);
ns.Write(requestWithHeader, 0, requestWithHeader.Length);
// Receive response
ns.Read(sizePacket, 0, sizePacket.Length);
int responseLength = BitConverter.ToInt32(sizePacket, 0);
byte[] response = new byte[responseLength];
int bytesReceived = 0;
while (bytesReceived < responseLength)
{
int bytesRead = ns.Read(response, bytesReceived, responseLength - bytesReceived);
bytesReceived += bytesRead;
}
(省略了一些异常捕获等)服务器做相反的事情,即它阻塞 NetworkStream.Read() 直到它有一个完整的请求,然后处理它并使用 Write() 发送响应.
(Left out some exception catching etc.) The server does the opposite, i.e. it blocks on NetworkStream.Read() until it has a whole request, then processes it and sends a response using Write().
Write()/Read() 的原始速度没有问题(即发送大数据包很快),但是一个接一个地发送几个小数据包,而不关闭连接,可能会非常慢(延迟50-100 毫秒).奇怪的是,这些延迟会出现在典型 ping 时间
The raw speed of Write()/Read() isn't a problem (i.e. sending large packets is fast), but sending several small packets, one after another, without closing the connection, can be terribly slow (delays of 50-100 ms). What's strange is that these delays show up on LAN connections with typical ping times <1 ms, but they do not occur if the server is running on localhost, even though the ping time would effectively be the same (at least the difference should not be on the order of 100 ms). That would make sense to me if I were reopening the connection on every packet, causing lots of handshaking, but I'm not. It's just as if the server going into a wait state throws it out of sync with the client, and then it stumbles a bit as it reestablishes what is essentially a lost connection.
那么,我做错了吗?有没有办法让 TcpServer 和 TcpClient 之间的连接保持同步,以便服务器始终准备好接收数据?(反之亦然:有时处理来自客户端的请求需要几毫秒,然后客户端似乎还没有准备好接收来自服务器的响应,直到它在 Read().)
So, am I doing it wrong? Is there a way to keep the connection between TcpServer and TcpClient synchronised so the server is always ready to receive data? (And vice versa: sometimes processing the request from the client takes a few ms, and then the client doesn't seem to be ready to receive the response from the server until it's had a few moments to wake up after blocking on Read().)
推荐答案
原来我的服务器和客户端毕竟不是完全对称的.我注意到了,但我认为这根本不重要.显然这是一笔大买卖.具体来说,服务器是这样做的:
It turns out my server and client were not completely symmetrical after all. I had noticed, but I didn't think it mattered at all. Apparently it's a huge deal. Specifically, the server did this:
ns.Write(sizePacket, 0, sizePacket.Length);
ns.Write(response, 0, response.Length);
我改成这个了:
// ... concatenate sizePacket and response into one array, same as client code above
ns.Write(responseWithHeader, 0, responseWithHeader.Length);
现在延迟完全消失了,或者至少无法以毫秒为单位进行测量.所以这就像一个 100 倍的加速.o/
And now the delay is completely gone, or at least it's no longer measurable in milliseconds. So that's something like a 100x speedup right there. o/
这仍然很奇怪,因为它正在向套接字写入与以前完全相同的数据,所以我猜套接字在写入操作期间会收到一些秘密元数据,然后以某种方式与远程套接字通信,这可能会将其解释为一个机会小睡一下.要么这样,要么第一次写入将套接字置于接收模式,导致它在收到任何内容之前被要求再次发送时触发.
It's still odd because it's writing exactly the same data to the socket as before, so I guess the socket receives some secret metadata during the write operation, which is then somehow communicated to the remote socket, which may interpret it as an opportunity to take a nap. Either that, or the first write puts the socket into receive mode, causing it to trip up when it's then asked to send again before it has received anything.
我想这意味着你发现所有这些示例代码都在附近,它显示了如何以固定大小的块写入和读取套接字(通常前面有一个描述要跟随的数据包大小的 int,与我的第一个版本相同),没有提到这样做会导致非常严重的性能损失.
I suppose the implication would be that all of this example code you find lying around, which shows how to write to and read from sockets in fixed-size chunks (often preceded by a single int describing the size of the packet to follow, same as my first version), is failing to mention that there's a very heavy performance penalty for doing so.
这篇关于使用 TcpClient 进行通信的更快方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!