上篇介绍了socket编程的准备知识,是否是有一种很想立刻就开始了解网络编程,甚至开始写点代码的感受,别着急,网络编程中还有一个比较重要的概念是TCP/IP,中文名称叫网络传输协议,本质上,TCP/IP是一种协议,同时也是网络编程中最重要的协议之一。TCP/IP涉及到的内容实在太多,无奈笔者才疏学浅,没法把整个TCP/IP介绍给你们,这篇文章的目的主要是基于上一篇文章的前提下,介绍TCP链接三次握手和断开链接四次挥手究竟作了什么?socket的状态有哪些?在各个API执行的过程当中,socket的状态是怎么变化的?但愿经过这篇文章,能让你们对在TCP链接创建与断开过程当中,socket的整个状态变化流程有更深刻的了解。web
几个术语
SYN : 同步序列编号,Synchronize Sequence Numbers,仅在三次握手创建TCP链接时有效。表示一个新的TCP链接请求。编程
ACK : 确认编号,Acknowledgement Number,对TCP请求的确认标志,同时提示对端系统已经成功接收全部数据。服务器
FIN : 结束标志,FINISH,用来结束一个TCP会话,但对应端口仍处于开放状态,准备接收后续数据。网络
TCP三次握手
创建一次链接会有下面的流程并发
1)服务器经过socket(初始化socket)、bind(绑定ip端口)、listen(开始监听服务)完成一次socket链接的创建,并调用accept函数准备好接收外部请求链接,这一步被称为被动打开socket
2)客户端经过socket(初始化socket)完成了链接创建,调用connect函数发起主动打开,此时客户端会发送一个SYN分节J给服务器,J的做用是告诉服务器,在接下来的数据传输过程当中,J是客户端在链接中传输数据的初始序列号函数
3)服务器收到客户端的SYN分节后,须要回复确认信号ACK,J+1,表明服务器已经收到客户端的请求,且已经确认了客户端的初始序列号,同时服务器会发送SYN分节K给客户端,K的做用是告诉客户端,在接下来的数据传输过程当中,K是服务器在链接中传输数据的初始序列号测试
4)客户端收到服务器的SYN分节后,回复确认信号(ACK)K+1。链接创建成功。动画
整个创建链接过程至少须要三个分节,所以被称为TCP三路握手。下面是TCP三次握手的流程图:网络传输协议
TCP四次握手
TCP经过三次握手创建链接,然而,断开链接须要四次握手,TCP断开链接的流程描述以下:
一、客户端应用程序调用close函数,TCP中,称首先调用close的那一端为主动关闭,主动关闭的这一端发送FIN分节,意味着已经完成发送数据了;
二、另外一端(即服务器),接收到关闭请求,也收到FIN分节,开始执行被动关闭操做,称为被动关闭的一端。这个FIN分节由TCP确认,发送一个确认分节ACK给客户端,收到的FIN同时也是做为文件结束符传递给应用,意味着应用程序在接收了FIN以后就不会再接收链接上的数据;
三、以后,接收到文件结束符的应用程序会关闭它的socket,服务端的TCP也会发送一个FIN分节;
四、客户端接收到FIN分节,并确认最后的关闭操做,发送ACK分节给服务端。
整个链接过程当中,每一端的关闭和确认关闭都各自须要一个FIN和ACK分节,整个过程一般共须要4个分节,所以也称为TCP四次握手。下面是TCP关闭链接四次握手的流程图:
实际上,网络上的传输是没有链接的,包括TCP也是同样,TCP所谓的"链接"与"断开链接",其实只是一种虚拟的叫法,只不过是在通信的双方维护一个"链接状态",让它看上去好像有链接同样,因此,了解TCP的状态变换是很是重要的,接下来介绍socket的状态以及在TCP创建链接与断开链接过程当中,socket状态的变化。
socket状态
socket共定义了11种状态:LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED、FIN-WAIT-一、FIN-WAIT-一、CLOSE_WAIT、FIN_WAIT、LAST-ACK、TIME-WAIT、CLOSING、CLOSED。
咱们假定A服务请求链接B服务。
LISTEN:开始创建链接,此时socket已经初始化成功,正在等待链接
SYN-SENT:A成功发送链接请求给B,等待对方响应
SYN-RECEIVED:B接收到A的链接请求,并回复了确认进行链接给A,此状态表示正在等待A也回复确认收到此消息
ESTABLISHED:表示链接已经成功创建;这个状态是链接阶段中进行数据传输的正常状态
FIN-WAIT-1:等待主动断开链接请求的确认,或者并发请求被拒绝的断开链接,这种状态一般持续时间很短,比较难捕捉
FIN-WAIT-2:等待B断开链接操做,这个状态一般持续时间也很短,可是若是B发生阻塞或者其余缘由没有关闭链接,那么这个状态就会持续较长时间
CLOSE-WAIT:B已经收到了A的断开链接请求,正在等待本地应用程序发送断开链接请求
CLOSING:A正在等待B的关闭链接确认信号,当A接收到本地程序断开链接的请求后,就发送断开链接请求给B,并进入此状态
LAST-ACK:B等待断开链接的确认信号
TIME-WAIT:等待一段时间,确认B接收到A的关闭链接确认信号
CLOSED:链接彻底关闭
那么在一个完整的TCP链接过程,TCP的状态是怎么转换的呢?
先来看看下面这张图,是UNIX网络编程中,TCP状态变化的经典流程图:
在server端,调用socket函数建立一个sockect,函数返回一个socket文件描述符,调用bind函数绑定ip地址和端口,以后调用listen函数,scokect变成正在监听的socket,进入LISTEN状态,并调用accept等待请求(想要测试LISTEN状态,能够创建socket=>bind=>listen,而后sleep10秒以后退出,启动server以后立刻用netstat命令能够看到)
客户端调用connect,主动打开文件描述符,请求创建链接,此时会触发TCP的三次握手,进入SYN-SENT状态
此时服务器调用了accept正在阻塞阶段,接收到客户端的链接请求后,进入SYN-RECEIVED状态,回复确认报文给客户端,等待客户端确认链接
客户端收到服务器的链接确认报文SYN后,此时TCP已经完成了三次握手,connect函数返回,确认创建链接,转成ESTABLISHED状态,并发送确认报文ACK给服务器。(当第二次握手完成,握手步骤的第二个segment被client接收到的时候,connect函数返回。)
在服务器侧,接收到客户端的确认报文,accept也收到返回,服务器也进入ESTALISHED状态。(握手步骤的第三个分节被server接收到的时候,accept函数返回,即connect返回后通过一半的RTT时间才返回。)
客户端和服务端成功创建"链接"后,就进行开始通讯,此时会调用read/write函数进行读写数据,读写数据完毕后,就准备关闭链接
假定客户端应用程序,收到了服务器的响应报文,完成了通讯过程,准备关闭应用,调用close函数发起主动关闭,此时客户端进入FIN-WAIT1状态,发送FIN分节到服务器,等待服务器确认
服务端收到断开链接请求分节FIN(M),此时read函数返回0,服务器准备执行关闭操做,再也不接收任何数据,并发送确认报文ACK(M+1)给客户端
客户端收到确认分节后,表示服务器已经接收到断开链接请求,此时不会再发送数据并等待服务器断开链接,进入FIN-WAIT2状态
服务器发送确认断开链接后,调用close函数,断开链接,close函数成功返回后,发送FIN分节给客户端,进入LASK-ACK状态,等待客户端的确认
客户端收到服务器的断开链接分节FIN(N)后,发送确认分节ACK给服务器,并进入TIME-WAIT状态,此时会等待足够的时间,大约是最长分节生命期的2倍(2MSL),确认服务器收到断开链接的确认分节,以后就会消失。 服务器收到客户端的断开链接的确认分节ACK(N+1)后,表示链接已经彻底断开了,此时进入CLOSED状态
看完上面的一大段文字可能有点枯燥,甚至有点懵,来看看这张动画图:
总结
本次主要介绍了TCP状态,以及在TCP链接创建和断开过程当中,TCP状态的变化,掌握了这个,对理解网络编程中,各个流程的状态有比较大的帮助,好比在排查服务是否启动的时候,就能够经过netstat -nlp | grep '端口号',查看服务的状态描述字符串,若是是LISTEN状态表示服务已经正常启动,若是服务处于其余状态,则能够经过服务状态来进一步排查问题。
原创文章,文笔有限,才疏学浅,文中如有不正之处,万望告知。
若是本文对你有帮助,请点个赞吧,谢谢^_^
更多精彩内容,请关注我的公众号。