本篇结构:
- 前言
- TCP报文首部格式
- 三次握手
- 四次挥手
- wireshark抓包三次握手
- wireshark抓包四次挥手
一、前言
比较郁闷,明明年纪不大,记性却出奇显出老态,不久前学习过的知识,再回头看发现基本已经忘却。
可能也是学得不扎实,不深入的原因吧。
抛开这些无关的念头,少年,再来深入理解一下吧,要知道这个世上大多数的事,都是一个重复的过程。
二、TCP报文首部格式
先来复习下TCP报文首部首部的格式。
- 源端口和目的端口(Source Port和Destination Port):分别占用2个字节,用于区别主机中的不同进程,而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一的确定一个TCP连接。
- 序号seq(Sequence Number):占4个字节,用来标识从TCP发端向TCP收端发送的数据字节流(tcp传输的每一个字节都按顺序编号),它表示在这个报文段中的的第一个数据字节在数据流中的序号。主要用来解决网络报乱序的问题。
- 确认号ack(Acknowledgment Number):占4个字节,确认序列号包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志为1时该确认序列号的字段才有效。主要用来解决不丢包的问题(确认序号减去上次收到的序号等于本段收到的报文的长度)。
- 数据偏移(Offset):占4个bit,指出tcp报文段的数据起始处距离tcp报文段的起始有多远,这个字段实际指出Ttcp报文段的首部长度。需要这个值是因为任选字段的长度是可变的,它用来表示首部中32bit(4字节)字的数目,因此最多能表示15个32bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段,正常的长度是20字节。
- 保留:占6bit。
TCP Flags:TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次为URG,ACK,PSH,RST,SYN,FIN。每个标志位的意思如下:- 紧急URG:当URG = 1时,表示TCP包的紧急指针域有效,告诉系统这个报文段中有紧急数据,应当尽快传输。
- 确认ACK:TCP应答号将会包含在TCP数据包中,有两个取值:0和1,为1的时候表示应答域有效,反之为0。
- 推送PSH:这个标志位表示Push操作,接收方tcp收到PSH = 1的报文段,就尽快交付给接收接收应用进程而不是再等到这个缓冲区都填满之后再向上交付。
- 复位RST:表示连接复位请求,当RST = 1,标明tcp有严重的错误,必须释放连接,重新建立运输连接。RST = 1还可以用来拒绝一个非法的报文段或者拒绝打开一个连接。
- 同步SYN:表示同步序号,用来建立连接,SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有SYN的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个连接严格的进行TCP的三次握手。
- 终止FIN: 表示发送端已经达到数据末尾,将释放一个连接,FIN = 1, 表示报文段的发送方的数据已经发送完成,请求释放连接。
- 窗口:占2个字节,存放的是数据是字节为单位的窗口值告诉对方,本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。这是让发送方设置发送窗口的依据。
- 检验和,占2个字节。
要理解三次握手,四次挥手,同步SYN、确认ACK、序号seq、确认号ack、终止FIN5个概念比较重要,下面再次加深这5个概念的理解:
- 同步SYN:在连接建立时用来同步序号,当SYN=1而ACK=0时,表明这是一个连接请求报文,对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1,SYN置1就表示这是一个连接请求或连接接受报文。
- 确认ACK:TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1。
- 序号seq:标识从TCP发端向TCP收端发送的数据字节流(tcp传输的每一个字节都按顺序编号),它表示在这个报文段中的的第一个数据字节在数据流中的序号。
- 确认号ack:确认序列号包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志为1时该确认序列号的字段才有效。
- 终止FIN:表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的TCP数据包后,连接将被断开。
三、三次握手
三次握手的文章很多,相关的图片也很多,但自己亲自动手写和画可以加深理解。
这里贴上我画的TCP三次握手的图:
TCP协议中,主动发起请求的一端称为『客户端』,被动连接的一端称为『服务端』。不管是客户端还是服务端,TCP连接建立完后都能发送和接收数据。
刚开始的时候,服务器和客户端都为CLOSED状态。在通信开始前,双方都得创建各自的传输控制块(TCB)。
服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。
1.第一次握手
客户端向服务端发送连接请求报文段。该报文段的头部中同步SYN=1,确认ACK=0,同时选择一个初始序号seq=x。请求发送后,客户端便进入SYN-SENT状态。
- SYN=1,ACK=0表示该报文段为连接请求报文
- x为本次TCP通信的字节流的初始序号
- TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号
2.第二次握手
服务端收到连接请求报文段后,如果同意连接,会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。发送完应答后服务端进入SYN-RCVD状态。
- SYN=1,ACK=1表示该报文段为连接同意的应答报文
- seq=y表示服务端作为发送者时,发送字节流中的第一个字节序号
- ack=x+1表示服务端希望客户端发送的下一个数据报初始序号是从x+1开始
3.第三次握手
客户端收到服务端连接同意的应答后,还会向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。该报文段的头部为:ACK=1,seq=x+1,ack=y+1。
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
3.1、TCP建立连接为什么是三次握手,而不是两次或四次?
首先应该清楚一点:不论握手多少次都不能确认一条信道一定是“可靠”的,但通过3次握手可以至少确认它是“可用”的,再往上加握手次数不过是提高它是“可用”的这个结论的可信度。
也就是说任意次的握手都是“不可靠”的,握手成功只能说明握手时的通信是正常的,并不能保证握手后的通信是正常的。握手只能保证尽可能的可靠,而不可能保证绝对可靠。
1.那为什么不是两次握手?
是为了防止已失效的连接请求报文段突然又传送到了服务端,造成服务端资源的浪费。
这句话怎样理解?
在一次TCP连接中,客户端A向服务端B发送连接请求SYN报文段,假如这个报文段没有及时被服务端B接收,而是滞留在网络的某处,于是客户端A超时重传,再次发送请求连接并且顺利与服务端B建立了连接,交换数据后断开连接。
滞留在网络中的某处的陈旧报文就变成了失效的连接请求报文。
但如果这个失效的请求SYN报文段,现在又突然传送到了服务端B处,设想这时是使用两次握手而不是三次握手,服务端B就以为客户端A现在建立请求连接,于是服务端B发出确认,新的连接就建立了,服务端B分配资源,等待客户端A传送数据,但客户端A并没有想要建立TCP连接,不会理会服务端B发送的应答,也不会向服务端B传送数据,于是服务端B就白白等待,空耗资源。
使用三次握手可以避免这个情况。服务端B收到客户端A的失效的陈旧SYN报文段,向客户端A发送SYN报文段,选择自己的序号seq=y,确认收到客户端A的SYN报文段,确认号ack=x+1。第三次握手客户端A收到B的SYN报文段后,从确认号就可得知不应理睬这个SYN报文段(因为A现在并没有发送seq=x的报文段)。
这时,客户端A会发送复位报文段,这个复位报文段中,RST=1,ACK=1,确认号ack=y+1。
服务端B收到A的复位报文,就知道不建立TCP连接,不会分配资源等待A发送数据。
2.为什么不是四次握手?
既然两次握手不可以,那四次握手,五次握手呢?
因为三次握手已经能说明握手时的通信是正常的,四次握手、五次握手就显得浪费了。
四、四次挥手
同样,先上一张图:
TCP连接的释放一共需要四步,这也是四次挥手的由来。
TCP连接是双向的,在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
1.第一次挥手
客户端数据发送完成,则它向服务端发送连接释放请求。该请求只有报文头,头中携带的主要参数为:FIN=1,seq=u。此时,客户端将进入FIN-WAIT-1状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- FIN=1表示该报文段是一个连接释放请求
- seq=u,u-1是客户端向服务端发送的最后一个字节的序号
2.第二次挥手
服务器收到客户端连接释放报文,通知相应的高层应用进程,告诉它客户端向服务器这个方向的连接已经释放了。
此时服务端进入了CLOSE-WAIT(关闭等待)状态,并向客户端发出连接释放的应答,其报文头包含:ACK=1,ack=u+1,并且带上自己的序列号seq=v。
- ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答
- seq=v,v是服务端释放应答报文段第一个字节序号
- ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节
客户端收到该应答后,进入FIN-WAIT-2状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
第二次挥手完成后,客户端到服务端方向的连接已经释放,服务端不会再接收客户端的数据,客户端也没有数据要发送了。但服务端到客户端方向的连接仍然存在,服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3.第三次挥手
服务端将最后的数据发送完毕后,就向客户端发送连接释放报文,其报文头包含:FIN=1,ack=u+1,由于在CLOS-WAIT状态,服务端很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
4.第四次挥手
客户端收到服务器的连接释放报文后,向服务端发出确认应答,报文头:ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。
该状态会持续2MSL(最长报文段寿命)时间,这个期间TCP连接还未释放,若该时间段内没有服务端的重发请求的话,客户端就进入CLOSED状态,撤销TCB。
服务端只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
4.1、为什么客户端最后还要等待2MSL?
第一:为了保证服务端能收到客户端的确认应答。
若客户端发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,服务端等待超时后就会重新发送连接释放请求,但此时客户端已经关闭了,不会作出任何响应,因此服务端就无法正常关闭。
第二:防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。
客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
4.2、TCP关闭连接是为什么是四次挥手?
关闭连接时,服务器收到客户端的FIN报文时,仅仅表示客户端不再发送数据了但是还能接收数据,并且服务端也未必全部数据都发送给对方了,所以服务端可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,服务端的ACK和FIN一般都会分开发送,从而导致多了一次。
五、wireshark抓包三次握手
wireshark是个抓包工具,很不幸,我不是很会玩,所以就不多说,想要玩的朋友只能自己找资料看了。
废话不多说,直接开干。
用wireshark简单验证一下TCP的三次握手,这里我以我的博客地址(http://blog.csdn.net/w1992wishes)为例。
打开wireshark,开启捕获:
在浏览器输入http://blog.csdn.net/w1992wishes地址,然后进入wireshark的捕获页面,在过滤器中输入http,找到GET /w1992wishes HTTP 1.1那条记录。
然后右键追踪流-TCP流,找到相关的记录。
可以看到HTTP上面的三次TCP记录,就是三次握手,分别来看。
1.第一次握手
客户端发送一个TCP,标志位为SYN,序列号为0,代表客户端请求建立连接。
2.第二次握手
服务器发回连接确认包,标志位为 SYN=1,ACK=1。将确认序号ack(Acknowledgement Number)设置为客户的ISN加1,即0+1=1,同时设置服务端序列号seq=0。
3.第三次握手
客户端再次发送确认包(ACK) SYN=0,ACK=1。并且把服务器发来的seq序号字段+1放在确定ack中(即ack=1,因为已经收到了序列号为0的数据,发送确认的一端所期望收到的下一个序号是1),发送给对方,并且seq=1(因为seq=0已经发送,seq=1是客户端希望收到的下一个数据初始序号)。
六、wireshark抓包四次挥手
再来看四次挥手:
1.第一次挥手
客户端给服务端发送TCP包,用来关闭客户端到服务端的数据传送,FIN=1,ACK=1,seq=12149,ack=2574。
2.第二次挥手
服务端收到客户端的FIN报文段后,回应一个应答,ACK=1,seq=2574,ack=12150。
3.第三次挥手
服务端数据传出完成,关闭与客户端的连接,发送FIN报文段,FIN=1,ACK=1,seq=2574,ack=12150。第三次挥手的seq和第二次挥手的seq相同,说明在客户端发起TCP连接关闭请求后,服务端的数据已经传输完了,所以seq是相同的。
4.第四次挥手
客户端收到服务端的FIN报文后,回应应答,发回ACK确认,ACK=1,seq=12150,ack=2575。
参考博文:
TCP 为什么是三次握手,而不是两次或四次? - 大闲人柴毛毛的回答 - 知乎
https://www.zhihu.com/question/24853633/answer/254224088