协议介绍
TCP 是一种面向连接的协议,它通过三次握手来让客户端和服务器建立连接并协商特性,同时提供了有序完成和立即断开连接的机制。
TCP 为应用程序提供可靠、有序的字节流服务。应用程序字节流通过 TCP 段在网络上传送,每个 TCP 段都作为互联网协议 (IP) 数据报发送。
TCP可靠性保证
- 检测数据包丢失(通过序列号)
- 错误(通过每段校验和)
- 重传(超时重传/快速重传)
- 确认应答
TCP段
报文头格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |C|E|U|A|P|R|S|F| |
| Offset| Rsrvd |W|C|R|C|S|S|Y|I| Window |
| | |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| [Options] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
: Data :
: |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Note that one tick mark represents one bit position.
- Source Port
源端口号 - Destination Port
目标端口号 - Sequence Number
如果是SYN报文,序列号为初始序列号(ISN),第一个数据字节的序列号将是ISN+1。如果不是SYN,则表示该报文段中第一个数据字节的序列号。 - Acknowledgment Number
ACK控制位用于指示该报文段是否包含对之前接收到的数据的确认。如果设置了 ACK 控制位,则此字段包含段发送方期望接收的下一个序列号的值。一旦建立连接,就会始终发送此值 - Data Offset (DOffset)
用于指示TCP头部中数据部分的起始位置相对于TCP报文段起始处的偏移量,以32位(即4字节)为单位。由于TCP头部可能包含可变长度的选项字段,因此数据偏移字段是必需的,以便接收方能够正确地解析TCP头部并找到数据部分的开始位置。 - Reserved (Rsrvd)
保留位,值为0 - Control bits
控制位,也称标志,包含- CWR:
1 bit ,拥塞窗口减少,用于IP首部的ECN(Explicit Congestion Notification,显式拥塞通知)字段。当ECE为1时,CWR置为1通知对方已将拥塞窗口缩小,以响应之前网络拥塞的指示。 - ECE:
1 bit,当TCP接收方收到一个设置了ECN标志的IP数据包时,它会在TCP头部中将ECE设置为1,以通知发送方从对方到这边的网络存在拥塞。 - URG: 1 bit. 当URG为1时,表示TCP报文段中有需要紧急处理的数据。此时,TCP头部的紧急指针字段有效,指向紧急数据的最后一个字节。
- ACK: 1 bi t当ACK为1时,确认应答的字段变为有效。TCP使用ACK来确认收到的数据,确保数据的可靠传输。
- PSH: 推送标准,表示接收方应尽快将收到的数据递交给上层应用程序,而不是将其缓存在接收缓冲区中。通常TCP向应用程序延迟传送和延迟交付数据以提高效率。发送方TCP使用缓冲区来存储由发送应用程序提交的数据流,并且可以选择报文段的长度。接收方TCP在数据到达时也要将其进行缓存,当应用程序就绪或者接收方TCP方便时,才将这些数据交付。希望对方立即收到就不能采用这种延迟方式,而需要发送方应用程序请求推送(PUSH)操作。发送方TCP设置推送标志(PSH)以告诉接收方TCP该报文段所包括的数据必须尽快地交付给接收应用程序,而不要等待更多的数据的到来。这样每创建一个报文段就立即发送。不过,目前大多数实现中都忽略推送操作请求,TCP可以选择是否使用推送操作。
5、半关闭 - RST: 1 bit 重置标志,表示TCP连接中出现异常,必须强制断开连接。
- SYN: 1 bit 同步标志位,用于建立一个TCP连接,当一个TCP连接开始时,连接的发起方发送一个带有SYN标志的TCP段,接收方回复一个带有SYN和ACK标志的TCP段
- FIN: 1 bit 发送方没有更多数据发送,希望断开连接
- CWR:
- Window
接收窗口大小,可以用于流量控制、滑动窗口机制、拥塞避免等 - Checksum
校验和字段用于确保数据传输的可靠性和完整性 - Urgent Pointer
紧急指针,用于处理紧急数据 - Options
段用于存储自定义的数据或扩展协议的功能。 - Data
Data 字段是 TCP 段中实际传输的数据部分,包含了应用程序层的数据。
分段传输
TCP 段(TCP Segment)是 TCP 协议中传输数据的基本单位,TCP 将来自发送应用程序的字节流打包成 TCP 段。每个 TCP 段都包含一个 TCP 头部和可能的数据部分。
- TCP 实现必须能够发送和接收 MSS选项 (Maximum Segment Size,最大报文段长度)。
- 应在 SYN 段中发送 MSS 选项,尤其是当接收 MSS 与默认值不同时。
- 如果在连接建立时未收到 MSS 选项,应假设默认的发送 MSS 为 IPv4 的 536 或 IPv6 的 1220。
实际发送的 TCP 段的最大大小应是远程主机可用的重组缓冲区大小和 IP 层允许的最大传输大小中的较小值。
Sequence Numbers 序列号
TCP序列号是一个32位的无符号整数,用于标识TCP报文段中数据的第一个字节的序号。每个TCP报文段都包含序列号,以指示该报文段中的数据在字节流中的位置。TCP序列号确保数据的顺序传输和可靠性。
作用:
确保顺序性:通过序列号,接收方能够按照正确的顺序重新组装接收到的TCP报文段中的数据。
检测数据丢失:如果接收方发现某个序列号的数据缺失,它可以要求发送方重传该数据。
实现可靠性:序列号与TCP的确认机制(ACK)相结合,确保数据的可靠传输
与确认号的关系:接收方在收到TCP报文段后,会向发送方发送一个确认报文段(ACK),其中包含一个确认号。确认号表示接收方已经成功接收到的数据的最后一个字节的序号加1。
TCP连接与关闭
TCP连接状态
- LISTEN - 表示等待来自任意远程 TCP 对等体和端口的连接请求
- SYN-SENT - 表示发送连接请求后,等待匹配的连接请求
- SYN-RECEIVED - 表示在接收和发送连接请求后,等待确认连接请求确认
- ESTABLISHED - 表示连接已打开,收到的数据可以传送给用户。连接数据传输阶段的正常状态
- FIN-WAIT-1 - 表示等待来自远程 TCP 对等方的连接终止请求,或等待先前发送的连接终止请求的确认
- FIN-WAIT-2 - 表示等待来自远程 TCP 对等方的连接终止请求
- CLOSE-WAIT - 表示等待本地用户的连接终止请求
- CLOSING - 表示等待来自远程 TCP 对等方的连接终止请求确认
- LAST-ACK - 表示等待先前发送给远程 TCP 对等体的连接终止请求的确认
- TIME-WAIT - 表示等待足够的时间以确保远程TCP对等方收到其连接终止请求的确认,并避免新连接受到先前连接延迟段的影响
- CLOSED - 表示根本没有连接状态
建立连接(三次握手)
TCP Peer A TCP Peer B
1. CLOSED LISTEN
2. SYN-SENT --> <SEQ=100><CTL=SYN> --> SYN-RECEIVED
3. ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED
4. ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK> --> ESTABLISHED
5. ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED
上图中,TCP 对等体 A 首先发送一个 SYN 段,表明它将使用从序列号 100 开始的序列号。在第 3 行中,TCP 对等体 B 发送一个 SYN 并确认从 TCP 对等体 A 收到的。请注意,确认字段表明 TCP 对等体 B 现在期望听到序列 101,确认占用序列 100 的 SYN。
在第 4 行,TCP 对等体 A 用一个空段进行响应,其中包含对 TCP 对等体 B 的 SYN 的 ACK;在第 5 行,TCP 对等体 A 发送一些数据。请注意,第 5 行中段的序列号与第 4 行中的相同,因为 ACK 不占用序列号空间。
半打开连接
如果 TCP 对等体之一在另一端不知情的情况下关闭或中止了其一端的连接,或者由于故障或重启导致内存丢失,连接的两端变得不同步,则称已建立的连接为“半开”。如果尝试向任一方向发送数据,此类连接将自动重置。
重置连接
关闭连接(四次挥手)
TCP Peer A TCP Peer B
1. ESTABLISHED ESTABLISHED
2. (Close)
FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> --> CLOSE-WAIT
3. FIN-WAIT-2 <-- <SEQ=300><ACK=101><CTL=ACK> <-- CLOSE-WAIT
4. (Close)
TIME-WAIT <-- <SEQ=300><ACK=101><CTL=FIN,ACK> <-- LAST-ACK
5. TIME-WAIT --> <SEQ=101><ACK=301><CTL=ACK> --> CLOSED
6. (2 MSL)
CLOSED
2MSL(去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)).TIME-WAIT状态的持续时间为2MSL,以确保如果接收方的ACK丢失,发送方能够重传FIN,并且能够在2MSL内重新接收到对方的ACK。
如果连接在TIME-WAIT状态期间收到延迟到达的旧报文段(例如FIN或ACK),TCP可以确定这些报文段属于已经关闭的连接,而不会将其错误地关联到新建立的连接上。linux操作稀土2MSL一般为60s。
半连接关闭
正常的 TCP 关闭序列在两个方向上可靠地传送缓冲数据。由于 TCP 连接的两个方向是独立关闭的,因此连接可能处于“半关闭”状态,即仅在一个方向上关闭,并且允许主机在半关闭的连接上继续在打开方向上发送数据
当连接主动关闭时,它必须在 TIME-WAIT 状态下停留 2xMSL(最大段生存期)的时间。但是,它可以接受来自远程 TCP 端点的新 SYN,以便直接从 TIME-WAIT 状态重新打开连接:
(1)为新连接分配初始序列号,使其大于前一个连接实例中使用的最大序列号,避免数据包混乱。
(2)如果 SYN 被证实为旧的重复包,TCP将返回 TIME-WAIT状态,以避免旧连接的数据影响新连接的完整性和可靠性。
数据通信
重传超时(Retransmission Timeout,RTO)
RTO 是 TCP 用来确定是否需要重传数据的时间间隔。如果在 RTO 时间内没有收到预期的 ACK,发送方会重传相应的数据段。
RTO:基于对往返时间(RTT)的测量和统计来确定的。大多数操作系统和TCP实现都会设置一个默认的RTO值。这个默认值通常基于广泛的网络环境和经验数据设定,可以作为一个起始点进行测试和验证。
拥塞控制(TCP Congestion Control)
基本拥塞控制算法,包括
- 慢启动 : TCP 开始发送数据时的初始阶段,目的是在不知道网络状况的情况下逐渐增加发送速率。
- 拥塞避免 :拥塞避免算法在慢启动结束后启动,目的是在网络出现拥塞迹象之前,逐步增加数据的发送量。
- TO 指数退避 :当 TCP 检测到超时(例如,由于数据包丢失或网络拥塞),它会触发重传机制,并进入指数退避过程。指数退避的目的是减少重传请求的频率,避免过多的重传数据包再次引起网络拥塞。
- 快速重传:快速重传是另一种重传机制,当接收方收到乱序的数据段并发送了重复的 ACK,发送方会快速重传预期的数据段,而不必等待 RTO。
流量控制(Flow Control)
TCP 协议为接收方提供了一种控制发送方数据流量的方法。是通过滑动窗口机制来实现的,接收方根据自己的缓冲区使用情况动态调整窗口大小。当接收方的缓冲区接近满时,它会减小窗口大小,从而限制发送方发送更多的数据
TCP拥塞控制与流量控制的区别
- 流量控制:主要目的是防止发送方发送数据过快,导致接收方无法及时处理而引起数据丢失。它通过控制发送方的发送速率,确保接收方能够及时处理到达的数据。发送方根据接收方的窗口大小来调整自己的发送窗口,确保不会超过接收方的处理能力。
- 拥塞控制:主要目的是避免因网络中过多的数据而导致的网络拥塞情况,这会影响整个网络的性能。拥塞控制通过调整发送方的发送速率,避免网络资源的过度使用,从而保持网络的稳定性和高效性。拥塞窗口是发送方根据网络的拥塞状况动态调整的(包括慢启动、拥塞避免、快重传和快恢复等算法)
保活 (TCP Keep-Alives)
如果在很长一段时间内没有收到任何传入数据段并且没有新的或未确认的数据要发送,则称 TCP 连接处于“空闲”状态.
TCP Keepalive 是由 TCP 协议栈在操作系统中实现的,不同的操作系统可能有不同的实现方式和配置选项。TCP-keepalive机制并不是TCP协议的标准部分,但大多数操作系统都实现了这一机制。
TCP Keepalive 是传输层的机制,而 HTTP Keep-Alive 是应用层协议 HTTP 的一个特性,用于在单个 TCP 连接上复用多个 HTTP 请求和响应,以减少连接建立和关闭的开销。
Linux Tcp Keep-Alives 保活参数
sysctl net.ipv4.tcp_keepalive_time 保活时间(默认7200s)
sysctl net.ipv4.tcp_keepalive_intvl 保活间隔(默认75s)
sysctl net.ipv4.tcp_keepalive_probes 探测次数(默认9次)
TCP缓冲
当TCP接收到报文后,这些数据确实会首先被缓存起来,而不是立即直接交给应用程序。TCP缓存报文的目的是为了提高可靠性和效率.
以下一些常见条件下,TCP 会将数据从接收缓冲区交给应用程序
- 接收缓冲区达到一定大小
- 应用程序调用 recv() 或 read() 等系统调用
- 数据包接收完成
- TCP 超时或窗口更新:如果接收到的 TCP 数据包较少且没有填满缓冲区,但时间较长且没有更多数据到达,TCP 也可能将当前缓冲区中的数据交给应用程序。
查看缓冲区大小(最小值、默认值和最大值)
[root@ip-172-31-22-75 ~]# cat /proc/sys/net/ipv4/tcp_rmem
4096 131072 6291456
[root@ip-172-31-22-75 ~]# cat /proc/sys/net/ipv4/tcp_wmem
4096 20480 4194304
什么是Socket
Socket,通常称为"套接字",Socket(套接字)是一个抽象层,它提供了网络通信的接口,使得应用程序可以通过Socket来发送和接收数据。TCP 协议的实现细节被封装在操作系统的网络栈中,而 Socket 是操作系统提供给应用程序的接口,用于访问这些协议。Socket API 通常是协议无关的,这意味着它们可以用于不同的网络协议,如 TCP、UDP、IP 等。
Java提供了 java.net.Socket 类来实现 TCP 通信。
Go提供了 net 包,其中包括 Listen() 和 Dial() 等函数来处理 TCP 和 UDP 通信。