TCP数据段解析
报文在TCP连接中传输,会被分割成多个TCP段,使用IP分组承载,从一个IP地址传输到另外一个IP地址。
IP分组主要组成:
- IP首部
- 主要包含了源IP地址和目标IP地址
- IP数据部分
- TCP首部
- TCP数据部分
这要主要对TCP首部说明一下
如下图所示:
image.png- 序号seq: sequence number 占用3个字节,TCP连接中传送的字节流中每个自己都按照顺序编号,使用seq表示将要传送数据的第一个字节的序号。
- 比如一段报文携带100字节,第一个字节的序号为301,那么下一个TCP段中的序列号级应该从401开始。
- 确认号:acknowledge number,占用4个字节,是期望收到对方下一个报文的第一数据字段的序号。
- 比如A给B发送报文,B接收到的报文其序列号为101,数据长度为200字节,则表明B收到了从101-300序号的字节,那么B给A发送ack确认号的时候,就需要将ack置为301。
- 数据偏移: 数据偏移,占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远
- 保留:占6位,保留今后使用,但目前应都位0;
- URG:紧急指针是否有效。为1,表示某一位需要被优先处理。
- ACK:仅当ACK=1时,确认号才有效。TCP规定在建立连接后,所有报文的传输都必须把ACK置为1。
- PSH:提示接收端应用程序立即从TCP缓冲区吧数据读走。
- RST:对方要求重新建立连接,复位。
- SYN:请求建立连接,并在其序列号字段进行序列号的初始值是设定(seq).
- 当SYN=1,ACK=0是表明是连接请求报文。若另一端同意,则在相应报文中应该使用SYN=1,ACK=1。
- FIN:用来释放连接。当FIN=1时,表明此报文的发送方数据已经发送完毕,并且要求释放TCP连接。
其中关于TCP连接的建立和断开,主要需要理解SYN,ACK,FIN,seq,ack
TCP建立连接的三次握手
示意图如下:
image.png最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。
- 服务端进程创建传输模块TCB,时刻准备接受客户端进程的连接请求,此时服务端处于LISTEN(监听)状态。
-
第一次握手 客户端进程创建TCB模块向服务器发送请求连接报文。
- 报文首部中的SYN置1,选择一个初始序列号seq=x。
- 此时客户端进入SYN-SENT(同步已发送状态)。
- TCP规定,SYN报文段(SYN=1时)不能携带数据,但是需要消耗掉一个序号。
-
第二次握手 TCP服务器接收到请求报文后,如果同意连接,则发出确认报文。
- 确认报文中ACK=1,SYN=1,确认号ack=x+1(因为第一次发送的SYN报文段没有携带数据,所以ack = x+1),同时自己也初始化一个序列号seq=y。
- 所以此时确认报文中为: SYN=1,ACK=1,ack=x+1,seq=y。
- 此时服务器进入到SYN-RCVD(同步收到)状态。
- 这个报文也不能携带数据,但是需要消耗一个序号。
-
第三次握手 客户端接收到确认报文后,还需要向服务器给出确认。
- 确认报文中ACK=1,ack=y+1,seq=x+1(因为第一次握手传递的是seq=x,而且没有携带数据,所以这一次序号就为x+1)。
- 此时TCP连接建立,客户端进入到ESTABLISHED(已建立连接)状态。
- TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
- 当服务器收到客户端的确认后也进入ESTABLISHED状态,从后双方就开始通过TCP连接传输数据。
为什么客户端最后还要发送一次确认?
主要是完成两个功能:
- 双方都确认已经做好了发送数据的准备工作。
- 允许双方就初始序列号进行协商,在握手过程中被发送和确认。
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果是采用三次握手的话,那一次失效的请求在到达服务端之后,服务端返回了确认报文,但是客户端没有再次发出 确认。服务器接收不到确认,那么就知道服务端并没有请求连接。
TCP断开连接的四次挥手
示意图如下:
image.png数据传输完毕之后,双方都可以主动断开连接。断开连接前,双方都处于ESTABLISHED状态。
-
第一次挥手 客户端进程发出连接释放报文,并且停止发送数据。
- 释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1)。
- 此时,客户端进入FIN-WAIT-1(终止等待1)状态。
- TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
-
第二次挥手 服务器收到连接释放报文,发出确认报文。
- ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时。
- 服务端就进入了CLOSE-WAIT(关闭等待)状态。
- TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
-
第三次挥手 客户端收到服务器的确认请求后。
- 此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文。
- FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
-
第四次挥手 客户端收到服务器的连接释放报文后,必须发出确认。
- ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。
- 注意此时TCP连接还没有释放,必须经过2∗ *∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。
- 可以看到,服务器结束TCP连接的时间要比客户端早一些。
为什么最后客户端最后需要等待2MSL?
MSL(Maximum Segment Lifetime),意为最大报文段生存时间,TCP允许不同的实现可以设置不同的MSL值。
因为在最后客户端发出确认后,报文传送到服务端需要时间,而且也可能丢失。
如果丢失的话,那么服务端在一定时间内没有收到客户端的确认回应,可能就判断客户端没有收到我的请求,会重新向客户端发送一次,此时客户端接收到之后,会重启2MSL计时器,然后再给服务端发送确认回应。
为什么连接只需要三次,但是断开需要四次?
因为在连接是,当服务端接收到客户端的连接请求之后,将SYN和ACK放在一个报文中发送给客户端。
而关闭连接是,服务端接受到客户端发送的FIN报文时,仅仅表示客户端不会在发送数据但是还能接收数据,而自己也未必将全部数据都发送给客户端了,所以首先发送一个ACK报文确认接收到客户端的断开请求,然后等待可能没有发送完的数据发送完毕之后,在发送FIN报文给客户端,表明自己也没有数据要发送了。因为FIN报文和ACK报文一般是分开发送,从而导致多了一步。
如果已经建立连接的情况下,但是客户端出现了故障怎么办?
TCP设有一个保活计时器。当客户端出现故障之后,服务端每收到一次客户端的请求就会重新复位计时器。通常为2个小时,如果没有接收到任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若连续发送了10次都没有反应,则认为客户端出现了问题,接着 服务器端关闭连接。
end
对于网络充满了好奇,慢慢的学习,慢慢的深入,发现越来越有趣!
网友评论