V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
felix021
V2EX  ›  程序员

TCP:学得越多越不懂

  felix021 ·
felix021 · 2020-04-06 11:35:43 +08:00 · 8024 次点击
这是一个创建于 536 天前的主题,其中的信息可能已经有所发展或是发生改变。

周末小课堂又开张了,这次我们来聊一聊 TCP 协议。

== 握手 ==

多少有点令人意外的是,大多数程序员对 TCP 协议的印象仅限于在创建连接时的三次握手。

严格地说,“三次握手”其实是一个不太准确的翻译,英文原文是 "3-way handshake",意思是握手有三个步骤。

不过既然教科书都这么翻译,我就只能先忍了。

“三次握手”的步骤相信各位都非常熟悉了:

A: 喂,听得到吗 (SYN)
B: 阔以,你呢 (SYN-ACK)
A: 我也阔以,开始唠吧 (ACK)

(咦,这不是远程面试的开场白吗)

那么问题来了:为什么不是 2 次握手或者 4 次握手呢?

== 3 次 ==

针对“为什么不是 4 次”,知乎的段子手是这么回答的:

A: 喂,听得到吗 (SYN)
B: 阔以,你呢 (SYN-ACK)
A: 我也阔以,你呢 (SYN-ACK)
B: ...我不想和傻\*说话 (FIN)

<s>由此可见知乎质量的下降。</s>

实际上,上面省略了真正重要的信息,在握手过程中传输的,不是“你能不能听得到”,而是:

A: 喂,我的数据从 x 开始编号 (SYN)
B: 知道了,我的从 y 开始编号 (SYN-ACK)
A: 行,咱俩开始唠吧 (ACK)

协商一个序号的过程需要一个来回(告知 + 确认),理论上需要 2 个来回( 4 次),互相确认了双方的初始序号( ISN,Initial Sequence Number ),才能真正开始通信。

由于第二个来回的“告知”可以和前一次的“确认”合并在同一个报文里(具体怎么结合后面讲),因此最终只需要 3 次握手,就可以建立起一个 tcp 链接。

这也解释了为什么不能只有 2 次握手:因为只能协商一个序号。

不过话说回来,知乎段子手的回复也不是全在抖机灵:毕竟,发起方怎么才能确认接收方已经知道发起方知道接收方知道了呢?即使发起方再问一遍,接收方又怎么知道发起方知道了接收方知道了呢?

很遗憾,结论是:无论多少个来回都不能保证双方达成一致。

由于实践中丢包率通常不高,因此最合理的做法就是 3 次握手( 2 个来回),少了不够,多了白搭;同时配上相应的容错机制。

例如 SYN+ACK 包丢失,那么发起方在等待超时后重传 SYN 包即可。

想想看,如果最后一个 ACK 丢了会怎样?

然后问题又来了:为什么需要协商初始序号,才能开始通信呢?

== 可靠 ==

我们都知道,tcp 是一个“可靠”( Reliable )的协议。

这里“可靠”指的不是保证送达,毕竟网络链路中存在太多不可靠因素。

在 IETF 的 RFC 793 ( TCP 协议)中,Reliability 的具体定义是:TCP 协议必须能够应对网络通信系统中损坏、丢失、重复或者乱序发送的数据。

Reliability:

The TCP must recover from data that is damaged, lost, duplicated, or delivered out of order by the internet communication system.

https://tools.ietf.org/html/rfc793

为了保证这一点,tcp 需要给每一个 [字节] 编号:双方通过三次握手,互相确定了对方的初始序号,后续 [每个包的序号 - 初始序号] 就能标识该包在字节流中所处的位置,这样就可以通过重传来保证数据的连续性。

举个例子:

  • 发送方( ISN=4000 )
    • 发出 4001 、4002 、4003 、4004
    • (假设每个包只有 1 字节的数据)
  • 接收方
    • 收到 4001 、4002 、4004
    • 4003 因为某种原因没有抵达
    • 这时上层应用只能读到 4001 、4002 中的信息

由于接收方没有收到 4003,因此给发送方的 ACK 中,序号最大值是 4003 (表示收到了 4003 之前的数据)。

过了一段时间( Linux 下默认是 1s ),发送方发现 4003 一直没被 ACK,就会重传这个包。

当接收方最终收到 4003 以后,上层应用才可以读到 4003 和 4004,从而保证其收到的消息都是可靠的。(以及,接收方需要给发送方 ACK,序号是 4005 )

注意:虽然 ISN=4000,但是发送方发送的第一个包,SEQ 是 4001 开始的,TCP 协议规定 SYN 需要占一个序号(虽然 SYN 并不是实际传输的数据),所以前面示意图中 ACK 的 seq 是 x+1 。同样,FIN 也会占用一个序号,这样可以保证 FIN 报文的重传和确认不会有歧义。

但是,为什么序号不能从 0 开始呢?

== 可靠² ==

真实世界的复杂性总是让人头秃。

我们知道,操作系统使用五元组(协议=tcp,源 IP,源端口,目的 IP,目的端口)来标识一个连接,当一个包抵达时,会根据这个包的信息,将它分发到对应的连接去处理。

一般情况下,服务器的端口号通常是固定的(如 http 80 ),而操作系统会为客户端随机分配一个最近没有被使用的端口号,因此包总能被分发到正确的连接里。

但在某些特殊的场景下(例如快速、连续地开启和关闭连接),客户端使用的端口号也可能和上一次一样(或者用了其他刚断开的连接的端口号)。

而 TCP 协议并不对此作出限制:

The protocol places no restriction on a particular connection being used over and over again. ... New instances of a connection will be referred to as incarnations of the connection.

那么:

  • 如果前一个连接的包,因为某种原因滞留在网络中,这会儿才送达,客户端可能无法区分(其 sequence number 在本连接中可能是有效的)。

  • 恶意第三方伪造报文的难度很小。注意,在这个场景里,第三方并 [不需要] 处于通信双方的链路之间,只要他发出的报文可以抵达通信的一方即可。

因此我们需要精心挑选一个 ISN,使得上述 case 发生的可能性尽可能低。

注意:不是在 tcp 协议的层面上 100%避免,因为这会导致协议变得更复杂,实现上增加额外的开销,而在绝大多数情况下是不必要的。如果需要“100%可靠”,需要在应用层协议上增加额外的校验机制;或者使用类似 IPSec 这样的网络层协议来保证对包的有效识别。

那么,ISN 应该如何挑选呢?

== ISN 生成器 ==

说起来其实很简单:

TCP 协议的要求是,实现一个大约每 4 微秒加 1 的 32bit 计数器(时钟),在每次创建一个新连接时,使用这个计数器的值作为 ISN 。

假设传输速度是 2 Mb/s,连接使用的 sequence number 大约需要 4.55 小时才会溢出并绕回( wrap-around )到 ISN 。即使提高到 100 Mb/s,也需要大约 5.4 分钟。

而一个包在网络中滞留的时间通常是有限的,这个时间我们称之为 MSL ( Maximum Segment Lifetime ),工程实践中一般认为不会超过 2 分钟。

所以我们一般不用担心本次连接的早期 segment ( tcp 协议称之为 old duplicates )导致的混淆。

注:在家用千兆以太网已经逐渐普及、服务器间开始使用万兆以太网卡的今天,wrap-around 的时间已经降低到 32.8s (千兆)、3.28s (万兆),这个假定已经不太站得住脚了,因此 rfc1185 针对这种高带宽环境提出了一种扩展方案,通过在报文中加上时间戳,从而可以识别出这些 old duplicates 。

主要风险在于前面提到的场景:前一个连接可能传输了较多数据,因此其序列号可能大于当前连接的 ISN ;如果该连接的报文因为某种原因滞留、现在又突然冒出来,当前连接将无法分辨。

因此,TCP 协议要求在断开连接时,TIME-WAIT 状态需要保留 2 MSL 的时间才能转成 CLOSED (如下图底部所示)。

                              +---------+ ---------\      active OPEN
                              |  CLOSED |            \    -----------
                              +---------+<---------\   \   create TCB
                                |     ^              \   \  snd SYN
                   passive OPEN |     |   CLOSE        \   \
                   ------------ |     | ----------       \   \
                    create TCB  |     | delete TCB         \   \
                                V     |                      \   \
                              +---------+            CLOSE    |    \
                              |  LISTEN |          ---------- |     |
                              +---------+          delete TCB |     |
                   rcv SYN      |     |     SEND              |     |
                  -----------   |     |    -------            |     V
 +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
 |         |<-----------------           ------------------>|         |
 |   SYN   |                    rcv SYN                     |   SYN   |
 |   RCVD  |<-----------------------------------------------|   SENT  |
 |         |                    snd ACK                     |         |
 |         |------------------           -------------------|         |
 +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
   |           --------------   |     |   -----------
   |                  x         |     |     snd ACK
   |                            V     V
   |  CLOSE                   +---------+
   | -------                  |  ESTAB  |
   | snd FIN                  +---------+
   |                   CLOSE    |     |    rcv FIN
   V                  -------   |     |    -------
 +---------+          snd FIN  /       \   snd ACK          +---------+
 |  FIN    |<-----------------           ------------------>|  CLOSE  |
 | WAIT-1  |------------------                              |   WAIT  |
 +---------+          rcv FIN  \                            +---------+
   | rcv ACK of FIN   -------   |                            CLOSE  |
   | --------------   snd ACK   |                           ------- |
   V        x                   V                           snd FIN V
 +---------+                  +---------+                   +---------+
 |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
 +---------+                  +---------+                   +---------+
   |                rcv ACK of FIN |                 rcv ACK of FIN |
   |  rcv FIN       -------------- |    Timeout=2MSL -------------- |
   |  -------              x       V    ------------        x       V
    \ snd ACK                 +---------+delete TCB         +---------+
     ------------------------>|TIME WAIT|------------------>| CLOSED  |
                              +---------+                   +---------+

                      TCP Connection State Diagram
                               Figure 6.

( tcp 连接状态图,截取自 rfc 793 )

那么问题又来了:为什么只有 TIME-WAIT 需要等待 2MSL,而 LAST-ACK 不需要呢?

== 报文 ==

针对 TCP 协议可以提的问题太多了,写得有点累,所以这里不打算继续自问自答了。

但写了这么多,还没有看一下 TCP 报文是什么结构的,实在不应该,这里还是祭出 rfc 793 里的 ascii art (并顺便佩服 rfc 大佬的画图功力)

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            TCP Header Format

简单介绍下:

  • 一行是 4 个字节( 32 bits ),header 一般共 5 行( options 和 padding 是可选的)
  • 第一行包含了源端口和目的端口
    • 每个端口 16bits,所以端口最大是 65535
    • 源 IP 和目的 IP 在 IP 报文头里
  • 第二行是本次报文的 Sequence Number
  • 第三行是 ACK 序列号
  • 第四行包含了较多信息:
    • 数据偏移量:4 字节的倍数,最小是 0101 ( 5 ),表示数据从第 20 个字节开始(大部分情况)
    • 控制位( CTL ):一共 6 个,其中的 ACK 、SYN 、FIN 就不介绍了
    • RST 是 Reset,遇到异常情况时通知对方重置连接(我们敬爱的防火墙很爱用它)
    • URG 表示这个报文很重要,应该优先传送、接收方应该及时给上层应用。URG 的数据不影响 seq,实际很少被用到,感兴趣的话可以参考下 RFC 854 ( Telnet 协议)
    • PSH 表示这个报文不应该被缓存、应当立即被发送出去。在交互式应用中比较常用,如 ssh,用户每按下一个键都应该及时发出去。注意和 Nagle 算法可能会有一些冲突。
    • 窗口大小:表示这个包的发送方当前可以接受的数据量(字节数),从这个包里的 ack 序号开始算起。用于控制滑动窗口大小的关键字段就是它了。

举个例子,三次握手的第二步,SYN 和 ACK 合并的报文就是这么生成的:

  • Sequence Number 填入从 ISN 生成器中获取的值
  • Acknowledgement Number 填入 [发送方的序号 + 1]
  • 将控制位中的 ACK 位、SYN 位都置 1

写不动了,真是没完没了(相信看到这里的同学已经不多了),但是 TCP 协议中还有很多有意思的设计本文完全没有涉及,文末我给出一些推荐阅读的链接,供感兴趣的同学参考。

== 总结 ==

  • TCP“三次握手”翻译不准确
  • 握手的目的是双方协商初始序列号 ISN
  • 序列号是用于保证通信的可靠性
  • 不使用 0 作为 ISN 可以避免一些坑
  • TCP 报文里包含了端口号、2 个序列号、一些控制位、滑动窗口大小
  • 我在字节跳动网盟广告业务线(穿山甲),由于业务持续高速发展,长期缺人。关于字节跳动面试的详情,可参考我之前写的

~ 投递链接 ~

后端开发(上海) https://job.toutiao.com/s/sBAvKe

后端开发(北京) https://job.toutiao.com/s/sBMyxk

广告策略研发(上海) https://job.toutiao.com/s/sBDMAK

其他地区、职能线 https://job.toutiao.com/s/sB9Jqk

== 推荐阅读 ==

[1] RFC 793:TRANSMISSION CONTROL PROTOCOL

https://tools.ietf.org/html/rfc793

[2] Coolshell - TCP 的那些事儿 (上 & 下)

https://coolshell.cn/articles/11564.html

https://coolshell.cn/articles/11609.html

[3] 知乎 - TCP 为什么是三次握手,而不是两次或四?

https://www.zhihu.com/question/24853633

34 条回复    2020-11-27 18:24:26 +08:00
felix021
    1
felix021   2020-04-06 11:45:05 +08:00
回答文中的问题:三次握手中的最后一个 ACK 丢了会怎样?

A:如果接着有数据要发(例如 http 请求),由于在 A 看来连接已经建立,可以立即发出下一个数据包,这个报文中也会有 ack seq,B 收到后就能成功建立连接。

B:如果一直没收到 ACK,SYN+ACK 这个包的计时器会超时,然后主动重传,直到收到 A 的 ACK ;如果重传多次仍然没有收到 ACK,则停止尝试,关闭该 TCP 连接。
felix021
    2
felix021   2020-04-06 15:24:50 +08:00
827 次点击,627 个会员阅读,21 个收藏,1 个感谢,0 个回复

_(:-] 」∠)_
Liampor
    3
Liampor   2020-04-06 15:40:47 +08:00
赞,就是那种字符画是用什么画的呢
LosLord
    4
LosLord   2020-04-06 16:01:24 +08:00   ❤️ 1
现在招人都这么花式的吗
also24
    5
also24   2020-04-06 16:22:52 +08:00   ❤️ 3
感谢楼主持续分享,正巧最近在看一本书风格很不错,顺道推荐一下:
《 Wireshark 网络分析就这么简单》
https://book.douban.com/subject/26268767/


很多人没有理解三次握手,我觉得其实是有个小地方被忽略了:

A: 喂,我的数据从 x 开始编号 (SYN)( seq-A = x )
B: 知道了,我的从 y 开始编号,我知道你从 x 开始编号了 (SYN-ACK)( seq-B = y, ack-A = x+1 )
A: 行,我知道你从 y 开始编号了,咱俩开始唠吧 (ACK)( seq-A = x+1, ack-B = y+1 )

x 序列的 seq ack 和 y 序列的 seq ack 其实是两条线,分开来看更清晰。
很多书本或者教程里在这个部分写的有点模糊,就让一些人混淆了。


另:
关于 seq num 的选择问题,rfc793 确实提出了 4 微秒变化的方案。
但是在 rfc1948 中提出了针对 seq num 进行猜测攻击的问题。
并在 rfc6528 中提出了新的 seq num 生成算法用于替换 RFC793 中的方案。

在 rfc7414 可以查阅到更多关于 TCP 协议的变动。

参考链接:
https://book.douban.com/subject/26268767/
https://tools.ietf.org/html/rfc793
https://tools.ietf.org/html/rfc1948
https://tools.ietf.org/html/rfc5961
https://tools.ietf.org/html/rfc6528
https://tools.ietf.org/html/rfc7414
hcocoa
    6
hcocoa   2020-04-06 16:35:12 +08:00
看题目以为要讨论拥塞控制
lhx2008
    7
lhx2008   2020-04-06 16:57:43 +08:00 via Android
确实的干货,知乎上面的那个回答完全就是瞎搞
SnoopyCat
    8
SnoopyCat   2020-04-06 17:00:15 +08:00
支持支持
fishioon
    9
fishioon   2020-04-06 17:26:40 +08:00
优秀的文章;其实某种意义上 TCP 是支持 2 次握手的,TCP 握手第三步是客户端发 ACK 给服务端,假如这时候不发送 ACK,直接发 DATA+ACK,服务也是能够正常转换成 established 并且接受数据的
dexter
    10
dexter   2020-04-06 18:47:57 +08:00
收藏收藏,浅显易懂,像我这样的小白都看的懂了
ujued
    11
ujued   2020-04-06 18:50:36 +08:00 via iPhone   ❤️ 1
1. 注意:不是在 tcp 协议的层面上 100%避免,因为这会导致协议变得更复杂,实现上增加额外的开销,而在绝大多数情况下是不必要的。如果需要“100%可靠”,需要在应用层协议上增加额外的校验机制;或者使用类似 IPSec 这样的网络层协议来保证对包的有效识别。

不是太理解。TCP 是可以 100%准确传递上层要求传递的数据分组的,收到 ACK 分组,才算分组传递成功,这是有硬性要求分组准确有序传达。

2. TCP“三次握手”翻译不准确

三次握手这个还算准确,毕竟礼仪之邦!
3 次数据分组的发送,可以理解为 3 种方式握手,即双方相互握手。
说是 一次握手分 3 步 问题也不大。


3. 序列号是用于保证通信的可靠性

更准确的讲,序列号是为了解决 ACK 分组受损或丢失而超时重发哪些分组,以及分组排序(选择重传差错恢复策略)。通信的可靠性不仅仅是由序列号保证的,是和检验和、ACK 分组、重传机制一起协作完成的。


4. 不使用 0 作为 ISN 可以避免一些坑

可以用 0 作为 ISN,每个 TCP 连接 ISN 起始号是随时间递增取模再加一个 hash 的值,可以为 0 。不过不会固定使用 0 或任何可以猜到的数字作为起始号是为了安全考虑。RFC1948 有详细介绍,以及为什么在 TCP 协议解决,如何解决的。大致问题是这样的:
如果在 3 次握手中,攻击者在发起连接的一方未收到 ACK 时而率先发送自己的 ISN,而抢先和对方建立连接,这时 3 次握手成了非法入侵者的机会。
also24
    12
also24   2020-04-06 19:13:12 +08:00   ❤️ 1
@ujued #11
1 、联系上下文来看,楼主说的 『 100%避免』指的应该是:
精心挑选的 ISN 无法 100% 的避免『误收旧包』和『恶意伪造』这两种情况。

2 、这个全看个人喜好,我赞同『三次』更容易带来误解

3 、其实我感觉楼主这里应该没有将它描述为充分条件的意思,如果描述为这样可能会更严谨:
TCP 设计了若干基于序列号的机制,用于保证通信的可靠性。


4 、联系上下文来看,楼主所说的『不使用 0 作为 ISN 』应该指的是:
『不固定使用 0 』 作为 ISN 。而不是说 ISN 『始终不应为 0 』。


另外,『不固定使用 0 』最初应该只是为了避免 『误收旧包』。
后续 rfc1948 提出的『不使用容易被猜到的数字』才是为了安全原因。

这一点可以在 rfc793 中查证:
To avoid confusion we must prevent segments from one incarnation of a connection from being used while the same sequence numbers may still be present in the network from an earlier incarnation. We want to assure this, even if a TCP crashes and loses all knowledge of the sequence numbers it has been using. When new connections are created, an initial sequence number (ISN) generator is employed which selects a new 32 bit ISN. The generator is bound to a (possibly fictitious) 32 bit clock whose low order bit is incremented roughly every 4 microseconds. Thus, the ISN cycles approximately every 4.55 hours. Since we assume that segments will stay in the network no more than the Maximum Segment Lifetime (MSL) and that the MSL is less than 4.55 hours we can reasonably assume that ISN's will be unique.
ujued
    13
ujued   2020-04-06 19:29:19 +08:00 via iPhone
@also24 ❤️
AllenHua
    14
AllenHua   2020-04-06 19:35:54 +08:00 via iPhone
你收藏有了
felix021
    15
felix021   2020-04-06 19:39:36 +08:00 via Android
@Liampor 我猜是 vim 或者 emacs
felix021
    16
felix021   2020-04-06 19:39:58 +08:00 via Android
@LosLord 招人太难啊。
felix021
    17
felix021   2020-04-06 19:46:07 +08:00 via Android
@hcocoa 本来想写一点的,但开了个头就写了 3000 多字,实在写不动了
felix021
    18
felix021   2020-04-06 19:50:10 +08:00 via Android
@fishioon 对的,这点我在第一个回复里提到了。实际上对于 A 来说,多发一个 ack 没啥区别,因为不用等 B 的回复,数据可以立即发出; 但对于 B 来说,没有 ack 就没法继续,这头看起来还是 3 次。
fishioon
    19
fishioon   2020-04-06 21:25:57 +08:00   ❤️ 1
@Liampor 现在有 http://asciiflow.com 来画这种图了
felix021
    20
felix021   2020-04-06 21:29:49 +08:00
@fishioon 这个厉害了
shino996
    21
shino996   2020-04-06 21:32:52 +08:00 via iPhone
我终于学会了直接拉到底🌚
felix021
    22
felix021   2020-04-06 21:49:46 +08:00
@shino996 都拉到底了,不投个简历吗
churchmice
    23
churchmice   2020-04-06 22:40:34 +08:00
不错
但说实话,这都是通信系统的常规操作
psirnull
    24
psirnull   2020-04-06 22:48:05 +08:00
疫情期间 不要握手
Keyes
    25
Keyes   2020-04-06 23:13:12 +08:00   ❤️ 1
@fishioon http://www.network-science.de/ascii
另推荐 Ascii Art Generator,我拿来做各种彩蛋 logo 用( 233
GeekBao
    26
GeekBao   2020-04-07 08:28:16 +08:00 via iPhone
嗯,从听到看完所有评论。加深印象一次。
sakura1
    27
sakura1   2020-04-07 15:12:57 +08:00
理论上完全的停等协议应该是不需要报文序列号的,不过吞吐量必然极地,tcp 会一次发送多个报文,我觉得这也需要 ISN 的一个原因
felix021
    28
felix021   2020-04-07 15:48:07 +08:00
@sakura1 对,为了实现滑动窗口,所以需要给字节编号
felix021
    29
felix021   2020-04-08 21:55:33 +08:00
@sakura1 想了下,如果没有序列号的话,停等协议也会有坑,如果网络中滞留的包出现了,还是需要某种机制去识别出来。
hawken
    30
hawken   2020-04-13 07:05:02 +08:00 via Android
文章写得很好,以后再复习一遍(希望近期面试用得上🤨)。不过三次握手的主要目的应该还有 阻止重复历史连接的初始化。这篇文章分析了一下: https://draveness.me/whys-the-design-tcp-three-way-handshake/

通过三次握手才能阻止重复历史连接的初始化;

通过三次握手才能对通信双方的初始序列号进行初始化;
Wirbelwind
    31
Wirbelwind   2020-04-13 19:00:43 +08:00
标准和实现上还是有一点区别的

TIME_WAIT 在实现上有一个定时器,2MSL(不同系统不一样时间.)
FIN_WAIT2 好像也是有一个定时器,Linux 大概是 MSL 时间。但是标准应该没有规定这里需要,按照标准 FIN_WAIT2 应该一直等待。
GSNote
    32
GSNote   2020-05-12 15:25:05 +08:00
@also24 用词精准
NGPONG
    33
NGPONG   301 天前
@felix021

好文挖坟,楼主您好,我是初学者,在预览文章中对以下段落感觉到困惑,不吝赐教

> TCP 协议的要求是,实现一个大约每 4 微秒加 1 的 32bit 计数器(时钟),在每次创建一个新连接时,使用这个计数器的值作为 ISN 。
>
> 假设传输速度是 2 Mb/s,连接使用的 sequence number 大约需要 4.55 小时才会溢出并绕回( wrap-around )到 ISN 。即使提高到 100 Mb/s,也需要大约 5.4 分钟

这个 sequence number 到底要使用多少,不是依照其内部实现的计数器当前时间的值来决定的嘛,为啥这里会和传输速率扯上关系(越快那么这个 wrap-around 就会缩短)
felix021
    34
felix021   301 天前 via Android
@NGPONG seq 被用来标识数据流中每一个字节的位置,传输速度越快,int32 就会越快被用到溢出。
关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2237 人在线   最高记录 5497   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 28ms · UTC 02:53 · PVG 10:53 · LAX 19:53 · JFK 22:53
♥ Do have faith in what you're doing.