TCP是否在每个数据包上都发送SYN/ACK,还是仅在建立初始连接时发送?

62

我有一个 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相比TCP有什么优势吗?


3
TCP/IP 中没有“数据包”这一术语。请参阅正确的术语:https://dev59.com/fHNA5IYBdhLWcg3wcdhk - Joe Phillips
6
@Phillips - TCP是建立在IP协议之上的协议。在TCP处理之前,没有"segments"这个概念。在此过程中,将传入的数据称为"packets"而不是"segments"是完全可以接受的,因为此时它们只是IP packets。数据以IP packets的形式进入TCP,然后作为segments、messages等形式输出。 - JSON
3个回答

119

有点像:

+-------------------------------------------------------+
|     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至少需要一个标头的带宽,加上底层附加的任何内容。但是数据段已经包括了所有这些...所以如果您正在发送数据,您可以同时免费发送一个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之上构建的某些应用层协议将具有一些特定于协议的方式来确认发送和接收的数据。

36
ACK可能会变得复杂。它不是每个数据包都需要,而是针对接收到的数据包数量,因此每8个数据包可能会有一个ACK。发送方有一个“窗口”,这是它在必须接收到ACK之前将要发送的量。然后还有选择性ACK,用于表示“已接收字节2000-8000,但未接收0-2000”。 - Zan Lynx
1
用户数据报协议通常用于查询-响应协议中,其中对查询的响应将表明已收到该查询,而未重复查询将向响应者证明其响应已被接收或查询发起者已放弃(响应者不关心哪种情况,因为在任何情况下,它的适当响应都是不再采取进一步行动)。 - supercat

20

SYN仅出现在起始段。

ACK出现在任意方向的后续段中。ACK还定义了窗口大小。例如,如果窗口大小为100,则发送方可以在期望接收ACK之前发送100个段。例如,如果发送方发送了100个段,但第50个段丢失,则接收方将收到1-49和51-100的片段。接收方然后将ACK设置为50(它期望的下一个段),并将窗口大小设置为1。发送方将重新发送带有序列号50的1个段。接收方然后将ACK设置为101,并将窗口大小恢复到更高的数字。

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

因此,这两种情况都不太正确。第一个情况实际上更接近现实,但所有SYN之后的数据包都必须包含ACK,以及确认号字段,该字段标识下一个期望的数据包编号。

会话的结束也涉及到使用FIN标记包和与其相关的ACK握手。

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

此外,如果是第一种情况,是否存在UDP比TCP更有优势,如果您只保持长时间的连接?

使用UDP时,您无法仅保持长时间的连接。没有连接。

SYN / ACK / FIN标志的这个序列是创建连接的要素。

对于UDP,没有SYN或ACK,因此通信是单向的,传递不能保证,顺序不能保持。但它具有较少的开销,因此在速度比可靠性更重要的情况下很有用,例如在流媒体中。

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

在TCP的维基百科条目和RFC中,有更多相关的内容。


3
除了阅读维基百科和RFC文档,我还推荐阅读W. Richard Stevens的《TCP/IP Illustrated, Volume 1 - The Protocols》这本书。它对大脑来说会更容易些 :) - Michael J. Gray
发送方将重新发送序列号为50的1个分段。接收方将ACK 101。难道不应该是接收方ACK 51吗?因为上一个接收到的分段是50。 - Rafael Eyng
我不理解关于“通信是单向的”这个评论。这完全没有意义。UDP只是IP上一个微不足道的、极其薄的层,因为它只是在IP上面加了一点巧克力酱,所以你可以在_两个_方向上发送UDP数据包。 - Cecil Ward
如果设计师选择使用UDP,则是为了获得更高的速度性能和最小化交换的流量,或者另一方面是为了允许完全控制通信方法。使用UDP,设计师可以自由选择构建一种新的协议。某些应用程序可能不需要可靠的传递、按顺序传递保证或其他类似TCP或SCTP提供的优点。但是,如果使用UDP,设计师可能需要进行更多的设计工作,从而使应用程序代码复杂化或最终发明一种自定义协议。 - Cecil Ward
@RafaelEyng 不需要,因为接收端缓冲了51-100段。当他在中间收到丢失的段时,他会按正确顺序放置它们,现在拥有1-100的所有内容。没有必要请求已经拥有的段。 - Jan

-1
想象一下: 最初的TCP标准RFC 793允许在第一个SYN数据包中发送数据。然而,今天不再是这样了。在三次握手的初始化过程中,请求方会收到一个单独的SYN数据包。假设A请求与B建立连接,因此A发送一个带有SYN位设置的数据包。B响应以确认接收并向A发送ACK + SYN数据包。从此之后可以传输数据。

Dordal对此问题有很好的解释。请点击此处链接。


1
那个链接已经失效了。这就是为什么你不应该在答案中链接到外部内容的原因。 - neuhaus
RFC 793,经修订后,至今仍然有效。请提供您所声称的RFC参考文献 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接