【Linux第二十一章】http

前言 🚀

学完 socket 之后,再看 HTTP,很多细节会突然变得顺起来:为什么 HTTP 一定建立在 TCP 之上,为什么请求报文和响应报文都要强调边界,为什么浏览器明明只是在"打开网页",底层却是在按协议交换结构化文本,为什么 GETPOST、状态码、CookieSession 这些概念会一起出现。

HTTP 并不是一个只能传网页的协议,它本质上是一套应用层通信约定。只要客户端和服务端都遵守同一套报文格式,双方就能基于网络传输结构化数据,并在此基础上完成文件访问、表单提交、用户认证和资源跳转等一系列上层业务。

这篇文章就把你这份笔记里的主线重新串起来:先看 HTTP 为什么要存在,再看它的报文结构、请求方法、资源与 Content-Type 的关系,最后再落到状态、CookieSession 与认证机制上。


一. HTTP 本质上是在约定结构化数据怎么传 🧠

协议本质上是一种约定。只要通信双方都遵守同一套格式,就能把"我发出去的内容"准确还原成"你理解到的内容"。

在网络通信中,直接把内存里的结构体原样发送出去,通常并不可靠。原因并不只是"不同 Linux 机器有差异",更关键的是:

  • 不同机器可能存在字节序差异
  • 结构体可能存在内存对齐差异
  • 指针字段本身没有可跨进程、跨主机传输的意义
  • 同一种语言或编译器下的内存布局也未必长期稳定

因此,真正可用于网络传输的,不是"内存布局本身",而是经过协议定义后的字节流。这就引出了两个很重要的概念:

1.1 序列化与反序列化

序列化,就是把业务中的结构化数据,按照协议约定,转换成一段可以在网络上传输的字节流。

反序列化,就是把收到的字节流,按照同样的协议规则重新解析出来,恢复为上层可以继续使用的字段结构。

从这个角度看,HTTP 请求报文和响应报文,本质上也可以视为一种"文本协议形式的序列化结果"。它把方法、路径、版本、首部、正文这些字段按约定组织成报文,让浏览器和服务器都能正确理解。

1.2 HTTP 底层依赖 TCP

HTTP 是应用层协议,它本身不负责丢包重传、按序交付、流量控制这些底层可靠性问题。它把这些能力交给传输层的 TCP 去完成,因此:

HTTP 底层建立在 TCP 之上。

这也是为什么 HTTP 通信时,writesend 只是把数据拷贝到发送缓冲区;至于数据什么时候真正发出去、一次发多少、出错后如何补偿,本质上是由内核里的 TCP 协议栈决定的。

💡 避坑指南:
HTTP 不是"网页专属协议",而是一套建立在 TCP 之上的应用层报文约定。网页只是它最典型的应用场景之一。


二. 为什么 HTTP 一定要强调报文边界 🧱

理解 HTTP,一个特别关键的前提是:TCP 是面向字节流的,不保留应用层消息边界。

这意味着客户端即使连续发出两个请求,服务端在接收时也不一定恰好"一次读到一个完整请求"。它可能出现:

  • 一次只读到半个请求
  • 一次读到一个半请求
  • 一次读到多个请求粘在一起

这就是应用层必须自己解决"报文边界"的原因。

2.1 为什么 UDP 不会天然遇到同样的问题

UDP 面向数据报,一次发送就是一个完整报文,一次接收通常也是一个完整报文,因此边界天然存在。

HTTP 基于 TCP,所以必须自己定义边界规则,否则服务端根本无法判断"当前这段字节流,到底哪里才算一个完整请求"。

2.2 HTTP 是如何确定请求边界的

HTTP 报文之所以容易解析,是因为它把报文结构设计得很清晰:

  • 请求行 / 状态行
  • 多行首部字段
  • 一个空行
  • 可选正文

其中,空行 是请求头与请求体之间最关键的分隔符。也就是说,服务端通常可以先持续读取,直到读到 \r\n\r\n,从而确认请求头已经结束。

接下来如果首部里还带有 Content-Length,就能继续按长度把正文读完。于是,一个完整的 HTTP 报文边界就被确定下来了。


三. HTTP 请求和响应长什么样 🗺️

3.1 请求报文的组成

一个典型的 HTTP 请求报文可以拆成四个部分:

  1. 请求行
  2. 若干请求头
  3. 一个空行
  4. 可选请求正文

请求行一般长这样:

http 复制代码
GET /index.html HTTP/1.1

它包含三部分:

  • 方法:如 GETPOST
  • 资源路径:如 /index.html
  • 协议版本:如 HTTP/1.1

3.2 响应报文的组成

服务端返回的响应报文结构与请求报文类似,也可以分成四部分:

  1. 状态行
  2. 若干响应头
  3. 一个空行
  4. 响应正文

状态行一般长这样:

http 复制代码
HTTP/1.1 200 OK

它包含三部分:

  • 协议版本
  • 状态码
  • 状态描述短语

3.3 版本号到底表示什么

报文里出现的版本号,表示这次通信使用或声明支持的 HTTP 协议版本。实际协商和行为还要结合客户端与服务端的实现能力去看,但至少在报文本身,它承担的是协议语义说明作用。

从学习角度看,可以先抓住三个常见版本:

  • HTTP/1.0:通常默认短连接
  • HTTP/1.1:默认支持持久连接
  • HTTP/2:在连接复用和传输效率上进一步增强

四. 浏览器看到的"网页",本质上只是服务器返回的资源 🔍

很多初学者会下意识觉得:浏览器访问网页,就是服务器"显示页面给我看"。

更准确地说,服务器做的事情其实是:

把某个资源按 HTTP 响应报文的方式返回给浏览器。

至于最终把返回内容解释成网页、图片、样式、脚本还是别的东西,是浏览器根据收到的数据类型去完成的。

4.1 HTTP 不只传网页

HTTP 被称为"超文本传输协议",但它能传输的远不止文本。只要服务端告诉浏览器"这份正文是什么类型",浏览器就能按对应方式解释:

  • text/html:按网页解释
  • text/css:按样式解释
  • application/javascript:按脚本解释
  • image/png:按图片解释
  • application/json:按结构化数据解释

因此,HTTP 更准确的理解方式是:

它负责传输资源;资源的含义由 Content-Type 和浏览器解释能力共同决定。

4.2 后缀与 Content-Type 的关系

笔记里提到"后缀决定文件类型",这个说法在教学上是方便理解的,但更准确地说:

  • 文件后缀通常是资源类型的一个线索
  • 服务端会根据映射关系设置 Content-Type
  • 浏览器再根据 Content-Type 去决定如何解释正文

也就是说,真正参与协议层语义表达的是 Content-Type 响应头。后缀常常只是服务端生成这个头部时的参考依据之一。

4.3 为什么图片不能随便用 getline 读取

图片这类资源通常是二进制数据,而 getline 适合按文本行读取。若用文本方式读取二进制内容,可能会:

  • 提前遇到特殊字节
  • 改变原始内容
  • 读取得不完整

一旦二进制正文被破坏,浏览器自然无法正确渲染图片。因此,处理静态资源时必须区分:

  • 文本资源可以按文本逻辑处理
  • 二进制资源要按二进制方式读取和发送

💡 避坑指南:

浏览器显示网页,并不是服务器"帮你渲染好了页面"。
服务器返回的是资源,浏览器负责解释和渲染。


五. GETPOST:都是请求方法,但语义和传参位置不同 🧩

5.1 GET 一般通过 URL 携带参数

GET 最常见的使用方式,是把参数拼在 URL 后面,通过查询串传递,例如:

http 复制代码
GET /search?wd=http HTTP/1.1

浏览器里的表单如果不显式写 method,默认也是 GET。用户在表单里输入的内容,最终会被浏览器编码后追加到 URL 上。

5.2 POST 一般通过请求正文传参

POST 则更常见于把参数放进请求正文里。请求头里会通过 Content-LengthContent-Type 等字段告诉服务端:

  • 正文有多长
  • 正文采用什么编码格式

5.3 POSTGET 更安全吗

笔记里提到"GET 私密性较差,POST 更好,但都容易抓取,增强安全性需要加密",这个结论方向是对的,但需要说得更准确一些。

GETPOST 的核心差异首先是语义与参数承载位置 ,而不是安全性本身。POST 把参数放在正文里,确实不会直接暴露在地址栏中,但:

  • 明文 HTTP 下,正文和 URL 一样都能被抓包看到
  • 真正提高安全性,依赖的是 HTTPS/TLS 加密
  • 是否敏感,还与日志、浏览器历史、代理缓存等场景有关

所以更严谨的说法是:

POST 只是比 GET 更不容易把参数直接暴露在地址栏,不等于天然安全。真正的安全保障依赖加密。

5.4 /s、表单提交与服务处理

表单提交到某个路径,例如 /s,本质上是浏览器向服务器请求某个资源路径或服务入口。服务端收到请求后,再把参数交给对应业务逻辑处理。

从系统视角看,这不应简单说成"这就是进程间通信"。更准确地说,它是:

  • 浏览器通过 HTTP 把参数发给服务器
  • 服务器进程解析请求
  • 服务器内部再把参数交给对应业务模块或程序处理

六. 状态码不是装饰,而是响应语义的一部分 💻

状态码是 HTTP 响应里非常重要的一部分,它用来告诉客户端:"这次请求处理到了什么结果"。

常见分类如下:

类别 含义
1xx 信息状态码
2xx 成功
3xx 重定向
4xx 客户端错误
5xx 服务端错误

6.1 状态码需要规范使用

状态码本质上属于协议约定的一部分。虽然现实里可能存在"服务端返回码不规范"的情况,但从设计和实现角度看,状态码应该准确表达处理结果,否则会让浏览器、代理、中间件和前端逻辑都难以做出正确反应。

6.2 重定向为什么一定要配合 Location

当服务端返回 3xx 状态码时,通常还要带上 Location 响应头,告诉客户端"接下来应该跳转到哪里"。

例如:

  • 301:永久重定向
  • 302:临时重定向

比如一个旧网站已经迁移到新地址,那么服务端就可以返回 301 + Location,通知浏览器后续访问新地址。这不仅影响当前跳转,也会影响搜索引擎和浏览器缓存对地址的记忆。


七. HTTP 的两个经典特征:无状态、但可以做会话管理 ⚠️

7.1 HTTP 为什么常被说成"无状态"

HTTP 无状态,指的是:

协议本身不会自动记住"上一次请求"和"下一次请求"属于同一个用户会话。

也就是说,单看协议层,每个请求都是相对独立的。服务器不能只靠 HTTP 本身天然知道"这是不是刚才那个已经登录过的用户"。

这里需要顺手澄清一个容易混淆的点:

  • HTTP 底层依赖 TCP
  • TCP 可以保持连接
  • 有连接 不等于有状态会话

所以"HTTP 无状态"说的是应用层身份语义,不是说底层绝对没有 TCP 连接存在。

7.2 浏览器缓存和 HTTP 无状态不是同一件事

浏览器确实可能缓存历史资源,但"缓存"解决的是资源复用与性能问题;"无状态"讨论的是服务器是否天然记住用户身份。这两件事有关联,但不是同一个概念。


很多网站需要根据用户身份区分权限,例如:

  • 是否已经登录
  • 能不能查看某个页面
  • 能不能操作某项资源

HTTP 本身无状态,因此服务器必须借助额外机制,把"这是同一个用户"这件事维持住。Cookie 就是最基础的会话管理手段之一。

服务端在响应里通过 Set-Cookie 告诉浏览器保存一段小数据;浏览器后续访问同一站点、同一路径或满足域规则时,会自动把对应 Cookie 放回请求头中。

常见相关头部有:

  • Set-Cookie
  • Cookie

这样服务端就能根据浏览器带回来的信息,识别这个请求来自谁。

笔记里提到"cookie 是内存级的",这个说法需要修正。更准确地说:

  • 会话 Cookie:浏览器关闭后失效,通常只在当前会话内保留
  • 持久 Cookie:会根据过期时间写入磁盘,在更长时间内有效

所以 Cookie 并不天然只存在内存里,它可以是会话级,也可以是持久化的。

8.4 为什么又要有 Session

如果把用户名、密码、权限这类敏感信息直接放进 Cookie,风险会非常高。更常见的做法是:

  1. 用户登录成功后,服务器端创建一个 session
  2. 服务器为这个 session 生成唯一的 session id
  3. 把这个 session id 放进 Cookie
  4. 客户端后续请求带上该 Cookie
  5. 服务端通过 session id 找到对应会话数据,完成身份认证

这样浏览器保存的通常只是一个标识,而真正敏感的用户信息留在服务端管理。

8.5 Session 的价值与边界

Session 的价值在于:

  • 不把敏感用户数据直接暴露给客户端
  • 可以在服务端集中控制会话有效期
  • 可以结合登录地点、时间、设备异常等策略使会话失效

但也要清楚边界:

  • 如果 session id 被窃取,仍然可能发生会话劫持
  • 因此真正的安全仍要依赖 HTTPS、安全属性配置、过期策略和服务端校验

💡 避坑指南:
Cookie 不是"存用户隐私数据的地方",更常见的是存放一个会话标识。
真正的认证状态通常由服务端 Session 维护。


九. 常见 HTTP Header 应该怎么看 📌

学习 HTTP 时,不要把头字段当成一堆要死记硬背的字符串。更好的方式是把它们按职责来理解。

9.1 描述正文的头

  • Content-Type:正文数据类型
  • Content-Length:正文长度

9.2 描述目标与环境的头

  • Host:当前请求要访问哪个主机、哪个端口
  • User-Agent:客户端环境信息
  • Referer:当前请求是从哪个页面跳转过来的

9.3 参与重定向和会话管理的头

  • Location:告诉客户端下一步跳到哪里
  • Cookie:客户端携带回来的会话小数据
  • Set-Cookie:服务端下发给浏览器保存的数据

总结 📝

HTTP 真正难的地方,不在于背出几种请求方法或几个状态码,而在于把它看成一整套完整的应用层通信规则:它通过文本化、结构化的报文格式,把请求和响应组织起来,再借助 TCP 的可靠传输能力,把资源、参数、状态和认证信息稳定地在客户端与服务端之间流动。

顺着这条主线再回头看这份笔记,很多知识点其实都在解决同一个问题:

  • 为什么不能直接传结构体,而要序列化
  • 为什么 HTTP 报文必须能被正确切分
  • 为什么 GETPOST 不只是"写法不同"
  • 为什么状态码和 Location 要配合
  • 为什么 HTTP 无状态,却还能做登录认证
  • 为什么 CookieSession 总是一起出现

最终可以把 HTTP 归纳成一句话:

HTTP 是建立在 TCP 之上的应用层协议,它用结构化报文完成资源访问、参数传递、状态表达与会话管理。

把这个总框架搭起来之后,后面继续学习 HTTPSCGI、静态资源服务器、前后端交互甚至浏览器缓存机制时,都会自然落到同一条理解主线上。

相关推荐
mhkxbq2 小时前
山东H3C服务器R4700G5等多型号,哪家售后有保障?
运维·服务器
海域云-罗鹏2 小时前
企业部署私有化模型,深圳数据中心服务器托管是关键
运维·服务器
恒创科技HK2 小时前
高防服务器有什么作用?
运维·服务器
王琦03182 小时前
第七章 命令解释器-shell
linux·运维·服务器
刘~浪地球2 小时前
Nginx + Tomcat 整合实战(四):会话管理与共享详解
运维·nginx·tomcat
RisunJan2 小时前
Linux命令-mv(移动或重命名文件和目录)
linux·运维·服务器
wh_xia_jun2 小时前
Windows/Linux 自动适配 + Pydantic Settings 配置
linux·运维·windows
第二只羽毛2 小时前
C++ 高并发内存池4
java·大数据·linux·c++·算法
风吹落叶32572 小时前
RabbitMQ 集群
linux·分布式·rabbitmq