三次握手与Socket API

什么是三次握手?

所谓三次握手其实指的是三次信息交换过程,三次信息交换完毕后咱们就能够认为一个“链接”创建好了,那么什么是一个“链接”呢?程序员

一个链接惟一肯定了发送方和接收方,除此以外一个链接还肯定了双方“说话的方式”,三次握手规定:双方在说话前都要加一个数字,该数字用来记录这是彼此的第几句话了,好比:编程

A: 1,今每天气不错啊
                  1,对啊 :B
                  2,明每天气也不错 :B
A:  2,不过好像明天有雨
A:  3,并且明天还要加班
A:  4,不想上班 :(
                  3, 我明天休假啦 :B
                  6, 下班啦 :B
                  4, 今天能够早点撤 :B
                  5, 开心 :B

从这里咱们能够看出双方说话前都报上这是本身说的第几句话,固然在真实状况下只有无聊到极点的人才会在微信里用这种方式聊天。可是微信所依赖的网络通讯协议其实就用这种看上去略显神经质的方式在彼此通讯,有的同窗可能已经看出来了,这种方式的好处在于可靠性服务器

从上面的聊天中咱们可看到,B最后说的几句话可能由于网络问题出现了乱序,但A依然知道该怎么阅读B发过来的信息,缘由就在于每一句都带有编号,A能够依照编号的顺序而不是接收数据的顺序进行阅读,这就是每句话加编号的做用。微信

真实的网络协议双方的第一句话都不是从1开始的,三次握手的目的就在于协商双方最开始的数字是几,好比A和B说:"个人信息序号从X开始",B对A说:"个人信息序号从Y开始",此后双方在X和Y的基础上每说一句就加1,以此来确保通讯的可靠性。网络

那么什么网络协议须要依赖三次握手交换说话序号来确保可靠性呢,答案就是网络通讯协议中的TCP,只有TCP协议在通讯前才须要进行三次握手彼此交换起始序号。socket

那么UDP协议在双方通讯前须要三次握手吗?固然是不须要的,UDP协议不负责通讯的可靠性,依赖UDP的通讯双方无需三次握手就能够直接发送数据。函数

本文的关注重点在TCP协议,如下内容都是关于TCP协议的,这一点要注意。spa

为通讯加上序号的重要性

TCP规定接收方须要对接收到的数据进行回复确认,也就是发送ACK,发送方接收到ACK后就知道接收方确实已经收到信息了,若是在一段时间后尚未接收到ACK信息,那么发送方就要重传消息,这就是TCP协议中所谓的超时重传机制,那么这里有一个问题,发送怎么知道该重传哪一个消息?操作系统

不要忘了使用TCP进行通讯的双方每句话都带有序号,接收方回复的ACK中一样带有序号,好比接收方发送的ACK消息为:code

ACK 15

这句话的意思实际上是在说:

序号14以前的消息我都已收到,能够发送序号15以后的数据了

当发送方接收到该ACK后就知道序号15以前的全部信息接收到都已收到,这样经过在ACK中携带上序号接收方能够准确的知道哪些数据没有发送成功。

这就是为通讯加上序号的重要性,一句话,就是为了确保TCP协议的可靠性。

能够说序号是TCP实现可靠性的基石

三次握手的过程

如今咱们已经知道了序号在TCP协议中的重要性,三次握手本质上就是交换彼此说话的起始序号,没有该序号TCP的可靠性无从谈起。三次握手实际上是相似以下过程:

A: 我说话的序号是从X开始的
     收到(不要忘了TCP协议中须要对每句话进行确认) :B
     我说话的序号是从Y开始的 :B
A: 收到(不要忘了TCP协议中须要对每句话进行确认)

即:
图片描述

可是,咱们能够看到B说的两句话起始彻底能够合并成一句,所以:

A: 我说话的序号是从X开始的
     收到,我说话的序号是从Y开始的 :B
A: 收到

即:
图片描述

这就是三次握手的由来,如今你应该明白了吧。

固然教科书上不是这样写的,教科书上是这样写的:
图片描述

总之是以你看不懂的方式来讲就对了 :) ,开玩笑哈,本文接下来的部分也以上图为例来说解,前两张图的目的是为了让你们更好的理解三次握手。

总之,交换双方说话的起始序号就是三次握手的目的,该序号极其重要,是TCP协议实现可靠性的基础。

三次握手以后TCP双方就能够交换数据了。

Socket API

在讲解socket API前咱们须要理解TCP协议的双方分为主动打开被动打开,从三次握手的角度讲,主动发起握手的一方属于主动打开;被动接受握手的一方属于被动打开。

很显然,客户端属于主动打开,服务器端属于被动打开。

接下来咱们就能够看socket API了。

在socket编程中有几个API很是重要,但不少资料对其解释差强人意。

实际上这些API分为两类,一类客户端和服务器端均可以调用;另外一类API独属于客户端或者服务器端:

客户端和服务器端均可以调用的API:

socket(), bind(), send/write(),write()/recv(),close()

独属于客户端和服务器端的API:

客户端:connect()
服务器端:listen(),accept()

在这里咱们比较感兴趣的是第二类API,为何connect函数只能被客户端调用、listen与accept函数只能被服务器端调用

要想理解这个问题咱们必须清楚的知道这些API与三次握手之间的联系。

三次握手与Socket API

咱们再来看一下客户端和服务器这两个概念,实际上当双方三次握手后正常通讯时无需区分服务器端和客户端,服务器端能够向客户端发送数据,客户端也能够向服务器端发送数据,在这个阶段客户端也好服务器也罢没什么区别,惟一能区分服务器仍是客户端实际上是经过三次握手这个过程来实现的

怎么区分呢?很简单,主动发起链接的一方是客户端,被动打开的一方是服务器端,而独属于客户端或者服务器端的几个API与三次握手密切相关。

实际上connect、listen以及accept函数与三次握手的关系以下:

图片描述

从图中咱们能够看到,三次握手实际上是客户端经过connect函数发起的,客户端调用connect函数不会当即返回,只有当三次握手成功完成后connect函数才会返回。

对于服务器server来讲,调用listen仅仅是服务器告诉操做系统已经准备好了被动打开,也就是被动接受握手,当服务器端尚未执行listen函数时,客户端调用connect函数是不会成功返回的,缘由很简单,connect函数的功能其实是发起三次握手,但此时服务器端尚未准备好,所以三次握手不会成功,connect函数也不会成功返回。

只有当服务器端调用listen函数后,服务器端才会作好准备来进行三次握手,注意这和调没调用accept函数没有任何关系。只要服务器端调用了listen函数,即便没有调用accept三次握手也能够成功。

三次握手后服务器端和客户端成功创建起连接(准确讲是成功交换了彼此说话的起始序号),服务器和相应客户端的链接信息会被放到操做系统的等待队列中,等等,为何要放入队列中呢?由于一个服务器能够和多个客户端创建链接,三次握手成功后须要维护这些客户端的链接信息,所以这些信息一般是操做系统用队列来维护的。

那么队列中的这些链接数据何时会被取出来呢?这就是accept函数的做用了,服务器端调用accept函数后会从队列中取出一个已经成功三次握手的链接数据,此后双方就能够进行正常通讯了。

从这里咱们也能够看出accept函数不会影响三次握手,但该函数可否很快返回是和三次握手有关的,当服务器端调用listen准备进行三次握手后假设尚未任何客户端同服务器端进行通讯,这时服务器端调用accept函数是不会返回的,缘由很简单,由于此时队列中尚未任何成功创建的链接,该情形就是上图所示,当第一个客户端同服务器端成功三次握手后队列中才会有链接信息,此时accept函数从队列取出该数据后才会返回。

基于以上分析,connect、listen以及accept同三次握手有密切关系。

connect函数用于发起三次握手所以只能被客户端使用。

listen用于准备接受握手,accept函数用于取出成功进行三次握手的链接信息,所以这两个函数只能被服务器端使用。

总结

本文中咱们讲解了什么是三次握手以及三次握手与socket API之间的联系,注意只有TCP协议才须要三次握手,但愿本文的讲解能加深同窗们对TCP协议的理解。

若是你喜欢这篇文章,欢迎关注微信公共帐号:码农的荒岛求生,获取更多内容。

clipboard.png

计算机内功决定程序员职业生涯高度
相关文章
相关标签/搜索