TCP的十一种状态与三次握手分析

2022-05-09 14:17:05 浏览数 (1)

我们知道TCP是面向连接的,我们只知道有连接断开,其实内部还有一些比较复杂的状态。去了解各个状态之间的切换有助于我们更加深入的了解TCP。下面我们就来分析各个状态。

1、如下图示(图源百度)图中显示出了10种状态。

我们假定断开时是client主动断开的。 对于server来说状态有:closed -> listen -> syn_recv -> enstablished -> close_wait -> last_ack -> closed 对于client来说状态有:closed -> syn_send -> enstablished -> fin_wait1 -> fin_wait2 -> time_wait 2.结合三次握手进行分析状态 我们知道三次握手的这样:  client 发送 syn x到server server 回复syn y和ack x 1 client再回复 ack y 1 下面分析上面三步与状态的关系: 1)首先,刚创建的socket都是closed状态,server调用listen之后进入listen状态 2)接着、客户端调用connnect。(TCP协议会完成三次握手,client发送第一个syn之后就进入syn_send状态。与此同时,server收到syn并回复syn和ack,server进入syn_recv状态) 3)之后、client和server都进入了enstablisted状态。之后就可以互相发送数据了。

关于TCP协议头中的确认号ack的理解可以参考:http://www.cnblogs.com/xcywt/p/8075623.html

关于Wireshark的理解可以参考:http://www.cnblogs.com/xcywt/p/8025113.html

结合Wireshark抓包分析三次握手:

 这里设置的过滤器,先清空当前捕获的包,在浏览器打开博客园。

假设TCPa -> TCPb

第一次:如下图,发送一个请求 syn,序号为0

第二次:B回复,发送syn,ack为0 1

第三次,A回复,ack = y 1。其中y为之前B发送过来的序号,这里过来的序号的0,所以ack = 1;

3.结合断开时四次握手进行分析状态

如图(图源百度,侵删)

我们知道四次握手是这样的(假定是client先close 的): client调用close。TCP协议会发送FIN x给server server收到FIN x之后,会回复ack x 1 接着、server调用close,给客户端发送FIN y 最后,客户端回复ack y 1 分析与状态的关系: 1)client调用close,发送了FINx。client进入fin_wait1状态。server收到并回复ack,server进入close_wait状态。然后client会收到ack,进入fin_wait2状态 2)server接着调用close,给client发送了fin,server则进入了了last_ack状态。 3)client收到FIN 之后,回复ack。client进入time_wait状态。server收到ack之后,进入closed状态。 (client在保持了2个MSL之后就进入closed状态) 4.注意事项 1)client进入time_wait状态之后,会保持在这个状态2MSL。目的是为了确保发送过去的ack可以被收到(因为后面已经没有数据可以发送了)。 2)连接过程是状态的改变,促使状态的改变是用户的调用。所以切换状态不一定是用户的调用。(比如,server进入close_wait状态,纯粹是TCP协议做好的,用户并没有调用什么接口) 3)关于退出时的分析,存在一个主动一个被动关系。上面分析的client主动,则client会出现fin_wait1、fin_wait2、time_wait状态。server会出现close_wait、last_ack状态。 如果是server主动断开的,则关系刚刚反过来了。server先进入fin_wait1状态,然后是fin_wait2状态。后面整个就反过来了。 5.关于closing状态的出现(这里就是第十一种状态) 通过上面的分析我们知道,client主动退出时,先给server发送了一个FIN。接着会收到一个ack确认这个FIN。 如果没收到ack,而是收到server发来的FIN y。那么这时候则进入closing状态。 这种情况是怎么出现的呢:那就是双方几乎同时closer一个socket。这是双方都正在关闭socket连接。这种情况出现的几率很小 6.为什么连接需要三次握手,断开需要四次握手。 首先我们知道,TCP协议是去全双工的。可以在发送的同时进行接收数据。 假定是主机A和主机B进行通信,断开时是A主动断开的。 1)三次握手:第一次握手表明A可以发数据给B。但是无法保证B发给A的数据可以被收到。所以B也需要发送SYN给A,A对它进行回应,才保证了B也可以发数据给A。 个人理解可以把三步拆分为四步理解: a)主机A给B发送SYN b)主机B回复ack --- 这时表明A可以发数据给B c)主机B发送SYN给A d)主机A回复ack --- 这时表明B也可以发送数据给A 只不过协议中,把中间两步放在一步进行了。 2)四次握手,就像下面这样理解: a)主机A给B发送FIN,表示对B说“我要断开了” b)主机B回复ack进行确认,表示对A说“嗯,我知道了,你可以断开了” c)然后B发送FIN给A,表示对A说“A,我也要断开了” d)A回复ack进行确认,表示对B说:“嗯,知道了,你断开吧” 前两步对A进行断开,后两步对B进行断开。 那么为什么不能把中间两步进行合并呢,因为无法保证被断开的一方的数据已经传送完毕了。 就拿上面的例子来说,假如A断开了通知B,但是B还有数据没有发送完毕,如果立即断开(调用close发送FIN),就无法保证数据的可靠性。 如果等数据发送完毕再将fin和ack一起发过去,n那么A就会长时间处于fin_wait1状态。这样就比较不好了。 实际情况中我们可能会在server忘记关闭客户端的socket。那么client就会一直处于fin_wait2状态,越来越多的fin_wait2状态会导致系统崩溃。所以我们需要在编程中要注意在什么情况下要关闭双方的socket。

7.其他知识 什么时候会收到SIGPIPE这个信号呢? 当server关闭一个连接之后,client接着发送数据,第一次发送会收到一个RST的响应。如果接着发送则系统会发出一个SIGPIPE信号给client。 系统默认的处理是将应用程序退出。实际编程中,我们可以捕获这个信号,让应用程序不退出

 如下(伪代码):

代码语言:javascript复制
#include"signal.h"
void sig_recvpipe(int sig)
{
}

int main()
{
    ......
    signal(SIGPIPE, sig_recvpipe);
    ......
}

0 人点赞