C#套接字异常延迟

发布于 2024-07-10 23:56:30 字数 1099 浏览 6 评论 0原文

我正在使用 C# 和 XNA 开发一个小型在线多人乒乓球游戏。

我使用套接字在个人 LAN 上的两台计算机之间传输数据。 效果很好。

问题在于速度:传输速度很慢。

当我 ping 第二台计算机时,它显示延迟为 2 毫秒。 我在代码中设置了一个小计时器,它显示大约 200 毫秒的延迟。 即使服务器和客户端在同一台计算机上(使用127.0.0.1),延迟仍然在15毫秒左右。 我认为这很慢。

以下是我的代码的一些片段:

server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, port));
server.Listen(1);
// Begin Accept
server.BeginAccept(new AsyncCallback(ClientAccepted), null);

在 ClientAccepted 中,我设置了一个 NetworkStream、一个 StreamReader 和一个 StreamWriter。 这就是当我想要更新玩家位置时发送消息的方式:

string message = "P" + "\n" + player.Position + "\n";
byte[] data = Encoding.ASCII.GetBytes(message);
ns.BeginWrite(data, 0, data.Length, new AsyncCallback(EndUpdate), null);

EndUpdate 所做的唯一事情就是调用 EndWrite。 这就是我接收数据的方式:

message = sr.ReadLine();

它不会阻止我的游戏,因为它位于第二个线程上。

这些是我尝试过的东西: - 使用IP代替TCP - 使用二进制消息而不是文本 - 使用 IPv6 代替 IPv4 没有什么真正有帮助。

关于如何改善延迟有什么想法吗?

谢谢

I am working on a little online multiplayer pong game with C# and XNA.

I use sockets to transfer data between two computers on my personnal LAN. It works fine.

The issue is with speed : the transfer is slow.

When I ping the second computer, it shows a latency of 2 ms.
I set up a little timer inside my code, and it shows a latency of about 200 ms.
Even when the server and the client are on the same computer (using 127.0.0.1), the latency still about 15 ms. I consider this as slow.

Here are some fragment of my code :

server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, port));
server.Listen(1);
// Begin Accept
server.BeginAccept(new AsyncCallback(ClientAccepted), null);

in ClientAccepted, I set up a NetworkStream, a StreamReader and a StreamWriter.
This is how I send a message when I want to update the player's location :

string message = "P" + "\n" + player.Position + "\n";
byte[] data = Encoding.ASCII.GetBytes(message);
ns.BeginWrite(data, 0, data.Length, new AsyncCallback(EndUpdate), null);

the only thing EndUpdate do is to call EndWrite.
This is how I receive data :

message = sr.ReadLine();

It dosen't block my game since it's on a second thread.

These are the stuff I tried :
- Use IP instead of TCP
- Use binary message instead of text
- Use IPv6 instead of IPv4
Nothing really did help.

Any thoughts about how I can improve the latency?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

猛虎独行 2024-07-17 23:56:30

您看到的延迟很可能是由于Nagle 算法造成的,该算法用于提高发送时的效率使用 TCP 传输大量小数据包。 本质上,TCP 堆栈将小数据包收集在一起,并在传输之前将它们合并成一个更大的数据包。 这显然意味着在发送输出缓冲区中等待的数据之前,延迟一小段时间(最多 200 毫秒)以查看是否发送了更多数据。

因此,请尝试通过设置 NoDelay 属性为 true。

但是,请注意,如果对套接字进行大量小型写入,则可能会因为 TCP 标头开销和每个数据包的处理而导致性能下降。 在这种情况下,如果可能的话,您需要尝试将写入数据批量收集。

The latency you're seeing is most likely due to Nagle's algorithm which is used to improve efficiency when sending lots of small packets using TCP. Essentially, the TCP stack collects small packets together and coalesces them into a larger packet before transmitting. This obviously means delaying for a small interval - up to 200ms - to see if any more data is sent, before sending what's waiting in the output buffers.

So try switching off Nagle by setting the NoDelay property to true on the socket.

However, be aware that if you do a LOT of small writes to the socket, that you may lose performance because of the TCP header overhead and processing of each packet. In that case, you'd want to try to collect your writes together into batches if possible.

淡看悲欢离合 2024-07-17 23:56:30

还有其他几个元问题需要考虑:

  1. 计时器精度:许多系统计时器仅每 15 毫秒左右更新一次。 我不记得精确的值,但如果你的计时总是大约 15 毫秒的倍数,请怀疑你的计时器精度不高。 这“可能”不是您的问题,但请记住这一点。

  2. 如果您在同一台计算机上进行测试,并且仅运行单核计算机,则线程/进程切换频率将主导您的 ping 时间。 没什么可做的,但尝试添加 Sleep(0) 调用以允许发送数据后线程/进程交换。

  3. TCP/IP 传输设置。 正如前面在 SocketOptionName.NoDelay 中提到的。 另外,如前所述,如果您要不断更新状态,请考虑 UDP。 顺序无法保证,但对于易失性数据,丢失数据包是可以接受的,因为无论如何状态很快就会被覆盖。 为了避免使用过时的数据包,请向数据包添加递增值,并且切勿使用标记早于当前状态的数据包。
    tcp 和 udp 之间的差异不应占 200 毫秒,但其他因素的组合可以。

  4. 轮询与选择。 如果您轮询接收的数据,轮询的频率自然会干扰接收速率。 我在专用网络线程中使用 Socket.Select 来等待传入数据并尽快处理它。

There are several other meta-issues to consider:

  1. Timer accuracy: Many system timers only update every 15 ms or so. I can't remember the precise value, but if your timings are always multiples of about 15 ms, be suspicious that your timer is not high precision. This is 'probably' not your issue, but keep it in mind.

  2. If you're testing on the same computer, and only running a single core machine, the thread/process switching frequency will dominate your ping times. Not much to do but try to add Sleep(0) calls to allow thread/process swaps after sending data.

  3. TCP/IP transmission settings. As alluded earlier in SocketOptionName.NoDelay. Also as mentioned earlier, consider UDP if you're continuously updating state. Order isn't guaranteed, but for volatile data missing packets are acceptible, as the state will be overridden soon anyway. To avoid using stale packets, add an incrementing value to the packet and never use a packet labelled earlier than the current state.
    The difference between tcp and udp should not account for 200 ms, but a combination of other factors can.

  4. Polling vs. Select. If you're polling for received data, the frequency of polling will interfere with receive rate, naturally. I use Socket.Select in a dedicated network thread to wait for incoming data and handle it as soon as possible.

稚然 2024-07-17 23:56:30

大多数网络游戏不使用 TCP 而是使用 UDP 进行通信。 这样做的问题是数据很容易被删除,这是你必须考虑的问题。

使用 TCP,主机/服务器之间有许多交互,以保证数据按顺序排列(使用 UDP 时您必须考虑的另一件事,即数据不是有序的)。

最重要的是,代码中的计时器必须等待,直到字节通过非托管代码从非托管套接字层冒出,等等。那里将会有一些您无法克服的开销(非托管到托管的过渡)。

Most networked games don't use TCP but UDP for communications. The problem with that is that data can be easily dropped, which is something you have to account for.

With TCP, there are a number of interactions between the host/server to guarantee data in an ordered manner (another thing you have to account for using UDP, the fact that data is not ordered).

On top of that, the timer in your code has to wait until the bytes bubble up from the unmanaged socket layer through unmanaged code, etc, etc. There is going to be some overhead there that you aren't going to be able to overcome (the unmanaged to managed transition).

如此安好 2024-07-17 23:56:30

您说您尝试了“IP”,并且您大概是通过指定 ProtocolType.Ip 来做到这一点的,但当您将其组合时,我不知道这真正意味着什么(即我不知道“幕后”选择了什么协议)使用 SocketType.Stream。

正如 casperOne 所说,如果您使用 TCP,请查看设置 SocketOptionName.NoDelay 是否对您有任何影响。

You said you tried "IP", and you presumably did that by specifying ProtocolType.Ip, but I don't know what that really means (i.e. I don't know what protocol is chosen 'under the hood') when you combine that with SocketType.Stream.

As well as what casperOne said, if you're using TCP then see whether setting SocketOptionName.NoDelay makes any difference to you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文