TCP/IP 核心概念详解:从网络分层到连接管理

前言

TCP/IP 是一个协议族,是由一系列协议组成的一个网络分层模型,就是一个四层模型。

在讲它是什么之前,我们先来看看为什么要分层?

为什么要分层

简化版的网络结构图:

在上述结构图中,客户端和服务器之间会有很多中间节点,这些节点可能是交换机、路由器或网关。

在理想情况下,客户端往服务器发送一个 HTTP 请求,会有多种路径,而这种路径的选择就叫做"路由"。但由于网络的不稳定性,网络传输的成功率不是也不可能是 100%,所以需要分层。

为什么呢?我来给你解释一下。

请求在传输的过程中,可能因某个节点导致消息的丢失,因此需要一种重传机制:让接收方在收到消息后发送一个回执,表示消息已收到。如果发送方到了指定的超时时间未收到该回执,就认为消息丢失,去重传一次消息。即使接收方已发送回执,而发送方未收到,也不用担心,重复发送消息,并不会导致接收方重复处理,接收方会根据记录识别出重复的消息,并主动抛弃它。

在重传机制中,传输的数据可能会比较大,可能会出现传输时间长、传输失败的概率高等问题。解决办法就是对数据进行分块,这样当某块数据传输失败了,只需单独重传该数据块即可,这样可以降低平均的传输时间,传输失败的概率也更低。

由于有了分块传输,就需要进行分层。因为应用层的协议不只有 HTTP,还有 FTP(传文件)、SMTP(邮件)、DNS(域名服务)。

这些协议都有分块传输的需求,所以需要把分块传输这个通用的功能抽出一层,让这一层只做分块传输:拆分数据块进行分发,接收数据块进行拼装。

此时应用层就不用去分块了,而是只需将要传输的数据交给下层,让它来做这件事;拼装数据也是由这一层来完成,应用层只需接收完整的数据。

数据的补发以及发送失败的回执都是由这一层完成,而这一层叫传输层。TCP(Transmission Control Protocol)传输控制协议是其中的一个协议,它能够保证数据的稳定传输。

有了传输层后,为什么还需要分层?

因为有些场景下,数据并不需要重传。比如游戏数据,在你掉线并重连后,你和其他人是不关心你在掉线时的数据的,只需知道你最新的数据(游戏状态)即可。又比如网络直播,因其实时性,在主播掉线并重连后,我们是不需要知道主播掉线后的画面的,只要知道最新的画面即可。

将数据发送出去,无需确认的这种策略叫 UDP(User Datagram Protocol)用户数据报协议,这是传输层的另一个协议。

TCP 和 UDP 都有一个共同的下层网络需求:都需要让一个数据从一个主机到达另一个主机,所以需要再抽取一层。

此时,传输层不再直接传输数据,只负责拆分、拼装数据,将单个数据块交给下层来完成发送,而这一层叫作网络层。IP 协议就位于此层,负责以最小单位来发送和接收网络数据,以及寻址、路由等工作。

再来梳理一遍数据传输的过程:

有一个 HTTP 请求报文需要从客户端发送到服务器,应用层会将发送报文的任务交给传输层,TCP 会将报文进行拆分,然后将每一份数据块交给网络层,IP 去完成单个数据块的发送,IP 并不需要确保数据发送成功。

等数据到了服务器,网络层会把接收到的数据包逐个交给传输层。传输层在收到某个数据块后,会准备回执发回客户端。如果客户端的传输层在一定时间内没有收到某个数据块的回执,就会执行重传。当服务器的传输层确认接收到了所有完整的数据块后,就会将它们拼装成一条完整报文,发给应用层。

应用层收到消息,就会准备响应报文给客户端... 至此,一次单向数据传输的过程就结束了。

你可能还会疑问,TCP/IP 不应该是有 4 层吗?

其实还有一层,叫做数据链路层,就是以太网、Wi-Fi等。这一层用于给网络提供现实世界中的物理基础和一些规则。

现在我们使用的双绞线是某种网络的底层协议,使用这种网线连接成的网络就叫做以太网。

TCP 连接

什么是连接

连接指的其实就是 TCP 连接,TCP 是一种有状态的连接。双方进行消息交换时,并不需要指明身份,因为在连接时,身份信息就已经保存了。而让通信的双方认识(交换身份信息)的过程就叫做双方的连接。建立连接是为了让双方认识对方并进行通信,不将对方的消息丢弃。

TCP 中有端口的概念,那么它是什么呢?

就是你电脑用于网络通信的端点。比如一次连接中,会有主机 A 的某个端口与主机 B 的某个端口建立连接。在 Java 中,我们通常会使用 Socket (套接字) 来使用端口,你可以把它想象成是一个专用于网络通信的插座,确保数据能在两端间传输。

我们再简单说说 TCP 连接的建立和关闭:

TCP 连接的建立

上图就是"三次握手"的过程:

主动连接方 A 发送 TCP 消息给被连接方 B,表示想要与 B 建立连接。B 收到后会返回一个消息,表示"我知道了,并且我也要和你建立连接"。然后 A 会再发送一条消息给 B,表示"好的,我知道你想要和我建立连接了"。

至此,双方的连接就成功建立了。

TCP 连接的关闭

连接之所以需要关闭,主要是为了节省资源。因为每次建立连接不仅需要在内存中保存对方的信息,还要持续占用主机的端口。

上图就是"四次挥手"的过程:

连接方 A 会先和连接方 B 说"我没有消息要发送了"。B 收到后,会先和 A "我知道了"(这是第二次挥手)。然后,B 会检查自己是否还有数据要发送,等到所有数据都发送完毕后,B 会再发送一条消息给 A 说"我也没有消息要发送了"(这是第三次挥手)。最后,A 会和 B 说"好的,我知道了"(第四次挥手)。

经过这个过程,连接就关闭了。

这里 B 需要回复两次是因为,它在收到 A 的关闭请求时,B 可能有数据还未发完,所以需要先回应 A 的请求,等到数据全发完后,再正式通知 A "可以关闭了"。

长连接

长连接就是连接一直不关闭。

因为理想情况下,所有人的主机都可以互相直连,但实际上,由于内网的存在,有一部分人并不能直接连接外部网络,而是需要通过某个服务器的端口来连接外网,让它帮你转发数据。就比如你手机的数据网络,它就处于内网中,你每次使用时,运营商的网关都会给你随机分配一个内网 IP。

然而,中间的网关为了节省资源,会将长时间不活动的连接关闭。但有时,你需要持续接收消息,需要保证连接不断开,这时就需要用到长连接。

实现长连接的方式叫做心跳,也就是每隔一段时间,就使用 TCP 连接发送一些无意义的短消息给对方,这样网关就不会认为你是空闲连接了。

相关推荐
xiaoshiquan120625 分钟前
as强制过滤指定依赖版本库,解决该依赖不同版本冲突
android
2501_929157682 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户094 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户094 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户094 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º7 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot
非专业程序员Ping14 小时前
HarfBuzz概览
android·ios·swift·font
Jeled15 小时前
「高级 Android 架构师成长路线」的第 1 阶段 —— 强化体系与架构思维(Clean Architecture 实战)
android·kotlin·android studio·1024程序员节
明道源码17 小时前
Kotlin 控制流、函数、Lambda、高阶函数
android·开发语言·kotlin
消失的旧时光-194319 小时前
Kotlin × Gson:为什么遍历 JsonObject 要用 entrySet()
android·kotlin·数据处理·1024程序员节