TCP是面向连接的协议。运输连接是用来传送TCP报文的。TCP的运输连接有三个过程,即建立连接、数据传输和连接释放。
TCP报文首部
1.源端口和目的端口,各占两个字节,分别写入源端口和目的端口;
2.序号,占4个字节,TCP连接中传送的字节流中的每个字节 都按顺序编号,例如,一段报文的序号字段值是101,携带的数据共有100字段,则下一段报文(如果存在下一个报文)的数据序号就应该从201开始;
3.确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如B收到了A发送过来的报文,其序列号是201,而数据长度是300字节,这表明B正确收到了A发送的到序号500为止的数据。所以,B希望收到A的下一个数据序号是501,于是B在向A发送的确认报文中把确认号置为501;
4.数据偏移,占4位,它指出TCP报文的数据距离TCP报文段的起始位置有多远;
5.保留,占6位,保留今后使用,目前应该都为0;
6.紧急URG,URG=1时,证明紧急指针字段有效,告诉系统此报文段中有紧急数据;
7.确认ACK,仅当ACK=1时,确认号字段才有效,TCP规定,在连接建立后所有报文的传输都必须把ACK置为1;
8.推送PSH,当两个应用进程进行交互式通行时,一端的应用进程希望在键入一个命令后能够立即得到对方的响应,这是就置PSH=1;
9.重置(复位)RST,RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新连接;
10.同步SYN,在建立连接时用于同步信号。当SYN=1,ACK=0时表明是连接请求报文,若同意连接,则响应报文中会使SYN=1,ACK=1;
11.终止FIN,用于释放连接。当FIN=1时,表明此报文的发送方的数据已经发送完毕,并且要求释放连接;
12.窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
13.校验和,占2字节,检验首部和数据这两部分;
14.紧急指针,占2字节,指出本报文段的紧急数据的字节数;
15.选项,长度可变,定义一些其他的可选参数。
TCP三次握手
简单说:
第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
详细说:
1.首先,客户机与服务器的TCP进程都处于CLOSED(关闭)状态,当要进行TCP连接时,客户机主动打开连接,服务器被动打开连接(这是因为服务请求总是由客户机向服务器发起,因为想要请求的资源都在服务器上,所以客户机想要获取资源就必须主动向服务器发起请求,而不能是等待服务器向自己(客户机)发起请求)。
2.然后,服务器的TCP进程先创建传输控制块TCB(传输控制块TCB存储了每一个连接中的重要信息,如:TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号,等等),此时,服务器就处于LISTEN(收听)状态。同样的,客户机也会首先创建一个传输控制块TCB发送给服务器。这样,准备工作就做好了。
3.现在就可以开始真正的三次握手了。首先,客户机先向服务器发送连接请求报文段,该报文段中将首部中的同步位SYN置为1(只有当SYN置为1时,才能表明客户机想要和服务器建立连接),并且随机选择一个初始序号x,注意此时的SYN数据报中并没有携带数据,但是仍旧要消耗掉一个序号(意思就是下次客户机发送数据的时候,序号为x+1),此时客户机进入到SYN-SENT(同步已发送)状态。
4.此时,服务器收到客户机的请求时,如果同意与该客户机进行连接,则需要向客户机发送确认报文。在发送报文中需要将SYN与ACK都置为1(当ACK置为1时,表明服务器同意与客户机进行连接;同时将SYN置为1,表明服务器想要和客户机建立连接),并且随机选择一个初始序号y,确认号为x+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了x,所以下一个序号为x+1),注意此时的SYN数据报中并没有携带数据,但是也要消耗掉一个序号(同样的,也就是说服务器下次发送数据的时候,序号为y+1),此时TCP服务器进程进入到SYN-RCVD(同步收到)阶段。
5.TCP客户端收到服务器的确认后,还要再向服务器给出确认。确认报文段中ACK置为1,确认号为ack=y+1(因为之前服务器给客户机发送的序号为y,因此现在客户机向服务器发送的确认号为ack=y+1,意思是客户机渴望收到的下一个报文段的第一个数据字节为y+1)此时客户机的发送序号为x+1(这是因为刚才刚才客户机向服务器发送连接请求时消耗了序号x,因此此时的序号为x+1)注意:在进行第三次握手时,ACK报文段可以携带数据,也可以不携带数据,如果携带数据,则消耗一个序列,这样客户机下次发送报文段时的序号为x+2,如果不携带数据则不消耗序号,下次客户机发送报文段时的序号为x+1。这时TCP连接已经建立,客户机和服务器都进入到ESTABLISHED(已建立连接)状态。
其实上面的三次握手实质上就相当于是下列的对话:
-客户机:服务器,我想要和你建立连接,你同意吗?(SYN=1)
-服务器:客户机,我同意和你建立连接(ACK=1);我也想和你建立连接,你同意吗?(SYN=1)
-客户机:服务器,我同意和你建立连接。(ACK=1)
其实,在进行第二次握手时(即服务器向客户机进行应答时),可以看作时发了两次包,先回答客户机的服务请求(ACK=1,ack=x+1),然后再向客户机发出请求(SYN=1,seq=y)
TCP四次挥手
![](https://i-blog.csdnimg.cn/blog_migrate/00871e16abe1fa41a952df9c04614819.png)
与建立连接的“三次握手”类似,断开一个TCP连接则需要“四次握手”。
简单说:
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
详细说:
1.数据传输结束后,通信的双方都可以释放连接。此时,客户机和服务器都处于ESTABLISHED(已建立连接)状态。
2.假设客户机请求完资源了,想要释放连接。首先,客户机的应用进程先向服务器发出连接释放报文段,该报文段中将首部的终止控制位FIN置为1(只有当FIN置为1时,才能表明客户机想要和服务器断开连接),并且序号为u(注意:此时的u不是随机产生的,而是之前客户机传送的数据的最后一个字节的序号加1)。此时客户机进入到FIN-WAIT-1(终止等待1)状态,等待服务器的确认。
3.服务器收到连接释放报文后发出确认,在发送报文中将首部中的ACK置为1(ACK置为1,表面服务器同意与客户机释放连接),并且产生序号v(注意:此时的v不是随机产生的,而是之前服务器传送的数据的最后一个字节的序号加1),并且发出确认号为u+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了u,所以下一个序号为u+1)。此时服务器就进入CLOSE-WAIT(关闭等待)状态,客户机进入FIN-WAIT-2状态。
此时,从客户机到服务器这个方向的连接就被释放了,也就是说,客户机已经没有数据要向服务器发送了,但是如果服务器向客户机发送数据,客户机仍要接收数据。也就是说:从客户机到服务器的连接已经被释放了,但是从服务器到客户机的连接还没被释放。此时,TCP连接处于半关闭状态。
4.如果服务器向客户机也没有要发送的数据的话,那么服务器的应用进程就可以向客户机发出连接释放报文段(注意此时还是服务器向客户机发送数据),该报文段中将首部的终止控制位FIN置为1(只有当FIN置为1时,才能表明客户机想要和服务器断开连接),ACK也置为1,并且序号为w(重点注意,此时的w不一定等于v+1。如果在客户机释放了连接之后,服务器向客户机仍旧发送了一部分数据,那么此时w不等于v+1,但是如果期间没有再发送数据,那么w就等于v+1。总而言之,这个w等于服务器上一次发送的数据的最后一个字节加1),并且发送确认号为u+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了u,所以下一个序号为u+1)。此时服务器就进入了LAST-ACK(最后确认)状态。
5.客户机收到服务器的连接释放报文后,必须对此报文进行确认。在该报文段中将ACK置为1,确认号为w+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了w,所以下一个序号为w+1),产生序号为u+1(因为上一个发送的数据的序号为u)。此时服务器进入到TIME-WAIT(等待时间)状态。但是,此时TCP连接还没有被释放掉。必须经过2MSL后服务器才能进入到CLOSED状态。(注:MSL叫做最长报文段寿命,RFC建议为两分钟,也就是说,要经过四分钟才能进入到CLOSED状态)。
其实上面的四次挥手实质上就相当于是下列的对话:
-客户机:服务器,我想和你断开连接,你同意吗?(FIN=1)
-服务器:我同意(ACK=1)
(在此期间,服务器可能还会向客户机发送数据,但是客户机却不能再向服务器发送数据)
-服务器:客户机,我想要和你断开连接,你同意吗?(FIN=1)
-客户机:我同意。(ACK=1)
再等待2MSL时间后就真正断开了连接。
常见面试问题:
问:三次握手中,为什么客户机最后还要再向服务器发送一次确认呢?
答:这是为了防止已失效的连接请求报文段突然又传到了服务器。所谓“已失效的连接请求报文段”是这样产生的。考虑一种正常的情况,客户机发出连接请求,但因为连接请求报文丢失而未收到确认。于是客户机再重传了一次连接请求,后来收到了确认,建立了连接。数据传输完后,就释放了连接。客户机共发送了两个连接请求报文段,其中第一个丢失,第二个到达了服务器,没有所谓的“已失效的连接请求报文段”。
但是如果出现了一种异常情况,即客户机发出的第一个报文段并没有丢失,而是在某个节点上长时间滞留了,直至客户机向服务器发送了第二个报文段并且已经完成数据传输释放了连接,此时,第一个报文到达服务器后会被误以为是客户机重新发起的一次连接请求,实质上是一个早已失效的连接请求。如果没有第三次握手,那么这个连接就建立了,但是客户机并不会向服务器发送任何请求,这样连接就会一直持续,白白的消耗网络资源。
问:如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
问:为什么客户机发送完最后一个数据后要在TIME-WAIT状态等待 2MSL(四分钟)的时间呢?
答:第一:为了保证客户机最后发送的那个ACK报文段能够到达服务器。这个ACK报文段可能会丢失。因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。服务器会超时重传这个FIN+ACK报文段,而客户机就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着客户机重传一次确认,重新启动2MSL计时器,最后客户机和服务器都可以进入到CLOSED(关闭)状态。如果没有2MSL等待时间,那么就无法收到重传的FIN+ ACK包,无法进入正常的CLOSED状态。
第二,防止“已失效的连接请求报文段”出现在本连接中。客户机在发送完最后一个ACK报文段,再经过时间2MSL,就可以使本连接持续的时间内所产生的报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。