网络基础 7 TCP IP协议 1
7. TCP/IP协议
原文链接:[最通俗易懂——TCP/IP协议,让你明明白白!_tcpip通俗介绍-CSDN博客](https://blog.csdn.net/wargames_dc/article/details/104693239#:~:text=本文深入讲解TCP%2FIP协议,包括其在网络体系结构中的角色、数据包处理流程、传输层的TCP和UDP协议工作原理,以及网络层中的IP协议,涵盖了IP地址、路由控制、IP分包与组包等内容。 摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >,从字面意义上讲,有人可能会认为 TCP%2FIP 是指 TCP 和 IP 两种协议。 实际生活当中有时也确实就是指这两种协议。)
1)TCP/IP 的具体含义
从字面意义上讲,有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议。实际生活当中有时也确实就是指这两种协议。然而在很多情况下,它只是利用 IP 进行通信时所必须用到的协议群的统称。具体来说,IP 或 ICMP、TCP 或 UDP、TELNET 或 FTP、以及 HTTP 等都属于 TCP/IP 协议。有时也称 TCP/IP 为网际协议群。
2)数据包
包、帧、数据包、段、消息
以上五个术语都用来表述数据的单位,大致区分如下:
术语 | 所属层级 | 类比 | 例子**** | 首部 |
---|---|---|---|---|
消息 | 应用层(L5) | 一封信 | HTTP请求、DNS查询 | |
段 | 传输层(L4) | 小快递包裹 | TCP分段(带序号和签收单) | 一件包裹上的快递单 |
数据包 | 网络层(L3) | 大快递箱子 | IP包(贴有发/收件人地址) | 箱子里这批货的货单 |
帧 | 数据链路层(L2) | 货运卡车 | 以太网帧(带MAC地址) | 卡车的货运单 |
包 | 全能术语 | “快递”统称 | 可指代任何层级的数据单位 |
联想:
- 发快递流程: 写信(消息)→ 装进小包裹(段)→ 放进大纸箱(数据包)→ 装卡车(帧)→ 统称快递(包)
每层都会接收上层数据并加一个首部,然后发给下层在下一层,例如:
- L4传输层:TCP首部+TCP的数据,整体发送给下一层
- L3网络层:收到上层的全部作为数据,再额外加一个IP首部,整体发送给下一层
- L2数据链路层:收到上层的全部作为数据,再额外加一个以太网包首部,整体发送给下一层
3)数据处理流程
下图以用户 a 向用户 b 发送邮件为例子:
数据处理流程:
📦 场景:你(应用程序)要给朋友寄一本书(数据),快递公司(协议栈)如何处理?
步骤 | 快递流程比喻 | 对应协议层 | 关键动作 |
---|---|---|---|
① 应用程序处理 | 你写贺卡(编码)+ 决定何时寄出 | 表示层+会话层 | 数据格式化+通信时机管理 |
② TCP模块处理 | 快递员打包书+贴防丢标签 | 传输层(TCP) | 加TCP头(端口号、序列号) |
③ IP模块处理 | 快递公司贴发/收件人地址 | 网络层(IP) | 加IP头(源/目标IP) |
④ 以太网驱动处理 | 装车+贴卡车车牌(MAC地址) | 数据链路层(以太网) | 加帧头(MAC地址) |
⑤ 接收端以太网处理 | 仓库检查车牌→ 不是自己的车就拒收 | 数据链路层(以太网) | 校验MAC地址 |
⑥ 接收端IP处理 | 拆箱看地址→ 不是自己的就转寄 | 网络层(IP) | 校验IP地址+路由转发 |
⑦ 接收端TCP处理 | 拆包验货+核对订单号 | 传输层(TCP) | 校验序列号+端口号 |
⑧ 接收端应用处理 | 朋友读贺卡+看书 | 应用层 | 数据解析+展示 |
4)传输层中的 TCP 和 UDP
TCP稳如老狗,连接重传全都有;UDP快如闪电,又快又快
协议 | 类型 | 可靠性 | 连接方式 | 数据形式 | 基本单位 |
---|---|---|---|---|---|
TCP | 面向连接 | ✅ 可靠 | 需建立/断开连接 | 流式数据(无间隔) | 段 |
UDP | 无连接 | ❌ 不可靠 | 直接发送 | 数据报(独立包) | 数据包 |
- IP协议的基本单位也是数据包,所以在叫法上可以通过IP数据包和UDP数据包来进行区分
①端口号
在传输层也有这种类似于地址的概念,那就是端口号。端口号用来识别同一台计算机中进行通信的不同应用程序。因此,它也被称为程序地址。
地址类型 | 所属层级 | 作用 | 类比 |
---|---|---|---|
MAC地址 | 数据链路层(L2) | 同一局域网内设备的物理标识 | 身份证号(不可变) |
IP地址 | 网络层(L3) | 全球互联网中主机的逻辑定位 | 邮寄地址(可变更) |
端口号 | 传输层(L4) | 同一主机内不同应用的标识 | 房间号(区分服务) |
1️⃣根据端口号识别应用
一台计算机上同时可以运行多个程序。传输层协议正是利用这些端口号识别本机中正在进行通信的应用程序,并准确地将数据传输。
2️⃣通过 IP 地址、端口号、协议号进行通信识别
- 仅凭目标端口号识别某一个通信是远远不够的。
通过端口号、IP地址、协议号进行通信识别
① 和 ② 的通信是在两台计算机上进行的。它们的目标端口号相同,都是80。这里可以根据源端口号加以区分。
③ 和 ① 的目标端口号和源端口号完全相同,但它们各自的源 IP 地址不同。
此外,当 IP 地址和端口号全都一样时,我们还可以通过协议号来区分(TCP 和 UDP)。
3️⃣端口号的确定
标准既定的端口号:这种方法也叫静态方法。它是指每个应用程序都有其指定的端口号。但并不是说可以随意使用任何一个端口号。例如 HTTP、FTP、TELNET 等广为使用的应用协议中所使用的端口号就是固定的。这些端口号被称为知名端口号,分布在 01023 之间;除知名端口号之外,还有一些端口号被正式注册,它们分布在 102449151 之间,不过这些端口号可用于任何通信用途。
时序分配法:服务器有必要确定监听端口号,但是接受服务的客户端没必要确定端口号。在这种方法下,客户端应用程序完全可以不用自己设置端口号,而全权交给操作系统进行分配。动态分配的端口号范围在 49152~65535 之间。
4️⃣端口号与协议
端口号由其使用的传输层协议决定。因此,不同的传输层协议可以使用相同的端口号。
此外,那些知名端口号与传输层协议并无关系。只要端口一致都将分配同一种应用程序进行处理。
②UDP
UDP 不提供复杂的控制机制,利用 IP 提供面向无连接的通信服务。 并且它是将应用程序发来的数据在收到的那一刻,立即按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况,UDP 也无法进行流量控制等避免网络拥塞行为。 此外,传输途中出现丢包,UDP 也不负责重发。 甚至当包的到达顺序出现乱序时也没有纠正的功能。 如果需要以上的细节控制,不得不交由采用 UDP 的应用程序去处理。
UDP 常用于一下几个方面:1.包总量较少的通信(DNS、SNMP等);2.视频、音频等多媒体通信(即时通信);3.限定于 LAN 等特定网络中的应用通信;4.广播通信(广播、多播)。
③TCP
TCP 与 UDP 的区别相当大。它充分地实现了数据传输时各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在 UDP 中都没有。 此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。 根据 TCP 的这些机制,在 IP 这种无连接的网络上也能够实现高可靠性的通信( 主要通过建立连接+序号机制、合理分片、数据校验、超时重传、流量控制、拥塞控制等机制实现)。
(a) 三次握手(重点)
TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
下面来看看三次握手的流程图:
三次握手标准流程
1. 第一次握手(SYN)
- 客户端 → 服务器:发送
SYN=1
(同步请求),附带随机初始序列号seq=J
。 - 客户端状态:
SYN_SENT
(等待服务器确认)。
2. 第二次握手(SYN+ACK)
- 服务器 → 客户端:
- 回复
SYN=1
+ACK=1
(确认请求), - 确认号
ack=J+1
(表示已收到seq=J
), - 附带服务器随机序列号
seq=K
。
- 回复
- 服务器状态:
SYN_RCVD
(等待客户端最终确认)。
3. 第三次握手(ACK)
- 客户端 → 服务器:
- 发送
ACK=1
(最终确认), - 确认号
ack=K+1
(表示已收到seq=K
)。
- 发送
- 双方状态:发出后客户端进入
ESTABLISHED
状态,服务器接受到后进入ESTABLISHED
状态(连接建立成功,可传输数据)。
为什么两次握手不行?
先说结论:
- 三次握手才可以阻止重复历史连接的初始化(主要原因)
- 三次握手才可以同步双方的初始序列号
- 三次握手才可以避免资源浪费
原因一:避免历史连接
简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。
我们考虑一个场景,客户端先发送了 SYN(seq = 90) 报文,然后客户端宕机了,而且这个 SYN 报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了 SYN(seq = 100) 报文(注意不是重传 SYN,重传的 SYN 的序列号是一样的)。
看看三次握手是如何阻止历史连接的:
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵情况下:
- 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
- 那么此时服务端就会回一个
SYN + ACK
报文给客户端; - 客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送
RST
报文给服务端,表示中止这一次连接。
如果是两次握手连接,就无法阻止历史连接,那为什么 TCP 两次握手为什么无法阻止历史连接呢?
我先直接说结论,主要是因为在两次握手的情况下,「被动发起方」没有中间状态给「主动发起方」来阻止历史连接,导致「被动发起方」可能建立一个历史连接,造成资源浪费。
你想想,两次握手的情况下,「被动发起方」在收到 SYN 报文后,就进入 ESTABLISHED 状态,意味着这时可以给对方发送数据,但是「主动发起方」此时还没有进入 ESTABLISHED 状态,假设这次是历史连接,「主动发起方」判断到此次连接为历史连接,那么就会回 RST 报文来断开连接,而「被动发起方」在第一次握手的时候就进入 ESTABLISHED 状态,所以它可以发送数据的,但是它并不知道这个是历史连接,它只有在收到 RST 报文后,才会断开连接。
可以看到,上面这种场景下,「被动发起方」在向「主动发起方」发送数据前,并没有阻止掉历史连接,导致「被动发起方」建立了一个历史连接,又白白发送了数据,妥妥地浪费了「被动发起方」的资源。
因此,要解决这种现象,最好就是在「被动发起方」发送数据前,也就是建立连接之前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手。
所以,TCP 使用三次握手建立连接的最主要原因是防止「历史连接」初始化了连接。
原因二:同步双方初始序列号
TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:
- 接收方可以去除重复的数据;
- 接收方可以根据数据包的序列号按序接收;
- 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道);
可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN
报文的时候,需要服务端回一个 ACK
应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。
四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。
而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
原因三:避免资源浪费
如果只有「两次握手」,当客户端的 SYN
请求连接在网络中阻塞,客户端没有接收到 ACK
报文,就会重新发送 SYN
,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK
确认信号,所以每收到一个 SYN
就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端的 SYN
阻塞了,重复发送多次 SYN
报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN
报文,而造成重复分配资源.
很多人问,两次握手不是也可以根据上下文信息丢弃 syn 历史报文吗?
我这里两次握手是假设「由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK
确认报文,所以每收到一个 SYN
就只能先主动建立一个连接」这个场景。
当然你要实现成类似三次握手那样,根据上下文丢弃 syn 历史报文也是可以的,两次握手没有具体的实现,怎么假设都行。
(b) 四次挥手(重点)
四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。 由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
四次挥手:FIN说我要走,ACK答慢点溜,反向再来一遍,TIME_WAIT防丢
- TIME_WAIT:等待2MSL(像分手冷静期),默认60秒
下面来看看四次挥手的流程图:
中断连接端可以是客户端,也可以是服务器端。 第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。 第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。 第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。 上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况。
具体流程如下图:
为什么连接的时候是三次握手,关闭的时候却是四次握手?
可以三次握手的原因:服务器收到客户端的SYN请求后,可以直接回复SYN+ACK报文
- ACK报文是用来应答的
- SYN报文是用来同步的
所以建立连接只需要三次握手,而断开连接时:
-
客户端 → 服务器:发送FIN(表明客户端数据发送完毕)
-
服务器 → 客户端:回复ACK(确认收到FIN,知道你客户端不发数据了)
- 但服务器可能还要发送数据
-
服务器 → 客户端:发送FIN(表明服务器数据也发完了)
-
客户端 → 服务器:回复ACK(确认断开)
-
连接完全关闭
🔍 核心原因:TCP是全双工协议
- 双向独立关闭:TCP连接的两端(客户端和服务器)可以独立关闭自己的发送通道,而接收通道仍可继续工作。
- 四次挥手的本质:
- 客户端主动关闭发送方向(FIN)
- 服务器确认(ACK),但仍可发送数据
- 服务器关闭自己的发送方向(FIN)
- 客户端最终确认(ACK)
所以第二次挥手和第三次挥手不能合并成一步,如果合并那么就有可能导致服务器没传完就强行关闭了
为什么要等待2MSL?
MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。
💡 核心原因
- 全双工,保关闭,ACK丢了能重传
- 防止服务器没收到ACK,重发FIN时找不到连接(避免连接错乱)。
- 客户端保持
TIME_WAIT
状态,可重发ACK帮服务器正确关闭。
- 旧数据,要清光
- 避免旧连接的延迟数据被新连接误收(相同端口+IP导致脏数据)。
- 等待2MSL让本次连接所有数据包从网络中彻底消失。