从 Socket 到 HTTP:长连接与短连接的本质
编解码、正/反序列化、协议
计算机只认二进制的本质是计算机只认电的状态(或者磁极)。
我在其他文章中讲过一个理论,计算机硬件对于数据的处理,本质就是对电的状态的判断,比如,高电平/低电平、有电/没电。我们在逻辑上会将这种状态的划分称作 0 和 1,也就是所谓的二进制。
编码
当需要将数据交给计算机硬件时(传输、存储等),必须将数据转化为硬件认识的电信号(转为二进制),通过编码规则将数据转换为二进制的过程就叫编码。好的编码规则可以压缩存储空间。
编码还有一种更广泛的理解:任意两种数据格式之间的转换。比如 Base64 编码,就是将二进制流格式转换成 ASCII 格式,但是其本质也是改变二进制在内存中的存放规则。
解码
按照同样的编码规则,将已经编码后的二进制逆向为原来的格式,这个过程就称为解码。
正/反序列化
序列化是编码在特殊场景下的特殊叫法,序列化更强调操作的数据是对象类型。
把 对象类型 按照序列化规则转换为 二进制 的过程,一般称为序列化,反过来就是反序列化。
例如 Java 序列化,就是将 对象 按照 Java 提供的序列化规则 转换为二进制。还有相对复杂一点的序列化,如 JSON 序列化,先将 对象 转换 为 JSON 格式字符串,然后将字符串以 UTF-8 编码规则存储于设备中。
协议
协议用来定义通信时的规约。比如 内容格式、字段数量、每个字段的长度(解决粘包问题)、谁先发、何时关闭、异常如何处理等。
当双方定好规约后,发送时按照规约发送内容,接收时按照规约进行拆解。
假设没有协议,接收方收到一大串二进制后,根本无从下手,无法解析。
Socket
数据在网络中传输时会经过 OSI 5 层模型(物理层、数据链路层、网络层、传输层、应用层):
发送方 接收方
┌─────────┐ ┌─────────┐
│ 应用层 │ │ 应用层 │
└────┬────┘ └─────────┘
│ 数据 ▲
▼ │ 数据
┌─────────┐ ┌─────────┐
│ 传输层 │ 将数据用传输层协议封装 │ 传输层 │ 对传输层协议拆包
└────┬────┘ └─────────┘
│ 封装后的数据 ▲
▼ │ 拆分出的被传输层协议封装的数据
┌─────────┐ ┌─────────┐
│ 网络层 │ 将数据用网络层协议封装 │ 网络层 │ 对网络层协议拆包
└────┬────┘ └─────────┘
│ 封装后的数据 ▲
▼ │ 拆分出的被网络层协议封装的数据
┌─────────┐ ┌─────────┐
│ 链路层 │ 将数据用链路层协议封装 │ 链路层 │ 对链路层协议拆包
└────┬────┘ └─────────┘
│ 封装后的数据 ▲
▼ │ 收到的被链路层协议封装的数据
┌─────────┐ ┌─────────┐
│ 物理层 │ ────→ 网线/光纤 ────→ │ 物理层 │
└─────────┘ └─────────┘
整个流程中,物理层、数据链路层、网络层、传输层 由物理设备、硬件、操作系统内核各自实现,
应用层由应用程序自己实现,一般用来自定义协议,应用层常见的开源协议太多了,HTTP、MQTT、SSE、FTP、SSH、SMTP 等。
常规情况下(基于操作系统开发),应用层只能和传输层通信,无法和其他层级跨级交互。操作系统专门提供了一套 API,让应用层可以向传输层写入数据,或从传输层读取数据到应用层,这套 API 就称为 Socket。
长连接
传输层的 TCP 协议,天生就是长连接,这是理解一切的起点。
TCP Socket 从建立那一刻起,就是一条长期活着的通道。
建立连接 → 保持连接 → 直到某一方主动 close()。
长连接的存活时间
长连接不是无限存活的,当操作系统检测到连接内长时间没有流量时(Linux 默认两个小时),会发送一次 KeepAlive 探测包,当 探测包能顺利发送时,操作系统会重置等待时长,当探测包不能顺利发送时,操作系统内核会调用 close 断开 Socket 连接。
有些系统版本,KeepAlive 探测包是默认关闭的,但长连接也不是无限存活,当连接长时间没有数据时,运营商也会断开连接,但是又运营商断开的连接,不是通过 close 方法,所以服务器和客户端无法感知。数据时,运营商也会断开连接,但是又运营商断开的连接,不是通过 close 方法,所以服务器和客户端无法感知。
短连接
短连接是由应用层协议(传输层必须使用 TCP)决定的。本质就是应用层协议中,约定一个字段。当服务器收到数据,并解析协议后,发现这个字段满足短连接条件时,服务器会在响应后直接调用 close 关闭连接。
一般短连接都是由服务器侧关闭,因为服务器响应后就属于完成了一次交互,服务器直接调用 close 关闭连接即可。TCP 是可靠性传输,有 ACK 确认,所以即使服务器响应后立刻调用 close 方法,也不会出现客户端未收到响应数据,连接就被断开的情况。
HTTP 是长连接还是短连接
HTTP 是一个典型的支持短连接的协议。其规定在请求头中,如果 Connection: keep-alive 就是长连接,如果 Connection: close 就是短连接。
当服务器收到数据后,就可以使用 HTTP 协议拆包,然后判断请求头中的 Connection 的值,如果是 close,则在响应后,直接调用关闭连接即可。
如果是应用程序自己写 Socket 部分,则需要自己判断。如果是直接使用 Web 服务器产品(Tomcat、Nginx 等)时,则无需理会,因为 Web 服务器会帮我们判断。
HTTP 是长连接还是短连接
前面已经知道 HTTP 长/短连接的原理了,所以理解 HTTP 是长连接还是短连接 就更容易。
HTTP 在 1.0 版本时,默认是短连接,即 Connection: close。
HTTP 在 1.1 版本时,默认是长连接,即 Connection: keep-alive。
为什么有些博文说 HTTP 1.1 在短期内大量请求时是长连接,长时间不请求时会退化成短连接?
其实其他博文没有讲本质。HTTP 协议还规定了一个请求头 Keep-Alive: timeout=5。这个请求头就是长连接的空闲存活时间。
前面讲过 TCP 长连接的存活时间,其实一个道理。当连接的空闲时间超过 timeout 时,服务器就会断开连接。
之所以说 HTTP 1.1 在短期内大量请求时是长连接,就是因为处于 timeout 时间内,当超出 timeout 仍没有请求,服务器就会关闭连接,看起来就像是短连接一样。

3万+

被折叠的 条评论
为什么被折叠?



