TCP详解+wireshark抓包演示
简介
- TCP理论
- TCP报文格式
- 三次握手
- 三次握手图解
- 为什么要三次握手
- 四次分手
- 四次分手图解
- TCP的半关闭
- 实战抓包演示(Wireshark)
TCP理论
TCP提供了一种面向连接的、可靠的字节流服务。
面向连接:接双方在通信前需要预先建立一条连接,这犹如实际生活中的打电话。
- 应用数据分割成TCP认为最适合发送的数据块。
- 重传机制。设置定时器,等待确认包
- 对首部和数据进行校验
- TCP对收到的数据进行排序,然后交给应用层
- TCP的接收端丢弃重复的数据
- TCP还提供流量控制
TCP连接必须要经历三次握手,而释放一个TCP连接需要四次握手,这是由TCP的半关闭特性造成的。因为TCP连接时全双工的,因此,需要TCP两端要单独执行关闭。值得注意的是,主动关闭的一端在发送FIN之后,依然还能正常接收对方的数据,只是通知对方它已经没有数据需要发送了,同理,被动关闭的一端在收到FIN之后,仍然可以发送数据,直到它自身同样发出FIN之后,才停止发送数据。
TCP报文格式
TCP_format.jpg- Source Port和Destination Port:分别占用16位,表示源端口号和目的端口号;用于区别主机中的不同进程,而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一的确定一个TCP连接;
- Sequence Number:用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节在数据流中的序号;主要用来解决网络报乱序的问题;
- Acknowledgment Number:32位确认序列号包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志(下面介绍)为1时该确认序列号的字段才有效。主要用来解决不丢包的问题;
- Offset:给出首部中32 bit字的数目,需要这个值是因为任选字段的长度是可变的。这个字段占4bit(最多能表示15个32bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段,正常的长度是20字节;
- TCP Flags:TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次为
URG
,ACK
,PSH
,RST
,SYN
,FIN
。每个标志位的意思如下:- URG:此标志表示TCP包的紧急指针域(后面马上就要说到)有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据;
- ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0; TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1
- PSH:这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序,而不是在缓冲区中排队;
- RST:这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包;
- SYN:表示同步序号,用来建立连接。
SYN
标志位和ACK
标志位搭配使用,当连接请求的时候,SYN
=1,ACK
=0;连接被响应的时候,SYN
=1,ACK
=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有SYN
的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个连接严格的进行TCP的三次握手; - FIN: 表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送
FIN
标志位的TCP数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。
- Window:窗口大小
三次握手
三次握手图解
TCP_hand.jpg- 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
- 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
完成了三次握手,客户端和服务器端就可以开始传送数据。
为什么要三次握手呢?
防止已失效的连接请求报文突然又传送到了服务端。
所谓“已失效的连接请求报文”是这样产生的。
- 考虑一种正常的情况
客户端发出连接请求,但因连接请求报文丢失而为收到确认。于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传送完毕后,就释放了连接。客户端共发送了两个连接请求报文,其中一个丢失,第二个到达了B。没有“已失效的连接请求报文” - 假定出现了一种异常的情况
即客户端发出的第一个连接请求报文段并没有丢失,而是在某些网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到达了服务端。本来这事一个早已失效的报文。但服务端收到失效的请求报文后,就误以为是A又发出的一个新的连接请求。于是就向客户端发出确认报文,同意建立连接。如果不采用三次握手,则服务端发出确认,新的连接就建立了。
由于客户度并没有发出建立连接的请求,因此不会理睬服务端的去人,也不会向服务端发送数据。但服务端缺以为新的连接已经建立,并一直等待客户端发来请求。那么服务端的资源就拜拜浪费了
采用三次握手的办法可以防止上述现象的发生。在刚才的情况下,客户端不会向服务端的确认发出确认。服务端收不到确认,就知道客户度没有要求建立连接。
四次分手
TCP连接时全双工的,因此,需要TCP两端要单独执行关闭。简单来说A、B两端,A提出断开连接,则发包告诉B,让B不要发数据了。B收到后就对A做一个回应,然后再发一个包给A,告诉A不要再发数据了,B就关闭自己的发送通道,A收到B的关闭请求后,也做出回应。然后关闭自己的发送通道。
四次分手图解
TCP_bye_img.jpg当客户端没有数据要发送是就要释放客户端这边的请求,客户端会发送一个报文(没有数据),Flags设置为FIN=1。服务端收到报文后先响应一个报文,接着再向客户端请求连接释放,也是FIN。客户端收到回复后再回复一个确认信息,并进入TIME_WAIT状态,等待2MSL时间。
- 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
- 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;
- 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;
- 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。
其他情况:
服务端向客户端发送 FIN = 1 的释放连接请求,但这个报文丢失了, A没有接到不会发送确认信息, 服务端 超时会重传,这时客户端 WAIT_TIME 还能够接收到这个请求,这时再回复一个确认就行了。(客户端收到 FIN = 1 的请求后 WAIT_TIME会重新记时)
另外服务端存在一个保活状态,即如果客户端突然故障死机了,那服务端那边的连接资源什么时候能释放呢? 就是保活时间到了后,服务端会发送探测信息, 以决定是否释放连接。
注意:这里我们假设是客户端连接断开的发起方,如果是服务端,流程还是一样。
TCP的半关闭
TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力,这就是TCP的半关闭。
半关闭的产生:
客户端发送FIN,另一端发送对这个FIN的ACK报文段。 此时客户端就处于半关闭。
实战抓包演示(Wireshark)
- 这里使用我们使用的代码为linux下Socket编程(一)中的代码。
- 为了更清楚的看到连接的过程,我们还需要进行抓包,这里使用Wireshark。
操作步骤:
- 打开Wireshark,进入抓包状态
- 设置过滤规则
tcp and tcp.port==9999 and ip.src ==192.166.11.14 or ip.dst==192.166.11.14
。我这里演示的服务端ip是192.166.11.14。 - 启动服务端程序
- 启动客户端然后连接。这里我们直接使用windows的telnet服务。
telnet 192.166.11.14 9999
- 连接后一次输入'a' 'b'。发送两次数据
- ctrl+c 关闭服务端。
抓包结果
TCP2.png第一次握手:
这里的截图我们只接到传输层的协议部分。
向着服务端发送一个Syn的报文。其中Sequence Number=0
TCP_hand_1.png
第二次握手:
服务端向客户端发一个Acknowledgment和Syn的报文。Acknowledgment number=1
Sequence number =0
TCP_hand_2.png
第三次握手:
客户端发向服务端一个Acknowledgment的报文。Acknowledgment number=1
Sequence number =1
然后连接建立完成
TCP_hand_3.png
第一次发送数据
TCP_send_1.png\
第二次发送数据
TCP_send_2.png第一次分手:
TCP_bye_1.png第二次分手:
TCP_bye_2.png第三次分手
TCP_bye_3.png第四次分手
TCP_bye_4.png