首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

TCP:三次握手和四次挥手

先说点题外话,最近看的东西比较乱,前两天主要看了HTTP的知识,但由于其知识点较为分散,概念很多,而且不太好用“发现问题à解决问题”这样的思路表达出来,如果只把知识点搬出来的话,读者看起来不仅会很枯燥,而且对于整个知识架构的来龙去脉都理解的不清晰不透彻,因此这方面知识我想再准备一下思路,过几天再发,而且由于HTTP涉及到的知识比较广,因此我可能会分几期来讲述。

此外,关于上面“发现问题à解决问题”的讲述思路,我认为是非常好的,而且我确实也是这么做的,因为万物出现必有其因,那些知识、概念、理论,都不是凭空产生的,很多都是大家发现问题以后尝试着去解决问题而产生的,因此我们学习知识,不能只靠死记硬背,而是要先了解它们产生的时代背景,为什么需要它们,而后才是考虑怎么实现它们。

因此我写的文章都是按照这样一种思路来讲述的,目的就是想让读者在当时的背景下思考问题,在源头思考问题,从而把握整个知识的前因后果,学到的东西才能更扎实,灵活理解比死记硬背效果要好得多。希望大家以后也按照这种方法去学习,对提高学习兴趣很有帮助哦!(好像跑题了咳咳)

大家应该多多少少听过TCP的三次握手,大致也能了解一些情况,但是对于“挥手”这个词可能就有点陌生了,在这里我想把TCP建立连接和断开连接的整个过程详细的说一下。

本来,一台主机是可以直接发送数据到另一台主机的,毕竟这些功能都是下面的网络层数据链路层实现好了的,但是这并不能保证可靠性和完整性,比如对方主机业务繁忙,你发过去的报文全部丢失也是有可能的,你肯定不希望你发的数据就这么石沉大海,这时候建立一个可靠的连接就是非常必要的了。

之前我写过一篇关于数据可靠性传输的文章,大家可以去翻一下,这里只讨论怎么建立可靠连接。

好的我们进入正题

TCP三次握手和四次挥手

其实握手的整个过程也很简单,假设A要和B建立连接:

A发送报文1到B请求建立连接àB返回报文2表示可以建立连接àA发送确认报文3给B以确认B的响应,连接建立完毕,双方可以开始传送数据了。

到这里可能会有人要问了:“为什么是三次握手?两次不够吗?”,这里确实和我们的实际生活不太一样,我们可能在QQ微信上通信,发个“在吗”,然后接收到回应就可以正常聊天了,但是在计算机网络中却不能这样做,我们知道,发送的报文是有延迟的,而且延迟时间是和网络状况有很大的关系的。

请想象这样一种情况:主机A向B发送报文1,但是因为网络问题,报文1迟迟发不过去,A当然不会一直这么傻傻等B的回应啊,到了超时时间就会终止这条连接,那么假如我们用的是两次握手,B收到以后就应该返回报文2并打开连接,然而A已经是处于断开状态了,这样B就会一直处于连接状态,就造成了资源的浪费。

换成三次握手的话,B收到报文1后马上返回响应报文2,A收到B的响应报文2后再发送一个报文3向B进行确认,也就是说,先是A请求通信,而后B通知A自己已准备就绪最后A再通知B自己也已经准备就绪,这样的话,A和B就都知道双方已经处于就绪状态,这时候就可以正常的建立连接了。

如果不理解的话,我再举个例子就明白了,室友想让你帮忙带份饭,于是发信息给你(第一次握手),结果你隔了两小时才看到(网络延迟),这时候你会怎么办?如果你发一个“好的”,然后给他带饭,那么恭喜,你带着饭回到宿舍的时候很有可能看到室友已经吃上了外卖(两次握手的后果),但如果你看到以后先问他“我收到了你的消息,现在还用带饭吗?”(第二次握手),得到回复说要带以后你再去做,这才是比较正常的做法(第三次握手),当然如果没有回复就不带了hiahiahiahia(例子来自一名长期给室友带饭的同学,没错就是我55555)。

其实三次握手并不能一定保证A和B都处于连接就绪状态,因为主机并不能在每次收到确认报文的时候保证对方确实已经准备就绪,但如果这样设计的话,就会有四次握手,五次握手……这样下去岂不成无限循环了吗?所以说,三次握手基本上已经可以保证顺利通信了,虽然不是百分之百,但这确实是当下最经济有效的方式。

理解了原理,下面就让我们来看一下实际中的三次握手吧!

这里要介绍几个概念,它们都是存储在TCP报文头部信息里的标志位:

ACK(acknowledgement):返回响应报文中的确认标识。

Seq(Sequence number):顺序号码,主机的标识码

SYN(synchronous):用来表示建立连接的标志位,为1时表示想要建立连接

为了方便理解,我会将报文序号写在标识位的后面,比如Seq_1表示报文1中的Seq。根据图中的描述,我们可以看到建立连接的步骤是这样的:

①客户主机(下称为A),发送报文1给B,里面的信息是SYN_1和Seq_1,SYN_1为1,Seq_1则可能是A随机产生的数字,比如123456,作为A的标识,此时A处于SYN_SENT状态;

②服务器主机(下称为B),接收到报文1之后立即回应报文2,里面的信息是SYN_2,Seq_2和ACK_2,其中SYN_2还是1,Seq_2是B的标识,假设为998,ACK_2是Seq_1的值加1,比如是123457,此时B处于SYN_RECV状态;

③A收到报文2之后开始核对ACK_2,发现是Seq_1加1,证明B确实已经正确接收并返回报文了,这时候A需要发送报文3来通知B自己也已经准备就绪,报文3中的数据主要是Seq_3和ACK_3,Seq_3为报文1中的Seq_1加1,同样用来标识本机,同时ACK_3设为Seq_2(即B的Seq)加1,表示已经收到B的回应,因为SYN是请求连接的标志位,所以也就不必再设为1,否则会让B认为是A另外请求的连接,之后A处于ESTABLISHED状态;

④主机B将核对Seq_1和Seq_3,如果((Seq_3==Seq_1+1)&&(ACK_3==Seq_2+1))==true ,则证明这个报文确实是A对之前连接的回应,此时B处于ESTABLISHED状态,至此三次握手完毕。

理解了三次握手,那么四次挥手也就非常简单了,或许又有人要吐槽了,现实生活中两个人要再见,你挥一下手,他挥一下手就已经可以了,为什么会变成四次,事实上,服务器在建立连接和断开连接的时候都要得到对方的确认才可以,三次握手就是这样,那么四次挥手也应该是这样,其实你和朋友的告别也是这样,看起来是两步,但是却执行了四步:

①你挥一挥手,不带走一片云彩(请求断开连接)

②你确认朋友已经看到了你挥手的动作(确认对方已经收到你的信息)

③朋友也向你挥手(对方请求断开连接)

④朋友确认你已经看到了他挥手的动作(对方确认你已经收到对方的信息),通信结束

所以这里有一步被大家忽略掉了,就是“确认对方已经接收到自己发出的请求”,但是在计算机网络中通信,A主机是无法判断B是否已经接收自己的“挥手动作”的,因此当A向B发送请求断开连接的报文后,还需要B传回来一个报文确认自己已经收到,这样A才能放心的断开连接(不然让B一个人傻傻等着多不好),但这只是从A的角度看待问题,A想要断开连接是因为A已经没有需要发给B的数据了,但这并不能代表B的想法,因此只有B也做了和A一样的操作,通信才能被终结。

那么大体的流程就是这样:

A请求断开通信àB返回确认报文àB请求断开通信àA返回确认报文à谈话结束

老样子,原理明白了我们就来看一下实践,先上图(渣渣图片,将就着看吧)。

因为要断开连接,所以不需要SYN了,除此之外再加一个标识FIN(finish),用来表示请求断开连接。

相对握手来说,挥手会显得很简单了,下面是具体步骤:

①客户(A)发送报文1,将FIN_1标志位设为1并设置Seq_1,表示主机,此时A进入FIN_WAIT_1状态;

②服务器(B)接收报文1,根据Seq_1找到指定TCP连接,而后发送报文2,ACK_2为Seq_1+1,A接收以后变为FIN_WAIT_2状态;

③B根据Seq_1找到TCP连接,将该条连接上自己的Seq作为Seq_3,并把FIN_3设置为1,发送报文3,之后B关闭连接;

④A接收报文3,把ACK_4设置为Seq_3+1,发送报文4,A进入TIME_WAIT状态;

⑤A等不到B的回复信息,认为B已经断开连接,之后A也断开连接,四次挥手过程结束。

需要注意的是,四次报文传输之后,A(先发起断开请求的主机)没有立即断开连接,而是先等待一段时间,确认没有收到信息之后再断开的。

还有一点就是,这张图上没有标明Seq的存在,网上关于挥手时有没有Seq的问题也有不同的版本,我个人的观点是应该有的,因为Seq唯一的表示了一条TCP连接,一台主机上的TCP连接是很多的,而且两台主机之间也会有很多条TCP连接,比如HTTP1.1就是利用多条TCP连接来提升服务器响应速率的,因此我认为这一步应该是有Seq的,我用Wireshark抓包验证,果不其然,Seq是有的,(而且我发现Seq其实在每条TCP连接里都有,真土鳖)。

看图说话

不不不,不是这个

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180408G1NAU900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券