10深刻理解TCP/IP的11种协议状态

基本网络模型

在这里插入图片描述

TCP状态转换图

在这里插入图片描述

TCP/IP的11种状态

在这里插入图片描述

第11种状态

在这里插入图片描述

1、注意几个要点:

  • 任何一端都可以主动关闭连接。
  • 在调用accept()函数之前,两端已经进入了ESTABLISHED状态。
在调用listen(int socked, int backlog)函数的时候,做了2件事情:
(1)将主动套接字转换为被动套接字。TCP状态转换图从CLOSED状态转换为LISTEN状态
(2)backlog规定了内核应该为相应的套接字排队的最大连接个数。内核为这个监听套接字维护两个队列:(两个队列之和不能超过backlog)

a:未完成连接队列:此时这个队列中的套接字处于SYN_RECV状态。

b:已完成连接队列:此时这个队列中的套接字处于ESTABLISHED状态。

	也就是说在listen()的时候,套接字有2种状态SYN_RECV 和 ESTABLISHED。
	accept()是从已完成队列中的队头中获取已经处于ESTABLISHED状态的套接字,然后accept()返回给用户进程,若队列为空,则accept()投入睡眠,直到已完成队列中有新的一项。
  • socket()->bind()->listen() ;调用listen()后把未连接的套接字变为被动套接字,只负责监听,状态由CLOSED->LISTEN状态。

2、TCP/IP协议为什么要三次握手,和四次断开?

TCP/IP是全双工、双通道协议,既可以收也可以发,是一个双向认证的
过程,双方必须确保对端确实收到了自己发送的包,这样全双工通道完全
建立起来了。 先调用close的那一端,最终socket状态推进到TIME_WAIT
状态,会等一会再close,这里使用了IO复用技术。

3、为什么主动关闭socket的那一端需要进入TIME_WAIT状态而不是直接进入CLOSED状态?

因为要给对等方一个确认,但是在公网上有可能这个确认不是那么顺利,出现了网络拥塞和异常处理,此时不把先close的一方推到CLOSED状态,是因为让确认包在异常的情况下进行几次重新发送,保证对方真正的进入CLOSED状态,然后释放该释放的资源。就是让最后一个确认包在网络不通畅的情况下真正的进入确认状态。
注意:
TCP/IP协议是双通道的,客户端关闭与服务端关闭没有任何的关系,TCP/IP是一个流协议,Server端调用read()返回0,则通知对方写断了,A端关闭了socket并不代表B端不能往socket写数据,顶多写失败了。linux内核也是有缓冲区的(发送缓冲区、接收缓冲区),缓冲区达到了低水位才会往对端发送数据。

TIME_WAIT时间是2MSL,(2倍的最大生命期时间),原因是(ACK y+1) 如果发送失败可以重发。服务端处于close状态,不等于客户端也处于close状态。

4、TCP/IP为什么会出现半连接状态FIN_WAIT_2,socket状态不往下推进呢?

因为对等方没有显示的调用close,服务器挂掉也相当于显示的调用close函数,若A端一直是FIN_WAIT_2,需要查B端为什么没有调用close函数,有可能是阻塞在某一函数上,也有可能忘记了close。

5、第11种状态CLOSING

任一对等方都可以显示的调用close(),当双方同时调用close时,这时就会出现第11种状态CLOSING。客户端 服务端同时调用close时,TCP/IP协议能检测出来,因为都需要对方的确认,此时TCP/IP协议将状态置为短暂的CLOSING。当收到对方的确认时,双方都进入到了TIME_WAIT状态,因为双方都主动的调用了close()函数,都进入到了TIME_WAIT状态,经过n时间后,双方都消失。

6、TCP/IP是一个流协议,FIN DCBA 即send(fd,“ABCD”);然后调用close(),则对端一定能收到数据么?

对方能收到,而且是保证可靠的能收到。因为tcp/ip协议是一个流协议。清空缓冲区才会调用close。数据包在缓存区里面,调用close函数,TCP/IP就会处理缓冲区,将数据推到对等方,最后有个FIN也会跑到对等方缓冲区里面,然后对等方一个一个的收报文,当收到FIN后,发现是一个流水结束符,此时检测到了对方的socket已经关闭了,此时把数据给到用户空间,至此,TCP/IP的工作已经全部做完,然后用户程序处理。
TCP/IP能保证"ABCD"给到对端,而且也能保证告诉对端socket已经关闭了。

通过程序观察TCP/IP的各种状态

演示1:多个客户端与单个服务器(不支持并发)连接,此时多个客户端仍然可以连接上服务器
在这里插入图片描述

在这里插入图片描述

为什么3个客户端都能与单服务器建立连接,但只有一个客户端能通信?
	答:因为accept函数放在了for循环的上边,这样主进程只能发包收包,不能accept
	没有机会调用accept 即 没有机会从已完成三次握手的队列中拿到连接,所以不支持
	并发,只支持单连接。正确的做法是将accept放在循环里面

代码实现请看:
https://blog.csdn.net/WUZHU2017/article/details/82784995
演示2:还是用上面的代码
(1)启动服务端,然后启动客户端,状态如下
在这里插入图片描述

(2)现在ctrl+ c掉服务端

杀死server,相当于服务器主动的close,然后对等方收到FIN后,TCP/IP协议在上层应用不知道的情况下向对方发了一个确认包

在这里插入图片描述

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章