TCP协议(一)概述
关于TCP协议,想开一个专题来介绍一下。先挖坑再慢慢填。如有纰漏,欢迎指正 😄
什么是TCP协议
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF RFC 793定义。
TCP协议位于OSI 7层参考模型和TCP/IP 4层参考模型的传输层,TCP/IP参考模型:
- 应用层。处于最高层,这一层有较多协议,例如:HTTP/FTP/SMTP协议
- 传输层。主要有TCP和UDP协议,TCP传输的是数据段(Segment),UDP传输的是数据报(Datagram)
- 网络层。有IP/ICMP协议等,IP协议传输的是包(Packet)
- 网络接口层。对应OSI的数据链路层和物理层,数据链路层传输的是帧(Frame),物理层传输的是比特(Bit)
TCP数据段 Segment
以太网帧(Frame)默认的MTU(Maximum Transmission Unit,最大传输单元)是1500字节,如下图所示:
也就是说,IP协议的包最长是1500字节,IP协议头至少20字节,TCP协议头至少20字节,因此TCP协议的最大负载(payload)是1460字节,但TCP和IP协议往往有额外的头信息,因此TCP实际负载要低于1460字节。
如果应用层协议(如HTTP)要发送2500字节数据怎么办?那就需要分成两个TCP Segment,通过给Segment编号确保接收方能按顺序进行数据还原。
TCP头信息
协议中对TCP头部的定义如下 图片来源 :
- Source Port 源端口,即本地端口 16 Bits
- Destination Port 目标端口,即远程端口 16 Bits
提示
前面说过TCP是面向连接的,而一个TCP连接是由五元组来确定(源IP,源端口,目标IP,目标端口,协议),在TCP头部只定义了端口,IP地址和协议信息是在IP协议头中定义。
- Sequence Number 序号,用于保证数据有序,通常缩写为SEQ
- Acknowledgement Number 通常缩写为ACK,数据接收方每收到一个Segment都会回复一个ACK,以确保数据不丢失
- Offset 偏移量,该字段指定了TCP头部会有几个32 Bits的Word组成,取值范围[5,15],即TCP头部长度在20 Bytes ~ 60 Bytes 之间
- Reserved 4 Bits的保留字段
- Flags 标志位 8 Bits,Bit置为1表示该位生效,后续会再详细介绍,这里仅简单说明含义
| 标志位 | 含义 |
|---|---|
| CWR | 略 |
| ECE | 略 |
| URG | 紧急标志,表示这个Segment需要优先处理 |
| ACK | 略 |
| PSH | 表示Segment需要优先处理 |
| RST | 复位标志,用于告诉对方需要重置一个连接 |
| SYN | 用于在三次握手中建立连接 |
| FIN | 用于关闭连接 |
提示
PSH与URG的区别:带有PSH的Segment会进入接收端缓冲区,并告知接收方应刷新缓冲区,把缓冲区中的所有数据都交付给应用层;而URG报文不会进入接收端缓冲区,会直接交付到应用层。
- Window 即滑动窗口(Sliding Window),用于流量控制,后续再详细介绍
- Checksum TCP Segment的校验和
- Urgent Pointer 紧急数据指针,只有URG标志位为1时有效
TCP的状态
掌握TCP就必须了解每个TCP连接的状态迁移:
以及三次握手和四次握手:
三次握手
三次握手用于建立TCP连接,在三次握手之前,Server必须处于监听(LISTEN)状态,并打开一个端口,否则将无法建立起连接。三次握手的过程:
- Client向Server发送SYN标志和初始化序列号(ISN: Initial Sequence Number),即图中的x,此时Client进入SYN_SENT状态
- Server收到SYN和SEQ=x,回复ACK=x+1,并同时产生一个Server端的ISN(即图中的y)和SYN标志发送给Client,此时Server进入SYN_RCVD状态
- Client收到ACK,确认ACK是否为x+1,收到SYN+SEQ y,回复ACK=y+1。此时Client进入ESTABLISHED状态,当Server收到ACK时也进入ESTABLISHED状态,完成TCP连接建立。
提示
三次握手的过程可以看成双方交换ISN的过程,双方发送自己的ISN并确认收到对方的ISN。
四次握手
四次握手用于断开TCP连接,前面提到TCP是面向连接的、可靠的,因此断开连接的过程也要保证是可靠的。关于上图有一点需要注意:TCP断开连接并不是一定要Client来发起,Server端同样可以发起。我习惯上称主动关闭的一方和被动关闭的一方,为便于表述以及和图片对照,仍把主动关闭的一方称为Client。四次握手过程如下:
- Client发起FIN报文+SEQ,进入FIN_WAIT_1状态
- Server收到FIN后回复ACK,进入CLOSE_WAIT状态
- Server等待自己的数据全部发送完毕后,向Client发送一个FIN,进入LAST_ACK状态,等待最后一个ACK
- Client收到Server的FIN,回复ACK后进入TIME_WAIT状态,Server收到ACK直接关闭
Client进入TIME_WAIT后仍需等待2 MSL(Max Segment Lifetime,最大数据段生存周期)才能进入CLOSED状态。因为:1.需要确保Server端收到最后一个ACK,若Server端未收到ACK会重发FIN,此时Client还需响应;2.因Segment在网络中的最长生存周期是MSL,等待2MSL可以确保在本次TCP连接中发送的Segment在网络中消失,避免跟后续连接的数据包混在一起。
提示
RFC793定义的MSL是2分钟,在Linux中硬编码为30秒,网上流传可以通过调整tcp_fin_timeout参数来改变MSL实际上是不对的
在四次握手中,
主动关闭的一方会经历 FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED,
被动关闭的一方会经历 CLOSE_WAIT -> LAST_ACK -> CLOSED,
记住以上状态变更,对定位相关问题会有帮助~~
实战:netstat 工具
理论需要配合实践,netstat就是查看网络连接的最好工具,这里先介绍基本使用,后续将会有高级用法。
Windows和Linux都自带netstat工具,可以查看协议五元组和连接状态,在Linux中使用-t参数可以查看所有TCP连接: