什么是三向握手?所谓三次握手,其实就是指三次信息交换过程。三方信息交换后,我们可以认为“连接”已经建立。那么什么是“联系”呢?连接唯一确定发送方和接收方。此外,一个连线也决定了双方的“说话方式”。三向握手规定双方在说话前必须加一个数字,这个数字用来记录这是对方。前几句,如:A:1,今天天气好1,是的:B2,明天天气好:BA:2,但明天好像要下雨了A:3,我还要上班明天加班A:4,不想上班:(3,明天放假:B6,下班了:B4,今天可以早退:B5,开心:B由此可见,双方都反映我是第一次说几句话,当然,在现实情况下,只有极其无聊的人才会在微信中这样聊天。但微信所依赖的网络通信协议实际上以这种看似神经质的方式互相交流,可能同学们已经看出这种方式的优势在于可靠性,从上面的聊天中我们可以看出,B的最后几个字可能是乱序的网络问题,但是A还是知道怎么读B发来的信息的原因是每句话都有一个编号,A可以按照编号的顺序读取,而不是按照收到数据的顺序读取。这是给每个句子加一个数字的功能。真实网络协议的第一句单词不是从1开始的,三次握手的目的是协商双方的初始数。比如A和B说:“我的留言号从X开始”,B对A说:“我的留言号从Y开始”,然后双方在X和B的基础上,每句话加1Y保证通信的可靠性。那么什么网络协议需要依靠三次握手来交换话音号码来保证可靠性呢?答案是网络通信对于协议中的TCP,只有TCP协议在相互通信前需要进行三次握手,交换起始序号。那么UDP协议是不是需要双方进行三次握手才能通信呢?当然没有必要。UDP协议不负责通信的可靠性。UDP通信双方无需三次握手即可直接发送数据。本文重点介绍TCP协议。下面的内容都是关于TCP协议的,需要注意的地方。通信中加入序号的重要性TCP规定接收方需要对接收到的数据进行确认,即发送ACK。发送方收到ACK后,就知道接收方确实收到了信息。如果未收到ACK消息,发送方将重传该消息。这就是TCP协议中所谓的超时重传机制。那么这里就有问题了。发件人如何知道要重传哪条消息?别忘了双方使用TCP进行通信的每一句话都有一个序号,接收方回复的ACK也有一个序号。比如接收方发送的ACK报文是:ACK15这句话的意思其实是:序列号14之前的所有报文我都收到了,可以发送序列号15之后的数据了。当发送方收到ACK后,就会知道sequencenumber15之前的所有信息都已经收到了,所以通过在ACK中携带sequencenumber,接收方可以准确的知道哪些数据没有发送成功。这就是在通信中添加序列号的重要性。一句话,就是保证TCP协议的可靠性。可以说序列号是TCP可靠性的基石。三次握手的过程现在我们知道序号在TCP协议中的重要性了。三向握手本质上就是互相交换起始序号进行通话。没有序列号,TCP的可靠性无从谈起。三向握手其实类似下面的过程:A:我的发言序号是从X那里收到的(别忘了每句话都需要在TCP协议中确认):B我的发言序号从Y:BA:Received开始(别忘了每句话都需要在TCP协议中确认)也就是:但是,我们可以看到B说的两句话开头是可以合为一句话的,所以:A:我发言的序号是从X开始的,我发言的序号是从Y开始的:BA:收到:这就是三次握手的由来,你现在应该明白了吧。当然,这个不是教科书上写的,是教科书上写的:总之,用你看不懂的方式说是对的:),开个玩笑,本文剩下的部分也会用到上面的以图为例进行说明,前两张图的目的是为了让大家更好的理解三次握手。总之,交换双方发言的起始序号就是三次握手的目的。这个序号极其重要,是TCP协议可靠性的基础。在三次握手之后,TCP双方就可以交换数据了。SocketAPI在讲解socketAPI之前,我们需要明白TCP协议的两端分为主动开放和被动开放。从三次握手的角度来看,主动发起握手的一方属于主动开口;被动接受握手的一方属于被动开口。很明显,客户端是主动开启的,服务端是被动开启的。接下来我们可以看看socketAPI。有几个API在socket编程中非常重要,但是很多资料解释的并不尽如人意。其实这些API分为两类,一类是客户端和服务端都可以调用的;一个属于客户端或服务端:客户端和服务端都可以调用的API:socket(),bind(),send/write(),write()/recv(),close()都是API独属于客户端和服务端的:client:connect()server:listen(),accept()这里我们比较感兴趣的是第二类API,为什么connect函数只能被客户端调用,以及listen和accept函数只能由服务器调用?要理解这个问题我们必须清楚的知道这些API和三次握手之间的联系。三向握手和SocketAPI我们先来看看client和server这两个概念。实际上,当双方经过三次握手后正常通信时,是不需要区分服务端和客户端的。服务器可以向客户端发送数据,客户端也可以向服务器发送数据。在这个阶段,客户端和服务器之间没有区别。区分服务器端和客户端的唯一方法实际上是通过三次握手的过程来实现的。如何区分?很简单,发起连接的一方是客户端,被动打开的一方是服务端,而专属于客户端或服务端的几个API与三次握手密切相关。其实connect、listen和accept函数与三次握手的关系如下:从图中我们可以看出三次握手实际上是由客户端通过connect函数发起的,客户端会调用连接函数时不会立即返回。只有当三次握手成功时connect函数才会返回,直到完成。对于服务器server来说,调用listen只是服务器告诉操作系统它已经准备好被动打开,也就是被动接受握手。当服务端还没有执行listen函数时,客户端调用connect函数是不会成功返回的。很简单,connect函数的作用其实就是发起三次握手,但是此时服务器还没有准备好,所以三次握手不会成功,connect函数也不会返回成功。只有当服务器调用监听函数时,服务器才会准备好进行三次握手。请注意,这与调用accept函数无关。只要在server端调用listen函数,即使不调用accept,三次握手也能成功。在三次握手之后,服务器和客户端就成功建立了链接(准确的说是成功交换了彼此的起始序号),服务器和对应客户端的连接信息会放在操作系统的等待队列等。放在队列中?因为一个服务器可以与多个客户端建立连接,所以在三次握手成功后需要维护这些客户端的连接信息,所以这些信息通常由操作系统使用队列来维护。那么什么时候取出队列中的连接数据呢?这就是accept函数的作用。服务端调用accept函数后,会从队列中取出一个3次握手成功的连接数据,然后双方就可以正常通信了。从这里我们也可以看出accept函数不会影响三次握手,但是函数能否快速返回就关系到三次握手了。当服务器调用listen为三次握手做准备时,假定还没有客户端与服务器通信。通信,此时调用accept函数时服务器不会返回,原因很简单,因为此时队列中没有建立成功的连接,情况如上图,当第一个client与server通信成功三次握手后,队列中才会有连接信息。这时候accept函数要等数据从队列中取出才会返回。综合以上分析,connect、listen、accept与三次握手密切相关。connect函数用于发起三次握手,因此只能由客户端使用。listen用于准备接受握手,accept函数用于获取三次握手成功的连接信息,所以这两个函数只能由服务端使用。综上所述,在这篇文章中,我们讲解了什么是三次握手以及三次握手与socketAPI的联系。请注意,只有TCP协议需要三次握手。希望本文的讲解能够加深同学们对TCP协议的理解。如果您喜欢本文,请关注微信公众号:码农的荒岛求生获取更多内容。计算机内功决定程序员的职业高度
