问题描述
我们最近在学习网络相关的内容,TCP 自然是绕不开的技术内容,必须要学习。
该笔记将记录:与 TCP 协议相关内容,是我们的学习笔记。
解决方案
我们通过以下途径进行学习:
1)在互联网中开放的免费学习视频;
2)通过 Wireshare 抓包观察(curl https//blog.k4nz.com ⇒ ./tcp.pcapng)
初步认识
面向连接的传输层协议,提供可靠的传输服务。
端口号标识要访问的服务。
五元组来区分不同的连接:源、末网络地址,源、末端口号,协议类型
原理简述
建立连接(三次握手)
如下为 TCP 建立连接(三次握手)的文字描述,其中的数据来自于 Wireshark 抓包的结果:
0 Server
在创建连接前,Server 处于 LISTEN 状态,以准备接收来自 Client 连接请求;
1 Client ⇒ Server(第一次)
Client 向 Server 发送 SYN 数据包:SYN = 1,Sequence number = 10955578,ACK = 0,Acknowledgment number = 0;
Client 进入 SYN-SENT 状态,等待匹配的连接请求;
2 Client ⇐ Server(第二次)
Server 向 Client 发送 SYN-ACK 数据包:SYN = 1,Sequence number = 157237846,ACK = 1,Acknowledgment number = 10955579;
Server 进入 SYN-RECEIVED 状态,等待请求确认;
3 Client ⇒ Server(第三次)
Client 向 Server 发送 ACK 报文:SYN = 0,Sequence number = 10955579,ACK = 1, Acknowledgment number = 157237847;
Client 进入 ESTABLISHED 状态;
4 Server
Server 收到 ACK 报文后,进入 ESTABLISHED 状态;至此,一条全双工的连接就已经建立。
通过上述的三次报文发送,在 Client 与 Server 之间建立已知连接。
关于 ACK Number 与 SYN Number 增长:
1)在握手过程中,由于没有 Payload 部分,因此 ACK Number 与 SYN Number 每次都 +1;
2)但是在数据发送过程中,ACK Number 与 SYN Number 每次都增加数据大小;
数据发送
假如每次发送 500B 数据,Client 能够连续发送,那么:
A =>
—- Seq = M ~ M + 499
—- Seq = M + 500 ~ M + 999
—- Seq = M + 1000 ~ M + 1499
<= B
—- Ack = M + 1500
重复的 ACK 提示对端重新发送,重传机制。
对于流量控制,通过控制窗口大小(Window)实现:
1)在握手时,会通过 Window 字段,通知对方窗口大小。
2)在数据发送时,Window 包含告诉对端能够接收的窗口大小。
3)窗口大小是动态的,滑动窗口最大 65535(16-bit)
终止连接(四次挥手 / 三次挥手)
在终止连接时,任何一方都可以发起终止请求,不再区分 Server 和 Client 角色 。我们使用 Initiator 代表 发起方 ,使用 Receiver 代表 接受方:
1 Initiator ⇒ Receiver
Initiator 向 Receiver 发送 FIN 报文:Sequence Number = 10956299,ACK = 1,Acknowledgment Number = 157303540,FIN = 1;
发送之后,Initiator 进入 FIN-WAIT-1 状态:等待对端的连接终止请求,或者说连接终止请求已发送;
2 Initiator ⇐ Receiver
Receiver 向 Initiator 发送 ACK 报文:Sequence Number = 157303541,ACK = 1,Acknowledgment Number = 10956300,FIN =1;
发送之后,Receiver 进入 CLOSE-WAIT 状态:等待本地用户终止连接;
3 Initiator ⇐ Receiver
Receiver 向 Initiator 发送 FIN 报文:Sequence Number = 157303540,ACK = 1,Acknowledgment Number = 10956299,FIN = 1;
发送之后,Receiver 进入 LAST-ACK 状态:等待之前发送到远程TCP的已知连接终止请求;
接收之后,Initiator 进入 FIN-WAIT-2 状态
4 Initiator ⇒ Receiver
Initiator 向 Receiver 发送 ACK 报文:Sequence Number = 10956300,ACK = 1,Acknowledgment Number = 157303541,FIN = 1;
发送之后,Initiator 进入 TIME-WAIT 状态:等待足够的时间以确保远程TCP收到连接终止请求。
5 Receiver
接收之后,Receiver 进入 CLOSE 状态:
补充说明:
1 为 FIN 报文,2 为 FIN 的 ACK 报文;3 为 FIN 报文,4 为 FIN 的 ACK 报文;理想情况下,先进行 1 2 步骤,然后再进行 2 3 步骤。但是,有时 Initiator 与 Receiver 几乎同时发送 FIN 要求断开连接,因此 1 3 同时发生在网络上,而不是 1 2 结束之后。这也解释为什么「
3 Initiator ⇐ Receiver 的 Sequence Number = 157303540」小于「2 Initiator ⇐ Receiver 的 Sequence Number = 157303541」,因为:Receiver 在发送 对 Initiator 的 ACK 前,已经开始发送 FIN 报文了。因此正确的发包顺序是 1 3 2 4 或 1 3 4 2
此外,也可以通过三次挥手关闭连接(FIN, ACK+FIN, ACK),四次挥手只是常见的一种,各种实现上还会有所出入。
另外,服务器端收到客户端的 FIN 后,很可能还没发送完数据,所以就会先回复客户端一个 ACK 包。稍等一会儿,完成所有数据包的发送后,才会发送 FIN 包,这也就是四次挥手了。
报文格式
Sequence Number:
Acknowledge Number:用于确认收到的哪些数据。
Header length:头部长度;
Resv.:
URG & Urgent Pointer:
ACK:确认位,三次握手中 ACK = 1,
PSH:= 1,对端收到该包时,不缓存,立即交由上层处理;
RST:= 1 ,重置位,
SYN:=1,初始位,建立连接时,首个数据包为 SYN 数据包;
FIN:=1,断开连接时
Window:窗口大小,控制对端发送数据
最后总结
共计 11 种状态,算上 UNKNOWN 共计 12 种状态。
关于 Seq 与 Ack 是如何增长的?
只要发送数据包,序列号就会增长 —— 这个说法是错误的。正确的说法是”有效载荷存在,或者 SYN 设置,或者 FIN 设置,才会导致序列号增加”
在数据发送过程中,根据抓包观察,Next Seq = Seq + TCP Len,而不是 +1 得到下个序列号 。但是,在连接建立过程中,TCP Len = 0,此时 Ack 是要 +1 的。在断开连接时亦是如此,,TCP Len = 0,此时 Ack 是要 +1 的。
1)发送 SYN、FIN、payload 数据包,都会导致 Seq 增加(作为下个数据包的 Seq 值);
2)ACK 值与上次收到的 payload 有关;
半开连接
在等待客户端响应 ACK 的 TCP 连接被称为 半开连接。
相关链接
TCP delayed acknowledgment – Wikipedia
这是针对 TCP ACK 的一种优化机制。不用每次请求都发送 ACK 数据包,而是等待特定时间(40ms)后,如果存在其他待发送数据包,则同时发送。如果不存在其他数据包,则超时后单独发送。
Nagle 算法(纳格算法)
是 TCP 协议中用于减少小包发送数量的一种优化算法,目的是为了提高实际带宽的利用率。举个例子,当有效负载只有 1 字节时,再加上 TCP 头部和 IP 头部分别占用的 20 字节,整个网络包就是 41 字节,这样实际带宽的利用率只有 2.4%(1/41)。往大了说,如果整个网络带宽都被这种小包占满,那整个网络的有效利用率就太低了。Nagle 算法正是为了解决这个问题。它通过合并 TCP 小包,提高网络带宽的利用率。Nagle 算法规定,一个 TCP 连接上,最多只能有一个未被确认的未完成分组;在收到这个分组的 ACK 前,不发送其他分组。这些小分组会被组合起来,并在收到 ACK 后,用同一个分组发送出去。
Service Name and Transport Protocol Port Number Registry
就是端口分配
参考文献
Wikipedia/Transmission Control Protocol
三次握手
How to Check TCP connections States in Linux with Netstat
Understanding TCP Sequence and Acknowledgment Numbers – PacketLife.net
38 | 案例篇:怎么使用 tcpdump 和 Wireshark 分析网络流量?