🍁博主简介:
🏅云计算领域优质创作者
🏅2022年CSDN新星计划python赛道第一名
🏅2022年CSDN原力计划优质作者
🏅阿里云ACE认证高级工程师
🏅阿里云开发者社区专家博主
💊交流社区:CSDN云计算交流社区欢迎您的加入!
目录
1、TCP报文格式及参数含义
2、TCP协议的基本特点
3、三次握手(TCP协议的连接建立)
4、为什么三次握手?而不是二次、四次握手?
5、TCP三次握手中的异常情况
5.1 异常一:第一次握手丢了
5.2 异常二:第二次握手丢了
5.3 异常三:第三次握手丢了
5.4 异常四:SYN 攻击与避免
6、四次挥手(TCP协议的连接释放)
7、TCP四次挥手中的异常情况
7.1 第一次挥手丢了
7.2 第二次挥手丢了
7.3 第三次挥手丢了
7.4 第四次挥手丢了
8、抓包分析TCP协议
9、TCP协议的应用场景
1、TCP报文格式及参数含义
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在OSI模型的第四层,即传输层,为用户提供可靠的、有序的和无差错的数据传输服务。TCP协议与UDP协议是传输层的两大主要协议,但两者在设计上有明显的不同:TCP提供的是可靠的数据传输服务,而UDP则更注重传输的速度和效率
TCP 把连接作为最基本的对象,每一条 TCP 连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到 IP 地址即构成了套接字,例如,若 IP 地址为 192.168.10.11 而端口号为 80,那么得到的套接字为192.168.10.11:80
。IP 协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信才是应用进程之间的通信。
TCP 报文是 TCP 层传输的数据单元,也叫报文段。TCP 报文的格式如下图所示:
每个参数的含义:
-
来源端口号(16位):识别发送连接端口。
-
目的端口号(16位):识别接收连接端口。
-
序列号(seq,32位):用来解决网络乱序问题。
如果含有同步化标志(SYN),则此为最初的序列号(ISN),第一个数据字节的序列号为 ISN + 1;如果没有同步化标志(SYN),则此为第一个数据比特的序列号。
-
确认号(ack,32位):期望收到的数据的开始序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
-
数据偏移(4位):以4字节为单位计算出的数据段开始地址的偏移值。相对于TCP头开始位置,没有选项时该值为5。
-
保留(3位):须置0
-
标志符(9位)
-
NS:ECN-nonce。ECN显式拥塞通知(Explicit Congestion Notification)是对TCP的扩展,定义于 RFC 3540 (2003)。ECN允许拥塞控制的端对端通知而避免丢包。ECN为一项可选功能,如果底层网络设施支持,则可能被启用ECN的两个端点使用。在ECN成功协商的情况下,ECN感知路由器可以在IP头中设置一个标记来代替丢弃数据包,以标明阻塞即将发生。数据包的接收端回应发送端的表示,降低其传输速率,就如同在往常中检测到包丢失那样。
-
CWR:Congestion Window Reduced,定义于 RFC 3168(2001)。
-
ECE:ECN-Echo有两种意思,取决于SYN标志的值,定义于 RFC 3168(2001)。
-
URG:为1表示高优先级数据包,紧急指针字段有效。
-
ACK:为1表示确认号字段有效。
-
PSH:为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
-
RST:为1表示出现严重差错。可能需要重新创建TCP连接。还可以用于拒绝非法的报文段和拒绝连接请求。
-
SYN:为1表示这是连接请求或是连接接受请求,用于创建连接和使顺序号同步。
-
FIN:为1表示发送方没有数据要传输了,要求释放连接。
-
窗口大小(WIN,16位):表示从确认号开始,本报文的发送方可以接收的字节数,即接收窗口大小。用于流量控制。
-
校验和(Checksum,16位):对整个的TCP报文段,包括TCP头部和TCP数据,以16位字进行计算所得。这是一个强制性的字段。
-
紧急指针(16位):本报文段中的紧急数据的最后一个字节的序号。
-
选项(最多40字节):每个选项的开始是1字节的kind字段,说明选项的类型。
-
0:选项表结束(1字节)
-
1:无操作(1字节)用于选项字段之间的字边界对齐。
-
2:最大报文段长度(4字节,Maximum Segment Size,MSS)通常在创建连接而设置SYN标志的数据包中指明这个选项,指明本端所能接收的最大长度的报文段。通常将MSS设置为(MTU-40)字节,携带TCP报文段的IP数据报的长度就不会超过MTU(MTU最大长度为1518字节,最短为64字节),从而避免本机发生IP分片。只能出现在同步报文段中,否则将被忽略。
-
3:窗口扩大因子(3字节,wscale),取值0-14。用来把TCP的窗口的值左移的位数,使窗口值乘倍。只能出现在同步报文段中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节。
-
4:sackOK,发送端支持并同意使用 SACK 选项。
-
5:SACK 实际工作的选项。
-
8:时间戳(10字节,TCP Timestamps Option,TSopt),
发送端的时间戳(Timestamp Value field,TSval,4字节),时间戳回显应答(Timestamp Echo Reply field,TSecr,4字节)
-
19:MD5 摘要,将 TCP 伪首部、校验和为0的TCP首部、TCP数据段、通信双方约定的密钥(可选)计算出MD5摘要值并附加到该选项中,作为类似对TCP报文的签名。通过 RFC 2385 引入,主要用于增强 BGP 通信的安全性。
-
29:安全摘要,通过 RFC 5925 引入,将“MD5摘要”的散列方法更换为SHA散列算法。
-
Linux中TCP头结构体定义如下:
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
2、TCP协议的基本特点
面向连接
-
TCP协议在传输数据之前,必须先建立TCP连接,并在数据传送完毕后释放连接。
-
每一条TCP连接只能有两个端点,且是点对点的(一对一)。
可靠传输
-
TCP提供可靠交付的服务,通过TCP连接传送的数据无差错、不丢失、不重复,并且按序到达。
-
TCP采用发送应答机制,发送的每个报文段都必须得到接收方的应答才认为传输成功。
-
TCP使用校验和来检验数据是否有错误,并在发送和接收时都计算校验和。
全双工通信
-
TCP允许通信双方的应用进程在任何时候都能发送数据。
-
TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。
面向字节流
-
TCP将应用程序交下来的数据看成是一连串的无结构的字节流。
-
TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。
3、三次握手(TCP协议的连接建立)
TCP连接的建立过程通常被称为“三次握手”,具体步骤如下:
第一次握手
-
客户端向服务端发送一个SYN报文段(同步序列编号SYN=1),并包含一个初始序列号ISN(Initial Sequence Number)。
第二次握手
-
服务端收到SYN报文段后,以自己的ISN回应(SYN=1),并确认收到客户端的SYN报文段(ACK=1,确认号为客户端的ISN+1)。
第三次握手
-
客户端收到服务端的SYN+ACK报文段后,向服务端发送一个ACK报文段(ACK=1,确认号为服务端的ISN+1),至此TCP连接建立完成。
4、为什么三次握手?而不是二次、四次握手?
采用三次握手有三个原因:
-
防止重复历史连接的初始化(主要原因);
-
同步双方的初始化序列号 ISN;
-
避免资源浪费。
不使用两次握手和四次握手的原因:
-
两次握手:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
-
四次握手:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
5、TCP三次握手中的异常情况
5.1 异常一:第一次握手丢了
客户端给服务端发的 SYN 包丢失后,也就不会收到 SYN+ACK,然后会启动「超时重传」,重传的 SYN 包的序列号都是一样的。
在 Linux 里,客户端的 SYN 包最大重传次数由 tcp_syn_retries 内核参数控制,可以修改,默认值一般是 6。
# cat /proc/sys/net/ipv4/tcp_syn_retries
6
第一次超时重传是在 1 秒后,第二次在 2 秒后,第三次在 4 秒后,第四次在 8 秒后,第五次在 16 秒后,第六次在 32 秒后。每次超时的时间是上一次的 2 倍。
当第六次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。所以,总耗时是 1+2+4+8+16+32+64=127 秒,大约 2 分钟左右。
5.2 异常二:第二次握手丢了
服务器给客户端发的 SYN + ACK 包丢了,会造成双方超时重传:
-
客户端迟迟收不到服务端回应的 ACK,发生超时重传,重传 SYN,和上面第一次握手丢了一样,第五次超时重传超时后断开连接;
-
服务端发了SYN,也需要客户端回应 ACK包,不然也会超时重传,重传 SYN+ACK。第五次重传超时后断开连接。
在 Linux 下,SYN+ACK 报文的最大重传次数由 tcp_synack_retries 内核参数决定,默认值是 5。
# cat /proc/sys/net/ipv4/tcp_synack_retries
5
5.3 异常三:第三次握手丢了
客户端发给服务端的 ACK 包丢了,会导致服务端超时重传 SYN+ACK,而不是客户端超时重传ACK包,这里强调下,所有 ACK 不会「超时重传」。
和上面第二次握手丢了服务端的行为一样,第 tcp_synack_retries 次超时重传超时后,服务端断开连接。但是客户端已经是 ESTABLISHED 状态,最后会因为发送数据失败,关闭连接。
5.4 异常四:SYN 攻击与避免
攻击者短时间伪造大量不同的 IP 地址发送 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发给伪 IP的的 ACK+SYN 报文,无法得到回应 ACK 报文,不停的往「半连接队列」塞数据,打满半连接队列,后续再收到 SYN 报文就会丢弃,导致其他正常的客户端无法和服务端建立连接。
开启 net.ipv4.tcp.tcp_syncookies,可以解决 SYN 攻击。参数有下面三个值:
-
0 值,表示关闭该功能;
-
1 值,表示仅当 「SYN 队列」满时,再启用它;
-
2 值,表示无条件开启功能。
开启 syncookies 功能可以绕过「SYN 队列」成功建立连接。过程如下图:
-
当「 SYN 队列」被打满后,后续服务端收到 SYN 包,不会丢弃,而是根据算法(通过原地址端口,目的地址端口和时间戳打造一个特别的Sequence Number)计算出一个 cookie 值;将 cookie 值放到第二次握手报文的「序列号」里,然后发给客户端;
-
客户端收到 SYN+ACK 包后,正常回应 ACK cookie+1;攻击者伪装的 IP 不会应答;
-
服务端接收到客户端的应答报文时,服务端会检查这个 ACK 包的合法性。如果合法,将该连接对象放入到「 Accept 队列」。
-
最后应用程序通过调用 accpet() 接口,从「 Accept 队列」取出连接对象。
那么在应对 SYN 攻击时,只需要设置 tcp_syncookies 为 1,命令如下:
$ echo 1 > /proc/sys/net/ipv4/tcp_syncookies
6、四次挥手(TCP协议的连接释放)
TCP连接的释放过程通常被称为“四次挥手”,具体步骤如下:
第一次挥手
-
客户端发送给服务器一个请求释放连接的数据包,即发送了一个指向服务器目标端口的一个 FIN 位为 1 的TCP 报文,表示客户端没有数据要发送了,但是仍然可以接收数据;并且 ACK 位也为 1,表示对上次传输数据结果的确认。并且之后处去等待状态,等待服务器的两次回应。
第二次挥手
-
服务器接收到客户端的释放连接请求之后,会先回应一个 ACK 位为 1 的报文,表示确认收到。但是,这时服务器可能还有数据没有发送完成,继续发送数据。
第三次挥手
-
服务器发送完数据之后,发送一个 FIN 为 1 的 TCP 报文,表示我也没有要发送的数据了,你可以释放连接了。当然 ACK 位仍然为 1 。
第四次挥手
-
客户端接收到服务器的同意释放连接的数据包之后,回复一个 ACK 为 1 的 TCP 报文,表示确认收到。
7、TCP四次挥手中的异常情况
7.1 第一次挥手丢了
当「主动方」调用 close 函数后,就会向「被动方」发送 FIN 报文,此时客户端的连接进入 FIN_WAIT_1 状态。
如果第一次挥手丢失了,那么「主动方」迟迟收不到「被动方」的 ACK 的话,就会触发超时重传 FIN 报文,重发次数由 tcp_orphan_retries 参数(默认 0)控制。超时重传次数超过 tcp_orphan_retries 后,再等一段时间(指数增加)还是没有收到第二次握手,就关闭连接。
# cat /proc/sys/net/ipv4/tcp_orphan_retries
0
7.2 第二次挥手丢了
在前面提过,ACK 报文不会超时重传,所以如果「被动方」的第二次挥手丢失了,「主动方」就会超时重传 FIN 报文,直到收到服务端的第二次挥手。与第一次挥手丢失行为一样。
7.3 第三次挥手丢了
当「主动方」收到第二次挥手 ACK 后,进入 FIN_WAIT_2 状态。
对于调用 close 函数主动关闭的连接,由于无法再发送和接收数据,所以 FIN_WAIT2 状态不可以持续太久,会启用一个计时器,时间长度由 tcp_fin_timeout 控制,默认60秒,如果计时器超时了还没有收到第三次挥手 FIN 报文,就直接进入 CLOSED 状态。
# cat /proc/sys/net/ipv4/tcp_fin_timeout
60
但是对于调用 shutdown 函数主动关闭的连接,只关闭发送方向,没有关闭接收方向,没有计时器。如果「主动方」一直没收到第三次挥手,那么「主动方」将会一直处于 FIN_WAIT2 状态。
「被动方」处于 CLOSE_WAIT 状态时,调用 close 函数,内核会发出 FIN 报文,然后进入 LAST_ACK 状态,等待「主动方」返回 ACK 来确认连接关闭。如果第三次挥手 ACK 丢失了,「被动方」就会超时重传 FIN 报文,重发次数仍然由 tcp_orphan_retries 参数决定,最后还是没有收到直接进入 CLOSED 状态。
7.4 第四次挥手丢了
如果第四次挥手的 ACK 报文丢了,「被动方」就会超时重传 FIN 报文,重发次数仍然由 tcp_orphan_retries 参数决定。「主动方」在收到 FIN 后,进入 TIME_WAIT 状态,开启时长为 2MSL 的计时器,如果途中再次收到第三次挥手 FIN 报文后,就会重置 2MSL 的计时器。如果第四次挥手一直丢失,但是第三次挥手不丢失,会使「主动方」迟迟关闭不了连接。
8、抓包分析TCP协议
利用 wireshark 工具抓包分析可以很清晰的看到这两个流程。
-
打开 wireshark 软件 开启抓包,过滤规则设置为 tcp 。
-
开发板连接上网络后,运行 samples 里面的 tcp client 例程,
这样就可以很方便的看到 TCP 的三次握手与四次挥手的全过程,如下图所示:
9、TCP协议的应用场景
TCP协议因其可靠性高、支持流量控制和拥塞控制等特点,广泛应用于对数据可靠性要求高、顺序要求严格的应用场景,如:
-
网页浏览:HTTP协议通常使用TCP作为传输层协议,确保网页内容的完整性和可靠性。
-
电子邮件:SMTP、POP3和IMAP等邮件协议使用TCP协议进行可靠的邮件传输。
-
文件传输:FTP协议使用TCP进行文件的可靠传输。
-
实时通信:如语音通话、视频通话等应用需要保证数据的完整性和顺序,通常选择TCP协议。