面试被问到这个问题,当时没回答上来,网络上查了很久,感觉还是这个比较全面,虽然文章并没有说清楚,但结合文章及后面的评论就差不多搞懂了。
TCP是基于IP的虚电路可靠的全双工通信服务,基本上可以分为链接建立,数据传输,链接拆除三个阶段。
为什么链接建立阶段采用三次握手机制?
先约定两个名字。A代表链接建立的发起方,B代表链接建立的接受方。
通过这个过程,TCP的全双工的虚电路服务就建立起来了。
为什么是三次握手?
我认为原因是TCP链接逻辑上区分成两个通道,一个通道用于A-》B,另一个通道用于B-》A。这一点从链接建立阶段看不明晰,但是链接拆除阶段非常清楚。我们
再回过头看看链接建立阶段,就非常明确的看出来,这三次握手确实是用于建立这个链路上的两个通道的。
那么为什么要三次握手?最近看到弯曲评论上有一篇文章,说TCP链接甚至可以建立半链接,也就是只建立一个通道,数据只能单向流动。这时候的三次握手就退化成两
API并没有提供建立半链接的能力,但是下层的协议确实具有]
所以说三次握手也不是必需的。
我们暂且不关心建立半链接。
究竟为什么需要三次握手?
从逻辑上讲,三次握手是最省的建立TCP链路[两个通道]规程机制了,所以就应该三次握手。
但是,实际上我们必须进行三次握手么?我们知道UDP是数据报服务,也就是无连接的服务。其实无连接的服务也可以看作某种连接服务,比如:看做链接建立、数据传
但是为什么TCP链接要三次握手进行建立?
我个人认为其实没有理由非得如此。不过我们可以通过TCP的拥塞控制机制得到一些启示。TCP的拥塞控制由“慢启动(Slow
start)”和“拥塞避免(Congestion avoidance)”,“快速重传(Fast retransmit)”、“快速恢复(Fast
Recovery)”,选 择性应答( selective
acknowledgement,SACK)等一系列机制组成的。其中慢启动是TCP链接建立以后,开始发送数据时的策略。为了避免TCP拥塞,采用缓慢提高T
当然,有人说三次握手是为了约定初始序号(ISN,Initial Sequence
Number,这儿的序号并不是按照数据包自然数序排列的,其实叫成偏移Offset更合理,因为它其实是指出这个包携带的数据在整个TCP数据流中的偏移量的
+
y,如果达到了最大值,就绕到0那儿开始回环。顺便说一句,最初序号是32bit的,现在提升到64bit了,因为要保证协议栈能识别出相同序号的不同的包,不
http://d.wanfangdata.com.cn/Periodical_czsfgdzkxxxb200902019.aspx]
归根结底,TCP链接的建立、使用、拆除三阶段是清晰的分离的,链接建立阶段是独立的,也不能用于传递数据,当然,数据传递阶段也没有建立链接的功能。
那么现在还是那个问题:为什么需要三次握手?
看评论:
1.
这是通信当中的基本问题, 看明白Two Generals' Problem:
http://en.wikipedia.org/wiki/Two_Generals'_Problem
就很容易明白为什么三次握手是必须的了.
这个问题的本质是, 通过一个不完全可靠的信道, 最少需要几次消息传输, 信道两边的人能够对一个问题达成一致. 对于TCP来说, 无论有没有初始
序号的要求, 想要两边都同意开始传出数据, 就至少需要3次消息的交换:
0次: 显然不行
1次: A->B, A不知道B是否同意
2次: A->B, B->A. B不知道A是否收到自己的消息, 因为信道不完全可靠
3次: A->B, B->A, A->B. 两边都收到了对方的ACK, 意味着各自都了解了对方的意图, 从而可以对是否开始通信这个最简单的问题
达成一致.
2.
这种主考官很好对付,我就遇到过类似的。直接就说三次握手的协议是理论结果和工程实践结合后,选择的一个折中方案,在互联网出现之初,这是一个比较好的方案。主
对于现在的互联网,三次握手其实已经落后了,效率低下。所以现在才有不少新的号称高性能面向连接的协议出来讨生活。
3.
TCP的三次握手是妇孺皆知的事情,似乎没有什么可以讨论的。笔者也曾经面试别人,提出过下面的问题:
“TCP为何要三次握手,四次断开?”
笔者期望的答案是:
TCP的连接建立一定要双向,所以第二个报文SYN/ACK齐发,而断开的时候可以保持半连接,即断开一半。理论依据是笔者根据Socket的实现得出的,Socket之中没有给出建立半连接的API,但是给出了shutdown可以关闭半连接。
我的理解:
tcp要建立一个全双工的连接,也就是可以理解为两个单独的通道,A->B和B->A。当A发给B 数据包(syn)时,可以理解为要和B建立链接,B回复给A数据包(ACK+SYN)时,即B同意建立A->B的连接,同时询问要和A建立链接,然后A回复B数据包(ACK)表示同意建立B->A的连接。这样一个全双工的连接便建立起来了。