56

我有一个 TCP 服务器,它监听传入的客户端,然后每秒向它发送一个数据包。我想知道,SYN/ACK 数据包是否仅在初始连接时发送,所以它看起来像这样:

<client connect>
SYN
ACK
DATA
DATA
DATA
<client disconnect>

还是像这样随每个数据包一起发送?

<client connect>
SYN
ACK
DATA

SYN
ACK
DATA

SYN
ACK
DATA
<client disconnect>

另外,如果是第一种情况,如果您只是长时间保持连接打开,UDP over TCP 有什么好处吗?

4

3 回答 3

107

有点像:

+-------------------------------------------------------+
|     client           network            server        |
+-----------------+                +--------------------|
|    (connect)    | ---- SYN ----> |                    |
|                 | <-- SYN,ACK -- |     (accepted)     |
|   (connected)   | ---- ACK ----> |                    |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/

when client sends...
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
|                 |                |                    |
|     (send)      | ---- data ---> |                    |
|                 | <---- ACK ---- |  (data received)   |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/

when server sends...
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
|                 |                |                    |
|                 | <--- data ---- |       (send)       |
| (data received) | ---- ACK ----> |                    |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/

...and so on, til the connection is shut down or reset

SYN 开始一个连接;您通常只会在建立连接时看到它。但是所有通过 TCP 发送的数据都需要一个 ACK​​。必须考虑发送的每个字节,否则它将被重新传输(或在严重的情况下连接重置(关闭))。

但是,实际连接通常与上图不完全相同,原因有两个:

  • ACK 可以累积,因此一个 ACK​​ 可以确认到该点收到的所有内容。这意味着您可以使用一个 ACK​​ 确认两个或多个发送。
  • ACK 只是 TCP 标头中的标志和字段。发送一个至少需要一个标头的带宽,再加上较低层附加的任何内容。但是数据段已经包含了所有这些......所以如果你正在发送数据,你可以同时免费发送一个 ACK​​。

大多数 TCP/IP 堆栈都试图减少裸 ACK 的数量,而不会过度冒险重新传输或重置连接。所以像这样的对话是很有可能的:

\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
|                 |                |                    |
|                 | <--- data ---- |       (send)       |
| (data received) |                |                    |
|     (send)      | -- data,ACK -> |                    |
|                 |                |  (data received)   |
|                 | <- data,ACK -- |       (send)       |
| (data received) |                |                    |
|  (wait a bit)   | <--- data ---- |       (send)       |
| (data received) |                |                    |
|     (send)      | -- data,ACK -> |                    |
|                 |                |  (data received)   |
|     (send)      | ---- data ---> |   (wait a bit)     |
|                 |                |  (data received)   |
|                 | <- data,ACK -- |       (send)       |
| (data received) |                |                    |
|  (wait a bit)   |   (dead air)   |                    |
|                 | ---- ACK ----> |                    |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/

至于 UDP,没有内置的 SYN 和 ACK 概念——UDP 本质上是“不可靠的”,并且不是面向连接的,因此这些概念并不适用。您的确认通常只是服务器的响应。但是一些建立在 UDP 之上的应用层协议将有一些特定于协议的方式来确认发送和接收的数据。

于 2010-08-30T21:57:09.540 回答
16

SYN 只是在开头。

ACK 在任一方向的后续段上。ACK 还将定义窗口大小。例如,如果窗口大小为 100,则发送方可以发送 100 个段,然后才能收到 ACK。例如,如果发送方发送了 100 个段,但第 50 段丢失了,那么接收方将得到 1-49 和 51 -100。然后接收方将 ACK 50(它期望的下一个段)并将窗口大小设置为 1。发送方将重新发送 1 个序列号为 50 的段。然后接收方将 ACK 101 并将窗口大小重新设置为更高的数字。

两者实际上都是 TCP 标头中的字段,可以与数据一起发送,尽管 SYN 和第一个 ACK​​ 通常是无数据的。

因此,您描述的两种情况都不是完全正确的。第一个实际上更接近现实,但是在 SYN 之后的所有数据包都必须包含一个 ACK​​,以及一个确认号字段,用于标识预期的下一个数据包的编号。

会话的结束还涉及与带有 FIN 标志的数据包和与之相关的 ACK 的握手。

交换的序列号用于识别丢失的数据包并启用重试机制,并以正确的顺序重新组合整个数据包流。

另外,如果是第一种情况,如果您只是长时间保持连接打开,UDP over TCP 有什么好处吗?

使用 UDP,您不能只在很长一段时间内保持连接打开。没有联系。

这个 SYN/ACK/FIN 标志序列是建立连接的原因。

使用 UDP,没有 SYN 或 ACK,因此通信是单向的,不能保证交付,也不能保留顺序。但它的开销较小,因此当速度比可靠性更重要时,它很有用,例如在流媒体中。

这有点简化了,但这是我目前能做的最好的。

在关于 TCP 的维基百科条目中还有更多关于此的内容,当然在 RFC 中。

于 2010-08-30T23:07:02.450 回答
0

想象一下:原来的 TCP 标准 RFC 793 允许数据与第一个 SYN 数据包一起发送。然而,今天的情况并非如此。在连接请求者发起三向握手期间,您得到的是一个单独的 SYN 数据包。假设 A 请求与 B 连接,因此 A 发送一个设置了 SYN 位的数据包。B 以 ACK 响应以确认收到,并向 A 发送 ACK + SYN 数据包。此后可以传输数据。

Dordal 对这个问题有很好的解释。点击这里链接。

于 2017-12-30T18:54:06.897 回答