0.背景
传输控制协议TCP(TransmissionControlProtocol)
简单总结下,TCP就是个位于传输层(四层)的面向连接的端对端可靠传输的全双工通讯协议
如何保证可靠馁,源自于三次握手和四次挥手。
三次握手:确保双方收发能力正常(报文数据能成功交互),可以看下我这个视频。
四次挥手:确保数据传输完全(报文数据不丢失)。
太细节的我也不懂,但是咱们的HTTP协议作为应用层协议,也是基于TCP的。因为开发中经常会碰到,此处简单总结下。
1.netstat命令
netstat命令不多赘述,下面是几个常见的用法:
- 展示TCP连线状态
netstat -antpo
# -t 显示状态
# -n ip显示
# 展示对应的pid
- 统计各TCP状态数
netstat -tnp |awk '{print $6}' | sort |uniq -c
# uniq 删除重复行
# uniq -c 统计重复行次数
- 统计某个状态的数量
netstat -na|grep ESTABLISHED|wc -l
2.TCP报文
不管你啥TCP报文都是长这样子的,一模一样根本分不出来谁是谁啊?
就跟看日志一样,单看到一个报文,我咋知道它是请求还是响应报文?
它搞这么多个字段,就是为了区分状态,比如要区分请求还是响应,我们可以在报文里加个direction字段,值用req/resp,看到req,我就知道这是一个请求报文。
TCP三次握手中SYN,ACK,seq ack的含义,TCP报文头详解,看的时候要注意下,ACK和ack是特意区分大小写的。
- TCP报文:由TCP首部和TCP数据两部分组成
- 首部:20字节的固定长度和可变长字段(选项和填充)组成
- 数据偏移(首部总长度):由行4所在的前4位数据偏移决定,最大1111(十进制15),这里的长度单位是字节,所以最大是4*15=60字节(480位)。
- 选项和填充:由于前面5行的20字节是定长,所以这里还有40字节的信息可以选填。可选部分的信息不一定是4字节,填充位(补0)是为了使TCP首部为4字节的整数倍。
- 数据:数据部分是可选的,比如三次握手和四次挥手。
3.TCP的11种状态
11种状态就是三次握手的5种加上四次挥手的6种
2.1 三次握手
ACK、SYN和FIN这些大写的单词表示标志位(1位),其值要么是1,要么是0;ack、seq小写的单词表示序号(32位、4字节)。
-
第一次握手的时候,箭头上有个SYN和seq。
-
同步位SYN
这个玩意用来区分TCP的报文是不是建立连接的报文(是不是握手报文),只有在TCP一二次握手时才会置位1,二次握手完成后SYN这个标志位被置0。
-
序列号seq
用来标记数据段的顺序。数据量大了肯定要分段发送,第一次本地随机产生ISN(Initial Sequence Number,初始序号值)。
握手阶段: 我当前的seq+1
传输阶段: 我当前的seq+我要发送的数据长度
这里我怕描述的有问题,抄一段网上的:
1.一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。每发送一次数据,就累加一次该数据字节的大小。 2.序列号不会从零开始,而是在建立连接时通过计算机随机生成一个数,由SYN包传给接收端主机。 3.假设主机A和主机B进行TCP通信,A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN(Initial Sequence Number,初始序号值)。 那么在该传输方向上(从A到B),后续的TCP报文段中序号值将被系统设置成ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。 例如,某个TCP报文段传送的数据是字节流中的第1025~2048字节,那么该报文段的序号值就是ISN+1025.另外一个传输方向(从B到A)的TCP报文段的序号值也具有相同的含义。
看完上面这句话我又懂了,由于收到的数据可能是乱的,但是只要你告诉我这个数据包在整个数据的哪个位置,我就能拼装了还原回来。
比如,数据被拆成了好几个包,然后第二个包比第一个包还先到,但是我拿ISN减一下,我就知道它该在整个请求数据的哪个位置了。
-
-
第二次握手
-
确认号ack
假设对面扔过来一堆TCP的报文,你肯定疑惑,哇靠都一样的,哪个是我要的?所以,你得告诉对面怎么表达。
上面不是有个seq吗?结合这个就能保证顺序,这个时候,就出现了seq和ack的计算规则。告诉对面,你希望收到的seq值是啥。
-
确认位ACK
标识数据成功接收且确认号ack有效。当ACK=1时,接收到数据且确认号ack有效。ACK=0时,未接收到数据且ack无效。
TCP规定,连接建立后的所有传输报文,都必须把ACK置为1。
为啥要搞个ACK位呢?看下上面SYN的描述,我们可以结合SYN和ACK分析出TCP报文的类型。
若SYN=1,ACK=0表示这是个请求连接的报文。若SYN=1,ACK=1表示这是个接受连接的报文。
-
SYN | ACK | 含义 |
---|---|---|
1 | 0 | 第一次握手 |
1 | 1 | 第二次握手 |
0 | 1 | 第三次握手/数据传输报文 |
2.1.1 三次握手的5种状态
2.1.1.1 CLOSED
2.1.1.2 LISTEN
2.1.1.3 SYN_RCVD
2.1.1.4 SYN_SENT
2.1.1.5 ESTABLISHED
2.1.2 总结
2.2 四次挥手
2.2.1 四次挥手的6种状态
2.2.2 总结
4.wireshark分析
打开wireshark,然后本地用xshell连接腾讯云的服务器,捕捉数据包。
先简单看下wireshark的窗口
3.1 三次握手
tcp and ip.addr==175.178.189.195 and tcp.stream==5
# 这里用到了stream, Wireshark用Stream index来区分不同的请求.
图里的就是三次握手阶段的报文,我们一个个来看。
3.1.1 一阶段握手
注意用[]括起来的是wireshark加上的注释信息
38 a4 00 16 34 e7 d8 a7 00 00 00 00 80 02 fa f0 84 3c 00 00 02 04 05 b4 01 03 03 08 01 01 04 02
###### 格式化一下
行1: 38 a4 00 16 源端口 目的端口
行2: 34 e7 d8 a7 序列号
行3: 00 00 00 00 确认号
行4: 80 02 fa f0 数据偏移(首部长度)4+保留位6+标志位6+窗口16
行5: 84 3c 00 00 校验和+紧急指针
# 20字节(4字节*5行=20字节) 固定长度结束
行6: 02 04 05 b4
行7: 01 03 03 08
行8: 01 01 04 02
##### 转换为2进制
行1: 1110 0010 1001 0000 0000 0000 0101 1000
行2: 1101 0011 1001 1111 0110 0010 1001 1100
行3: 0000 0000 0000 0000 0000 0000 0000 0010
行4: 1000 0000 0000 0010 1111 1010 1111 0000
行5: 1000 0100 0011 1100 0000 0000 0000 0000
# 20字节(32位*5行=160位) 固定长度结束
行1: 38 a4: 2字节,源端口 14500
行1: 00 16: 2字节,目的端口 22
行2: 34 e7 d8 a7: 4字节,序列号seq,887609511
行3: 00 00 00 00: 4字节,确认号ack,0
行4: 80 02 fa f0: 这里的信息有点多,拆分成2进制是1000 0000 0000 0010 1111 1010 1111 0000
1.前面16位最后6个00 0010是标志位:URG=0,ACK=0,PSH=0,RST=0,SYN=1,FIN=0,由SYN=1,ACK=0 => 第一次握手
2.后面16位是窗口大小: 64240(接收通告窗口Receiver Window,RWND,TCP滑动窗口协议,告诉对方本端的TCP接收缓冲区还能容纳多少字节 的数据,这样对方就可以控制发送数据的速度,从而达到流量控制.窗口大小为一个16位字段,故窗口大小最大为65535)
行5: 84 3c 00 00: 1.前面16位是校验和,用CRC算法校验报文数据(头+数据),确保数据不被损坏 2.后面16位是紧急指针,配合紧急标志位URG使用.
行6、7、8: 可选项和填充,其中可选项的格式有点像那种TLV结构的,可选项格式:1字节类型信息(kind),1字节长度信息(length),n字节具体信息(info),有时候是只有kind的.
校验头和紧急指针不是本文重点,补充几链接做参考:
好,下面结合wireshark的图来展示下:
由SYN=1,ACK=0(seq=887609511),可以知道这是1阶段握手。
3.1.2 二阶段握手
3.1.3 三阶段握手
3.1.4 第一次数据传输
3.1.5 第二次数据传输
3.1.6 总结下上面几个阶段的seq和ack
wireshark的相对值比较好用,直接参考这个。
#1次握手: client -> server
seq: 0 (本地随机的是: 887609511)
#2次握手: server -> client
seq: 0
ack: 1 (本地随机的是: 1359191029)
#3次握手: client -> server
seq: 1
ack: 1
#第1次数据传输: server -> client (根据端口可以看到是server给client发的)
seq: 1
ack: 1
#第2次数据传输: client -> server
seq: 1
ack: 22
#第3次数据传输: client -> server
seq: 51
ack: 22
第2次传输时,ack送的是22,seq22的数据去哪了?
被我在这里找到了