使用TCP进行实时命令:Nagle算法造成巨大延迟,我该怎么办?

发布于 2024-08-08 13:50:58 字数 2175 浏览 2 评论 0原文

我正在编写一个套接字服务器和 Flash 游戏客户端。游戏需要实时命令,例如移动和转弯。服务器尽快将这些命令发送到客户端非常重要,因为否则其他客户端将与移动/转动的客户端大量不同步。

这是由 Nagle 算术引起的问题的示例:

注意:如果您想了解这些命令的含义,请参阅下面的命令表。

第一个是我移动的船(向前+向右移动,向前已收到但向右未收到)

发送命令的客户端:

84796: Sending data: 2#4
84796: Sending data: 2#2
84904: Sending data: 2#3
84904: Sending data: 2#0
86187: Sending data: 2#4
86188: Sending data: 2#2
86374: Sending data: 2#3
86404: Sending data: 2#0

接收命令的客户端:

79244: Raw receive: 3#3#4$
79244: New command: 3#3#4
79398: Raw receive: 3#3#2$3#3#3$3#3#0$
79399: New command: 3#3#2
79399: New command: 3#3#3
79399: New command: 3#3#0
80635: Raw receive: 3#3#4$
80635: New command: 3#3#4
80908: Raw receive: 3#3#2$3#3#3$3#3#0$
80908: New command: 3#3#2
80908: New command: 3#3#3
80908: New command: 3#3#0

“时刻”是一个奇怪的术语,并不代表我想说的意思, 但这里似乎是上一个命令之后的时间量(以毫秒为单位)

  1. 向前移动 客户端 A 发送(时刻:0),客户端 B 接收(时刻:0)

  2. 右转 客户端A发送(时刻:0),客户端B接收(时刻:155)

  3. 停止移动 客户端A发送(时刻:108),客户端B接收(时刻:0)

  4. 停止转动 客户端A发送(时刻:0),客户端B接收(时刻:0)

  5. 前进 客户端A发送(时刻:1283),客户端B接收(时刻:1236)

  6. 右转 客户端A发送(时刻:1),客户端B接收(时刻:273)

  7. 停止移动 客户端A发送(时刻:186),客户端B接收(时刻:0)

  8. 停止转动 客户端A发送(时刻:30),客户端B接收(时刻:0)

这是命令对应的命令表:

Client->Client->服务器

2# (movement info)
0) now not turning
1) now turning left
2) now turning right
3) now not moving
4) now moving forward

服务器->客户端

3# (movement info)
[shipId]#
0) now not turning
1) now turning left
2) now turning right
3) now not moving
4) now moving forward

因此,您可以看到命令由于“Nagle”而完全不同步。这会导致其他客户端在开始移动命令的同时接收到停止移动命令,从而导致该玩家根本无法移动。

这就是为什么我需要 TCP 服务器尽可能快地实时发送这些命令。一个简单的修复方法就是禁用 Nagle。但是,我已经 Google 搜索(请注意,他关于 tcp 的建议消息部分在我的系统中实现,但与时间无关)一点,并注意到人们绝对不建议禁用 Nagle。

我是否真的不应该为此原因禁用 Nagle 算法而应该寻找其他解决方案?为什么(不)?

提前致谢。 - 汤姆

I'm writing a socket server and flash game client. The game requires real-time commands such as movement and turning. It is important for these commands to be sent by the server to the client as soon as possible because the other clients will otherwise desynchronise a lot with the moving/turning client.

This is an example of the problem caused by the Nagle arithmetic:

Note: see the command table below if you wish to understand what these commands mean.

First one is the ship I moved (moved forward + right, forward was received but right not)

The client sending commands:

84796: Sending data: 2#4
84796: Sending data: 2#2
84904: Sending data: 2#3
84904: Sending data: 2#0
86187: Sending data: 2#4
86188: Sending data: 2#2
86374: Sending data: 2#3
86404: Sending data: 2#0

The client receiving commands:

79244: Raw receive: 3#3#4$
79244: New command: 3#3#4
79398: Raw receive: 3#3#2$3#3#3$3#3#0$
79399: New command: 3#3#2
79399: New command: 3#3#3
79399: New command: 3#3#0
80635: Raw receive: 3#3#4$
80635: New command: 3#3#4
80908: Raw receive: 3#3#2$3#3#3$3#3#0$
80908: New command: 3#3#2
80908: New command: 3#3#3
80908: New command: 3#3#0

"moment" is a strange term that doesn't mean what I am trying to say,
but here it seems the amount of time in milliseconds after the previous command

  1. move forward
    send by client A (moment: 0), received by client B (moment: 0)

  2. turn right
    send by client A (moment: 0), received by client B (moment: 155)

  3. stop moving
    send by client A (moment: 108), received by client B (moment: 0)

  4. stop turning
    send by client A (moment: 0), received by client B (moment: 0)

  5. move forward
    send by client A (moment: 1283), received by client B (moment: 1236)

  6. turn right
    send by client A (moment: 1), received by client B (moment: 273)

  7. stop movement
    send by client A (moment: 186), received by client B (moment: 0)

  8. stop turning
    send by client A (moment: 30), received by client B (moment: 0)

This is the command table corresponding with the commands:

Client-> Server

2# (movement info)
0) now not turning
1) now turning left
2) now turning right
3) now not moving
4) now moving forward

Server-> Client

3# (movement info)
[shipId]#
0) now not turning
1) now turning left
2) now turning right
3) now not moving
4) now moving forward

So, what you can see is that the commands are totally desynched because of "Nagle". This causes the stop movement command to be received by other clients at the same time as the start movement command, causing that player to not move at all.

This is why I need these commands to be send in real-time, as fast as possible by the TCP server. An easy fix would be to simply disable Nagle. However, I have googled (note that his suggestion about tcp message partial is implemented in my system but has nothing to do with timing) a bit and noticed that people absolutely not recommend disabling Nagle.

Is it true that I should not disable the Nagle algorithm for this cause and should instead look for an other solution? Why (not)?

Thanks in advance.
- Tom

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

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

发布评论

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

评论(5

情释 2024-08-15 13:50:58

您想要使用 TCP_NODELAY 关闭该套接字的 nagle 算法。现代操作系统中有一些设置可以在系统范围内禁用它,这是令人不悦的,但没有理由不使用您知道需要低延迟的套接字来执行此操作。

setsockopt(TCP_NODELAY)

You want to use TCP_NODELAY to turn off the nagle algorithm for that socket. There are settings in modern OSes to disable it on a system-wide basis, and that IS frowned upon, but there's not reason you shouldn't do it with a socket that you KNOW you need low latency on.

setsockopt(TCP_NODELAY)

醉南桥 2024-08-15 13:50:58

如果需要尽可能快,则应使用 UDP。

但请注意,TCP 提供交付订单保证,而 UDP 则不提供任何保证。

在这个应用程序中,这一点似乎是一个至关重要的重要点,因此您必须在顶部放置您自己的确认/重试命令排序机制。

如果您想坚持使用 TCP,TCP__NODELAY 会有所帮助。随着游戏的进展,您可能最终需要一个数据流,而 TCP 在没有 TCP_NODELAY 的情况下非常适合。

RFC1006 规范了一种通过 TCP 流发送数据包的机制。

If it needs to be as fast as possible, you should use UDP.

However, note that TCP provides guarantee of delivery and order whereas UDP provides none.

That point would seem to be a critically important point in this application so you would have to layer your own acknowledgement/retry command ordering mechanism on top.

If you want to stick with TCP, TCP__NODELAY will help. As your game advances you may end up requiring a stream of data that TCP will be well suited to without TCP_NODELAY.

RFC1006 formalizes a mechanism for sending packets over a TCP stream.

柏林苍穹下 2024-08-15 13:50:58

请注意,您的交换机、路由器、ISP 的 swithcgear.... 等也可能会耗尽它们的黑色小内脏。这些肮脏的小野兽可以读取数据包,因此它们有时会随意重写它们。

看看 RTP(它在 UDP 上运行)以获得更适合网络生存的方法。

Note that your switch, router, ISP's swithcgear.... etc may all also nagle their black little guts out. The dirty little beasts can read packets, so they take liberties and rewrite them sometimes..

Take a look at RTP ( which runs over UDP) for a more network -survivable approach.

十年九夏 2024-08-15 13:50:58

如果您的目标是让船只最终在每个人的屏幕上执行相同的操作,那么您将必须发送位置更新。您不能假设仅仅因为客户端 A 执行了:

  • 向左转
  • 等待 100 毫秒
  • 停止转动

,客户端 B 就会看到完全相同的 100 毫秒延迟。 Nagle 缓冲只会使这种情况变得更加极端 - 但总会出现抖动和变化的延迟。

您确实需要发送类似以下内容的内容:(

  • 我位于位置 X1,Y1,标题为 Z1)向左转
  • (我位于位置 X2,Y2,标题为 Z2)停止转动

,以便客户端不断重新同步。

If your goal is to have the ships end up doing the same thing on everyone's screen, you're going to have to send position updates. You can't assume that just because client A did:

  • Turn Left
  • Wait 100ms
  • Stop turning

that Client B will see that exact same 100ms delay. The Nagle buffering just makes this more extreme - but there will always be jitter and varying delays.

You really need to send something like:

  • (I am at position X1, Y1 with heading Z1) Turn Left
  • (I am at position X2, Y2 with heading Z2) Stop Turning

So that the clients are continually resynchronising.

゛清羽墨安 2024-08-15 13:50:58

请注意,Windows 实现了 延迟 ACK 以及 Nagle。这可能会给您的各个 TCP 消息带来额外 200 毫秒的延迟。据我所知,除非通过注册表项(以及在某些情况下启用注册表项的热补丁),否则无法在 Windows 上更改此设置。

Note that Windows implements delayed ACK as well as Nagle. This can give you an extra 200ms of delay on individual TCP messages. As far as I know, you can't change this on Windows except via a registry key (and a hotpatch to enable the registry key in some cases).

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