图解TCP/IP协议

来源:互联网 发布:cpi最新数据 编辑:程序博客网 时间:2024/05/01 16:56

TCP三路握手与4路分手:

连接:主动打开端(一般为客户端)调用connect函数,connect函数处于阻塞状态,发送SYN信号给服务器,客户端进入SYN_SEND状态,服务器接收到SYN信号,服务端进入SYN_RECV状态,accept函数进入阻塞状态,同时发送SYN+ACK信号给主动打开端,主动打开端接收到此信号,connect函数返回,进入ESTABLISHED状态,说明客户端连接已经建立,同时客户端发送ACK信号给服务端,服务端接收到此信号,accept函数返回,进入ESTABLISHED状态,说明连接已经成功建立。在此过程中,若第二路信号客户端没有接收到,则connect函数无法返回,客户端无法进入ESTABLISHED状态,accept函数也就无法返回,若第三路信号服务端没有接收到,则服务端accept函数无法返回,服务端无法进入ESTABLISHED状态,进行下面的接收数据的操作。

 

断开连接:主动断开端调用close函数,close函数进入阻塞状态,发送FIN信号,进入FIN_WAIT_1状态,服务端接收到FIN信号,进入CLOSE_WAIT状态,同时发送ACK信号,然后调用close函数,进入阻塞状态,发送FIN信号,进入LAST_ACK状态,客户端接收到ACK信号,客户端close函数返回,进入FIN_WAIT_2状态,等待接受FIN信号,接收到FIN信号之后进入TIME_WAIT状态,同时也发送ACK给服务端,服务端接收到ACK之后close返回,进入CLOSED状态。

在断开连接的过程中,如果出现以下情况:

 

1、如果两端同时主动打开,发送FIN信号,两端同时接收到FIN,发送ACK,则进入CLOSEING状态,接收到ACK之后然后进入TIME_WAIT状态;

2、如果客户端先接受到FIN,在接收到ACK,则直接进如TIME_WAIT状态;

4路分手分为两个2路,每路的FIN+ACK分别结束一个close函数。

 

TIME_WAIT状态:执行主动关闭的那端,最终进入的状态,该端点在此状态的停留时间是最长生命分节期的2倍(2MSL)。

存在理由:1、可靠的实现TCP全双工连接的终止,2、每成功建立连接时,能够保证来自该连接先前化身的老的重复分节能在网络中消失。

为什么是2MSL呢?我认为服务端等待ACK的最大时间是MSL,若在MSL时间内没有收到ACK,则服务端会重发FIN信号,客户端接收到重发的FIN信号的最长时间也是MSL.

转载:如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。 
第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二 
个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT 
状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被 
丢弃。建立第二个连接的时候,不会混淆。

 

TCP为什么采用3路握手来建立连接,2路为什么不可以?

说法1、这个问题的本质是信道不可靠,但是通信双方需要就某个问题达成一致,而要解决这个问题,无论在通信中包含什么信息,3次是理论上的最小值,所以3次握手是在不可靠的信道上可靠的传输信息这一需求导致的,既客户端与服务端都需要发送一个信息给对方,然后接收对方的确认信号。

说法2、3次握手的目的是为了防止已经失效的连接请求报文段突然又传回到服务端,因而产生错误,试想一下在以下这一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络节点上长时间的停滞了,一致延误到连接释放以后的某个时间才到达server,本来这是一个早已消失了的报文段,server收到这个报文段之后误认为是client发出的连接请求,server同意建立连接,假设只有两路握手,则server发出确认数据之后连接就建立了,由于现在client没有发出建立连接请求,因此不会理睬server的确认,也不会给server发送数据,但是server一直等待client发送数据,这样server的很多资源就浪费了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

 

 

一下转载自:程序员的那些事。

通过两个图来梳理TCP-IP协议相关知识。TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道。如图1所示,给出了TCP通信过程的示意图。


图1 TCP 三次握手四次挥手


图1主要包括三部分:建立连接、传输数据、断开连接。


1)建立TCP连接很简单,通过三次握手便可建立连接。


2)建立好连接后,开始传输数据。TCP数据传输牵涉到的概念很多:超时重传、快速重传、流量控制、拥塞控制等等。


3)断开连接的过程也很简单,通过四次握手完成断开连接的过程。


三次握手建立连接:


第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;


第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;


第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。


握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。


传输数据过程:


a.超时重传


超时重传机制用来保证TCP传输的可靠性。每次发送数据包时,发送的数据报都有seq号,接收端收到数据后,会回复ack进行确认,表示某一seq号数据已经收到。发送方在发送了某个seq包后,等待一段时间,如果没有收到对应的ack回复,就会认为报文丢失,会重传这个数据包。


b.快速重传


接受数据一方发现有数据包丢掉了。就会发送ack报文告诉发送端重传丢失的报文。如果发送端连续收到标号相同的ack包,则会触发客户端的快速重传。比较超时重传和快速重传,可以发现超时重传是发送端在傻等超时,然后触发重传;而快速重传则是接收端主动告诉发送端数据没收到,然后触发发送端重传。


c.流量控制


这里主要说TCP滑动窗流量控制。TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。 滑动窗可以是提高TCP传输效率的一种机制。


d.拥塞控制


滑动窗用来做流量控制。流量控制只关注发送端和接受端自身的状况,而没有考虑整个网络的通信情况。拥塞控制,则是基于整个网络来考虑的。考虑一下这样的场景:某一时刻网络上的延时突然增加,那么,TCP对这个事做出的应对只有重传数据,但是,重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,于是,这个情况就会进入恶性循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这么行事,那么马上就会形成“网络风暴”,TCP这个协议就会拖垮整个网络。为此,TCP引入了拥塞控制策略。拥塞策略算法主要包括:慢启动,拥塞避免,拥塞发生,快速恢复。


四次握手断开连接:


第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。


第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。


第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。


第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。


图2给出了TCP通信过程中的状态转移图,理解此图是我们理解TCP-IP协议的关键。

图2 TCP状态转移图


状态图详细解读:


1.CLOSED:起始点,在超时或者连接关闭时候进入此状态。


2.LISTEN:服务端在等待连接过来时候的状态,服务端为此要调用socket,bind,listen函数,就能进入此状态。此称为应用程序被动打开(等待客户端来连接)。


3.SYN_SENT:客户端发起连接,发送SYN给服务器端。如果服务器端不能连接,则直接进入CLOSED状态。


4.SYN_RCVD:跟3对应,服务器端接受客户端的SYN请求,服务器端由LISTEN状态进入SYN_RCVD状态。同时服务器端要回应一个ACK,同时发送一个SYN给客户端;另外一种情况,客户端在发起SYN的同时接收到服务器端得SYN请求,客户端就会由SYN_SENT到SYN_RCVD状态。


5.ESTABLISHED:服务器端和客户端在完成3次握手进入状态,说明已经可以开始传输数据了。


以上是建立连接时服务器端和客户端产生的状态转移说明。相对来说比较简单明了,如果你对三次握手比较熟悉,建立连接时的状态转移还是很容易理解。


下面,我们来看看连接关闭时候的状态转移说明,关闭需要进行4次双方的交互,还包括要处理一些善后工作(TIME_WAIT状态),注意,这里主动关闭的一方或被动关闭的一方不是指特指服务器端或者客户端,是相对于谁先发起关闭请求来说的:


6.FIN_WAIT_1:主动关闭的一方,由状态5进入此状态。具体的动作是发送FIN给对方。


7.FIN_WAIT_2:主动关闭的一方,接收到对方的FIN-ACK(即fin包的回应包),进入此状态。


8.CLOSE_WAIT:接收到FIN以后,被动关闭的一方进入此状态。具体动作是接收到FIN,同时发送ACK。(之所以叫close_wait可以理解为被动关闭方此时正在等待上层应用发出关闭连接指令)


9.LAST_ACK:被动关闭的一方,发起关闭请求,由状态8进入此状态。具体动作是发送FIN给对方,同时在接收到ACK时进入CLOSED状态。


10.CLOSING:两边同时发起关闭请求时,会由FIN_WAIT_1进入此状态。具体动作是接收到FIN请求,同时响应一个ACK。


11.TIME_WAIT:最纠结的状态来了。从状态图上可以看出,有3个状态可以转化成它,我们一一来分析:


a.由FIN_WAIT_2进入此状态:在双方不同时发起FIN的情况下,主动关闭的一方在完成自身发起的关闭请求后,接收到被动关闭一方的FIN后进入的状态。


b.由CLOSING状态进入:双方同时发起关闭,都做了发起FIN的请求,同时接收到了FIN并做了ACK的情况下,由CLOSING状态进入。


c.由FIN_WAIT_1状态进入:同时接受到FIN(对方发起),ACK(本身发起的FIN回应),与b的区别在于本身发起的FIN回应的ACK先于对方的FIN请求到达,而b是FIN先到达。这种情况概率最小。


关闭的4次连接最难理解的状态是TIME_WAIT,存在TIME_WAIT的2个理由:


1.可靠地实现TCP全双工连接的终止。


2.允许老的重复分节在网络中消逝。


附:


慢热启动算法 – Slow Start


首先,我们来看一下TCP的慢热启动。慢启动的意思是,刚刚加入网络的连接,一点一点地提速,不要一上来就像那些特权车一样霸道地把路占满。新同学上高速还是要慢一点,不要把已经在高速上的秩序给搞乱了。


慢启动的算法如下(cwnd全称Congestion Window):


1)连接建好的开始先初始化cwnd = 1,表明可以传一个MSS大小的数据。


2)每当收到一个ACK,cwnd++; 呈线性上升


3)每当过了一个RTT,cwnd = cwnd*2; 呈指数让升


4)还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”(后面会说这个算法)

所以,我们可以看到,如果网速很快的话,ACK也会返回得快,RTT也会短,那么,这个慢启动就一点也不慢。


拥塞避免算法 – Congestion Avoidance


前面说过,还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”。一般来说ssthresh的值是65535,单位是字节,当cwnd达到这个值时后,算法如下:


1)收到一个ACK时,cwnd = cwnd + 1/cwnd


2)当每过一个RTT时,cwnd = cwnd + 1


这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。很明显,是一个线性上升的算法。


拥塞状态时的算法


前面我们说过,当丢包的时候,会有两种情况:


1)等到RTO超时,重传数据包。TCP认为这种情况太糟糕,反应也很强烈。


●sshthresh = cwnd /2

●cwnd 重置为 1

●进入慢启动过程


2)Fast Retransmit算法,也就是在收到3个duplicate ACK时就开启重传,而不用等到RTO超时。


●TCP Tahoe的实现和RTO超时一样。

●TCP Reno的实现是:

●cwnd = cwnd /2

●sshthresh = cwnd

●进入快速恢复算法——Fast Recovery


上面我们可以看到RTO超时后,sshthresh会变成cwnd的一半,这意味着,如果cwnd<=sshthresh时出现的丢包,那么TCP的sshthresh就会减了一半,然后等cwnd又很快地以指数级增涨爬到这个地方时,就会成慢慢的线性增涨。我们可以看到,TCP是怎么通过这种强烈地震荡快速而小心得找到网站流量的平衡点的。


快速恢复算法 – Fast Recovery


TCP Reno


这个算法定义在RFC5681。快速重传和快速恢复算法一般同时使用。快速恢复算法是认为,你还有3个Duplicated Acks说明网络也不那么糟糕,所以没有必要像RTO超时那么强烈。 注意,正如前面所说,进入Fast Recovery之前,cwnd 和 sshthresh已被更新:


●cwnd = cwnd /2

●sshthresh = cwnd


然后,真正的Fast Recovery算法如下:


●cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)

●重传Duplicated ACKs指定的数据包


●如果再收到 duplicated Acks,那么cwnd = cwnd +1

●如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。


如果你仔细思考一下上面的这个算法,你就会知道,上面这个算法也有问题,那就是——它依赖于3个重复的Acks。注意,3个重复的Acks并不代表只丢了一个数据包,很有可能是丢了好多包。但这个算法只会重传一个,而剩下的那些包只能等到RTO超时,于是,进入了恶梦模式——超时一个窗口就减半一下,多个超时会超成TCP的传输速度呈级数下降,而且也不会触发Fast Recovery算法了。


TCP New Reno


于是,1995年,TCP New Reno(参见 RFC 6582 )算法提出来,主要就是在没有SACK的支持下改进Fast Recovery算法的——


●当sender这边收到了3个Duplicated Acks,进入Fast Retransimit模式,开发重传重复Acks指示的那个包。如果只有这一个包丢了,那么,重传这个包后回来的Ack会把整个已经被sender传输出去的数据ack回来。如果没有的话,说明有多个包丢了。我们叫这个ACK为Partial ACK。

●一旦Sender这边发现了Partial ACK出现,那么,sender就可以推理出来有多个包被丢了,于是乎继续重传sliding window里未被ack的第一个包。直到再也收不到了Partial Ack,才真正结束Fast Recovery这个过程


我们可以看到,这个“Fast Recovery的变更”是一个非常激进的玩法,他同时延长了Fast Retransmit和Fast Recovery的过程。

3 0
原创粉丝点击