📚 本文收录于「流浪」的系列专栏
| 🐧Linux系统 | ⚙️C++ |
| 📊数据结构与算法 | 🐍Python |
| 🔗LangChain & LangGraph | 🗄️MySQL 数据库 |
| 🌿Git 工具 | 🌐计算机网络 |
| 🤖LLM | 💯大厂面试、八股 |
| 📚学习筑基专栏 |
🏠 博客主页:流浪 | 📝 原创首发于 CSDN
前言:《计算机网络篇8》说清了序列化与字节流的边界,边界归应用层自己切。真实的应用层协议长什么样?
HTTP就是最标准的样本:URL 怎么定位到文件、报文排成哪四段、空行凭什么分开报头和正文,这一篇跟着一次点击走完。
一、HTTP 是应用层上的文本协议,报文靠约定好的字符串拼出来
1.1 HTTP 站在应用层,一次请求就是一条 TCP 连接上的字节流
1. HTTP 只规定报文长什么样,怎么送到由下面的 TCP 负责
- HTTP 全称 HyperText Transfer Protocol,工作在应用层,下面依次是 TCP 和 IP,这个位置把两件事分开了:报文格式归 HTTP,可靠传输归 TCP。
- 请求发出之前,浏览器先要和服务器建 TCP 连接,这一步就是《计算机网络篇7:TCP 网络编程常用接口与调用流程》里客户端 socket、connect 那一套,连不上就谈不上发请求。
- 连上之后浏览器把 HTTP 字节写进连接,服务器读出来按同样格式解析,再把响应写回去。
2. 点一下超链接,背后是一次 socket 通信
- 我们日常见到的页面上那个蓝色链接本质是浏览器里的一条记录,存着目标资源的 URL,点下去不是「打开网页」,而是发起一次新的请求。
- 一次请求对应一次连接上的收发,收发靠 socket 接口,这条链从用户点击一直落到内核的收发缓冲区:
- 浏览器解析
href(超文本引用),拿到 URL - 建连接或复用连接,把请求字节写进发送缓冲区
- 服务器读字节、解析、回响应
- 浏览器解析
HTTP 管「报文长什么样」,TCP 管「字节怎么可靠送到」,两者叠加才凑成一次完整的网页访问。
1.2 HTTP 的两面:为什么重要,以及它传的是超文本
1. 重要性在于它是所有 Web 交互的公共语言
- 浏览器地址栏回车、APP 下拉刷新、后端服务调接口,走的都是 HTTP,它是覆盖面最广的应用层协议。
- 覆盖面广的直接后果是:不管写 C++、Java 还是 Python,只要想和别人的服务对话,最后都要拼一段 HTTP 报文或解析一段 HTTP 报文。
2. 超文本传输指的是正文里能挂指向其他资源的链接
- 超文本(HyperText)的重心在「超」:一段文字里可以嵌一个指向别的资源的引用,顺着引用就能跳过去。
- HTML 就是最常见的超文本,文件本身只是纯字符,但标签里写进了图片、视频、样式表、脚本的地址,浏览器解析到标签再去拉对应资源。
- 所以 HTTP 传的不只是文字,图片、音频、视频在它眼里都是「一段字节加一个类型标记」。
HTTP 的重要性来自它是 Web 的公共语言,超文本传输则解释了为什么一次页面访问会牵出几十个请求。
1.3 HTTP 的结构靠特殊字符串划区,序列化就是字符串拼接
1. 空格、CRLF、空行三样东西把报文切成段
- 第一行用空格分成三段,段与段之间是单个空格,行尾是回车加换行(CRLF,即
\r\n)。 - 后面每行是一个「字段名: 字段值」,同样以 CRLF 结尾,字段可以有很多行,顺序不影响语义。
- 字段写完后补一个单独的空行,也就是连续两个 CRLF,它宣告头部结束,后面才是正文。
2. 不依赖第三方库就能拼能拆,代价是冗余
- 《计算机网络篇8》讲过序列化是把结构化字段拼成一串字节,HTTP 用的是最朴素的拼法:字符串直接拼,字段之间用 CRLF 分开,不需要任何框架。
- 好处是任何语言几十行就能拼出合法请求,接收方也只要一个扫行尾的状态机就能拆开。
- 代价是体积和效率:整数 65535 在二进制里占两字节,写成文本要五字节,头部字段名还要在每个请求里重复。
HTTP 的序列化没有魔法,就是按约定把字段拼成 ASCII 字符串,CRLF 分行、空行分段,前提是双方都认这套字符约定。
1.4 https 在 http 外面套了一层加密,端口也从 80 换成 443
1. https 等于 http 加 TLS,明文变密文
- HTTPS 不是另一套报文格式,请求行、头部、正文一个字都没改,只是在 HTTP 和 TCP 之间插了 TLS,把整段字节加密后再交给 TCP。
- 加密之后,中间的路由器、代理、同一个 Wi-Fi 下的其他人抓到的都是密文,看不到请求路径和提交内容。
2. 80 与 443 只是默认值,不是两套协议
- HTTP 默认 80,HTTPS 默认 443,浏览器看协议名自动补端口,端口也可以改,改了必须在 URL 里显式写出来。
- 服务器同时开 80 和 443 很常见,80 一般只做一件事:把请求重定向到 443,让用户始终走加密通道。
https 是 http 报文外套了一层 TLS,格式没变,变的是传输通道和默认端口。
二、一条 URL 定位到全网唯一文件:域名解析成 IP,端口落到进程,路径落到文件
2.1 URL 由协议、主机、端口、路径四段组成
1. 一条 URL 拆开就是四个字段
- 完整形态是
scheme://host:port/path?query#fragment,从左到右是协议、主机、端口、路径,后面可带查询串和片段标识。 //右边第一段是主机,写域名是给人看的名字,写 IP 是给机器直接用的地址;冒号后是端口,省略按协议补默认值。- 再往后的
/a/b/c.html是路径,?k=v是查询串,井号后面是片段标识,片段只在浏览器内部用,不发给服务器。
2. 超链接是页面上的跳转元素,URL 是它指向的地址
- 超链接是 HTML 里的元素,形如
<a href="...">文字</a>,href里放的就是一条 URL,点它等于让浏览器访问这个地址。 - URL 不一定在超链接里,地址栏输入、接口文档、
curl后面跟的都是 URL。 - URI 是统一资源标识符的统称,URL 是其中带定位方式的一类,日常说的「网址」都是 URL。
URL 的四段各解决一件事:协议决定怎么通信,主机决定找哪台机器,端口决定找哪个进程,路径决定要哪个文件。
2.2 域名是给人用的名字,最终仍要换成 IP 才能路由
1. IP 难记还会变,域名把变化挡在人后面
- 点分十进制对人极不友好,记十个网站就到极限,服务器换机房时 IP 会变,让人跟着改地址不现实。
- 域名是一层人名化的中间层,用户只记
www.douyin.com,对应 IP 由 DNS 查,服务器换地址只改解析记录。 - 域名不参与路由,路由器只认 IP,所以域名再好看,最终还是要解析成一个 IP 才能把包发出去。
2. 入口从记域名走到搜关键词,域名的重要性被搜索引擎分走
- 早期上网的入口是「记住网址」,书签和门户网站的网址目录承担导航职能,域名短好记就占优势。
- 后来入口变成「搜关键词」,用户不必知道域名,搜索引擎把「记地址」接了过去,国内早期做网络实名的周鸿祎和做搜索的李彦宏,抢的都是这个入口。
- 移动互联网又把入口挪进 APP 内部搜索框,域名从用户眼前消失,只在开发和运维层面存在。
域名解决的是人的记忆问题,不是机器的路由问题,最终一定落到 IP 上。
2.3 DNS 把域名解析成 IP,浏览器拿到 IP 才发起连接
1. 解析先看本机缓存,没有再问 DNS 服务器
- 浏览器有 DNS 缓存,操作系统也有,hosts 文件还能手动写死映射,命中任何一层都不必往外问。
- 都没命中时请求发给本地 DNS 服务器,由它替客户端去问根、顶级、权威域名服务器,一层层问到结果,这叫递归查询。
- 结果带生存时间(TTL),有效期内复用,过期重查,所以改解析不能立刻全网生效。
2. 拿到 IP 之后紧接着就是 connect
- 解析出 IP 只是知道「找哪台机器」,接下来创建 socket、把 IP 和端口填进地址结构、调用 connect 发起三次握手,和《计算机网络篇7》的客户端流程一致。
- 连接建立后才轮到 HTTP 报文出场,报文写进这条连接的发送缓冲区,由 TCP 负责切段、重传、按序交付。
DNS 只负责把名字换成地址,换完就退场,后面的连接、收发、重传全是 TCP 和 HTTP 的事。
2.4 端口是协议约定俗成的门牌号,知名端口固定不动
1. http 80、https 443、ssh 22 都是约定
- 一台主机上跑着很多进程,IP 只能定位到主机,定位到具体进程要靠端口,端口号写在 TCP 头里,内核据此把数据交给对应 socket。
- 知名服务端口是约定好的:HTTP 80、HTTPS 443、SSH 22、DNS 53、SMTP 25,客户端不写端口就按协议默认值补。
- 这套约定登记在 IANA 的端口表里,改了端口所有调用方都得跟着改,实际部署中只有内网服务才随便挑。
2. 0 到 1023 是特权端口,绑定要 root 权限
- 0 到 1023 属于系统保留区间,普通进程不能直接绑定,Linux 上要求具备 CAP_NET_BIND_SERVICE 能力,root 默认具备。
- 这条限制防的是普通用户伪装系统服务:如果谁都能绑 80,本地一个恶意程序就能顶掉真正的 Web 服务器,把请求全接走。
- 常见做法是让 Nginx 以 root 启动绑定 80 和 443,再把工作进程降权到普通用户。
端口是主机内部的分发依据,知名端口靠约定统一,特权端口靠权限兜底。
2.5 路径指向服务器上的一个文件,上网的本质是 IO
1. 资源就是文件,网页、图片、视频都不例外
- 从 HTTP 角度看,「资源」不是抽象概念,服务器返回的每个东西最终都来自磁盘上的一个文件,或者程序动态生成的一段字节。
- 上网这件事拆开只有两个方向:把远端数据取到本地(浏览、看视频、下载),把本地数据送到远端(发帖、上传、提交表单),两个方向都是 IO。
2. 三段合起来才定位到全网唯一的文件
| 组成 | 对应 | 解决的问题 |
|---|---|---|
| http(s) | 端口(80 / 443) | 定位到主机上的哪个进程 |
| 域名 | IP | 定位到全网哪一台主机 |
| 路径 | 一个文件路径 | 定位到该主机上的哪个文件 |
缺一段就定位不到唯一文件:只有域名不知道找哪个服务,只有端口不知道找哪台机器,只有路径不知道去哪个目录取。
一条 URL 能定位到全网唯一的文件,是因为它把「哪台机器、哪个进程、哪个文件」按顺序答完了。
三、HTTP 报文分四段,空行把报头和正文分开
3.1 请求报文四段:请求行、请求头、空行、请求体
1. 请求行三段:方法、目标路径、协议版本
- 第一段是请求方法,常见的有 GET、POST、PUT、DELETE、HEAD;第二段是请求目标,通常就是 URL 的路径;第三段是协议版本。
- 版本号不是装饰,它决定对端按哪套规则解析:HTTP/1.1 默认复用连接,HTTP/1.0 默认一次一断。
2. 请求头一行一个字段,Host 和 Content-Length 最常用
Host指定要访问的站点域名,同一台服务器上可能挂很多网站,服务器靠它区分请求的是哪一个。Content-Type说明正文格式,如application/json;Content-Length说明正文有多少字节。- 此外还有
User-Agent、Cookie、Accept、Connection等字段,名字大小写不敏感,顺序也不影响语义。
3. 请求体不是每次都有,GET 一般不带
- GET 的语义是「取」,参数放在查询串里,报文通常到空行就结束,后面没有正文。
- POST 的语义是「提交」,数据放在请求体里,长度由
Content-Length声明。 - 带不带正文和有没有空行是两件事:没有正文,空行也不能省,解析器靠它确认头部结束。
请求报文的四段是一条单向流水:请求行说清要什么,头部补充说明,空行划界,正文承载数据。
3.2 响应报文四段:状态行、响应头、空行、响应体
1. 状态行三段:版本、状态码、原因短语
- 第一行形如
HTTP/1.1 200 OK,依次是协议版本、三位状态码、原因短语,原因短语只给人看,程序判断结果看状态码。 - 状态码第一位决定大类,后两位是具体含义,服务器可以自定义后两位,大类不能乱用。
2. 状态码按首位分五类,常用的只有几个
| 类别 | 含义 | 常见取值 |
|---|---|---|
| 1xx | 信息性,请求已收到,继续处理 | 100、101 |
| 2xx | 成功 | 200、204、206 |
| 3xx | 重定向,需要进一步动作 | 301、302、304 |
| 4xx | 客户端错误 | 400、403、404 |
| 5xx | 服务端错误 | 500、502、503 |
日常真正要记的是五个:200 正常、301 永久重定向、302 临时重定向、404 资源不存在、500 服务器内部错误。
3. 响应头告诉浏览器怎么处理正文
Content-Type决定浏览器怎么渲染:text/html当网页解析,image/png当图片显示,application/json交给脚本处理。Location配合 3xx 使用,指出接下来跳转到哪个地址;301 表示永久搬走,302 表示临时换地址,语义不同,缓存行为也不同。
响应报文是请求报文的镜像,状态行替代了请求行,其余三段一一对应。
3.3 空行只负责报头结束,正文多长由 Content-Length 决定
1. 空行是分隔符,不是长度声明
- 解析器的动作很机械:读第一行,再一行行读头部字段,读到空行就认为头部结束,剩下的字节都算正文。
- 空行不携带长度信息,它只回答「头部到哪里为止」,不回答「正文到哪里为止」。
- 请求里没有正文时空行照样要写:不写,服务器就不知道头部有没有结束,会一直等下一行。
2. 没有 Content-Length 时,靠连接关闭或分块编码定界
- 最常见的是
Content-Length声明字节数,收满这么多字节就是一条完整报文,多出来的属于下一条。 - 长度事先不知道时用分块传输编码(Transfer-Encoding: chunked),每块前面写十六进制长度,最后用一个长度为 0 的块收尾。
- 还有一种古老方式:不声明长度,靠服务器关闭连接表示结束,它只为兼容 HTTP/1.0 保留,无法区分正常结束和中途断线。
Content-Length与Transfer-Encoding不允许同时出现在一条合法报文里- 同时收到时以分块编码为准,并按错误处理,防止请求走私
3. HTTP 自己就是应用层切边界的实例
- 《计算机网络篇8》的结论是 TCP 不保留报文边界,边界必须由应用层自己划,HTTP 是最标准的落地:头部靠空行切,正文靠长度字段切。
- 一条 HTTP 报文被 TCP 拆成几段送达,接收方拼回来之后仍要靠空行和长度字段重新找出边界,否则一样解析错位;所以「粘包」在 HTTP 里不成为问题,协议自带定界手段,自己写私有协议不带这些手段才会掉坑。
空行解决头部边界,长度字段解决正文边界,两把尺子合起来才让一条报文在字节流里可被还原。
3.4 文本格式让人能读机器好解析,代价是头部冗余
1. 抓包就能看懂,调试成本低
- 用
nc连上服务器,手打三行就能发出合法请求,不需要任何工具库,这种可读性是二进制协议给不了的。 - 抓包时明文直接显示,请求方法、路径、头部字段一目了然,排查「为什么返回 404」非常快。
- 浏览器开发者工具的网络面板,展示的就是这套报文的解析结果。
2. 二进制要额外编码,头部也有冗余
- 文本协议只能传可见字符,图片、音频要走 multipart 或 Base64,后者让体积膨胀约三分之一;头部字段名每个请求重复传,HTTP/2 用头部压缩(HPACK)来治。
- 解析要逐字节扫描、数字要从字符串转回整数,比按固定偏移读二进制慢,这是可读性的对价。
选文本是用解析开销换可读性和通用性,这个取舍在 Web 场景划算,在极致吞吐场景未必。
四、请求路径落到磁盘:web 根目录、默认首页、路径拼接
4.1/是 web 根目录,不是 Linux 根目录
1. 服务器给每个站点划一个目录当根
- URL 开头那个斜杠容易被当成 Linux 的根目录
/,实际上它是 Web 服务器给这个站点指定的站点根目录,通常叫 web 根目录。 - Nginx 里用
root指令指定,自己写的服务器里就是一个字符串变量,比如webroot = "/wwwroot",对外的资源都放在这棵子树下。 - 站点根目录与系统根目录分离,等于把「能对外访问的范围」锁在一个目录里。
2. 不在这个目录下的文件,URL 再怎么写也访问不到
- 服务器收到
/a/b/c.html,会把它拼到 web 根目录后面去找,找不到就返回 404,不会跑去系统根目录找。 - 图片、样式表、脚本、视频遵循同一规则,必须真实存在于 web 根目录的某条路径下,才可能被 URL 命中。
- 所以「部署网页」本质就是把文件放进 web 根目录,再让服务器指向它。
URL 里的
/是站点的边界,不是文件系统的边界,这条区分决定了哪些文件能被外面看见。
4.2 真实路径等于 webroot 加上请求路径
1. 拼接公式只有一行
- 服务器把请求行第二段接到 web 根目录后面:
real_path = webroot + url_path。 - 这里没有映射表,路径就是目录层级,
/a/b/c.html对应webroot/a/b/c.html,一层不多一层不少。 - 这也是静态站点的目录结构经常和 URL 结构一致的原因:部署时怎么放,访问时就怎么拼。
2. 一个具体请求走一遍拼接
- 客户端请求
GET /a/b/c.html HTTP/1.1,假设服务器配置webroot = "/wwwroot"。 - 拼出的真实路径是
/wwwroot/a/b/c.html,服务器打开这个文件,把内容读出来作为响应正文。 - 如果服务器监听 80,完整地址就是
http://主机:80/a/b/c.html,端口省略时浏览器自动补 80。
静态资源的访问没有黑魔法,URL 路径和磁盘路径之间就是一次字符串拼接。
4.3 不带路径时补默认首页 index.html 或 index.htm
1. 首页是服务器约定,不是协议规定
- 浏览器请求
http://example.com/时路径只有一个/,服务器得自己决定给什么。 - 约定俗成的做法是找默认首页,优先
index.html,没有就找index.htm,再没有就返回目录列表或 403。这个约定不在 HTTP 规范里,不同服务器可以配不同的默认文件名。
2. 补默认路径就是把 webroot、斜杠、index.html 拼起来
- 代码里通常准备
homepage = "/index.html",请求路径是/时用首页路径替换它。 - 拼接结果是
webroot + homepage,即/wwwroot/index.html,效果与直接请求这个文件完全一样。 - 有的服务器先检查文件是否存在再决定是否回退,顺序错了会出现「明明有首页却返回 404」。
constchar*homepage="/index.html";// 默认首页if(strcmp(url_path,"/")==0){// 请求根路径时替换url_path=homepage;}// /wwwroot + /index.html => /wwwroot/index.html默认首页是服务器替用户补齐的那一段路径,补完之后仍走同一条拼接逻辑。
4.4 拼路径前必须挡住../,否则能读到站点外的文件
1. 目录穿越就是把路径逃出 webroot
- 直接把用户给的路径拼上去,请求
/../../etc/passwd拼完就是/wwwroot/../../etc/passwd,..一层层往上跳,最终逃出 web 根目录。 - 这类问题叫目录穿越(Path Traversal),是 Web 服务器最经典的漏洞之一,后果是配置文件和密码文件都可能被读走。
- 只过滤字符串里的
..不够,编码后的%2e%2e、多重斜杠、符号链接都能绕过简单检查。
2. 规范化之后校验前缀
- 正确做法是先做路径规范化(realpath 一类函数),把
..、.、多余斜杠全部消掉,得到绝对路径。 - 再检查这个绝对路径是否以 web 根目录为前缀,不是就直接返回 403,不再打开文件。
- 校验要在打开文件之前做,顺序反了等于没做;缓冲区也要限长。
落到代码就两步:realpath规范化得到绝对路径,strncmp和 web 根目录比对前缀,比对失败直接 403。
路径拼接的安全前提是「先规范化、再校验前缀」,只做字符串替换挡不住目录穿越。
4.5 一段最小服务器骨架把前面四章串起来
1. http_mini_server 的调用顺序
- 服务端骨架沿用篇 7 的四步:socket 创建套接字、bind 绑定地址、listen 置为监听、accept 取出连接。
- 拿到连接 fd 后 read 一次,读到的就是请求报文的字节流,先按 CRLF 切行,第一行取出方法、路径、版本。
- 然后拼路径、读文件、拼响应头、写回连接。
2. 解析首行、拼路径、读文件、写响应
// http_mini_server:最小 HTTP 服务器骨架#defineWEBROOT"/wwwroot"#defineHOMEPAGE"/index.html"intlfd=socket(AF_INET,SOCK_STREAM,0);// 1. 创建套接字// bind / listen 省略,与 TCP 服务端一致while(1){intcfd=accept(lfd,NULL,NULL);// 2. 取出连接charreq[4096]={0};read(cfd,req,sizeof(req)-1);// 3. 读请求报文charmethod[16],path[1024],version[16];sscanf(req,"%15s %1023s %15s",method,path,version);// 4. 拆请求行if(strcmp(path,"/")==0)// 5. 补默认首页strcpy(path,HOMEPAGE);charreal[2048];snprintf(real,sizeof(real),"%s%s",WEBROOT,path);// 6. 拼真实路径intfd=open(real,O_RDONLY);// 7. 打开文件if(fd<0){constchar*resp="HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";write(cfd,resp,strlen(resp));// 状态行 + 头 + 空行}else{charbody[8192];intn=read(fd,body,sizeof(body));charhead[256];inthn=snprintf(head,sizeof(head),"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n""Content-Length: %d\r\n\r\n",n);// 空行分隔头与正文write(cfd,head,hn);write(cfd,body,n);// 8. 写正文close(fd);}close(cfd);}这段代码把前三章串了起来:sscanf拆的是请求行,snprintf拼的是真实路径,响应头末尾那两个\r\n就是分隔报头和正文的空行。
看懂这段骨架,等于把「一条 URL 怎么变成一个文件」的整条链路走了一遍。
五、全篇总结
1. HTTP 是文本协议,靠约定的字符串划出结构
- 请求行、头部、空行、正文四段,用空格和 CRLF 分区,不依赖序列化库。
- 文本换来可读性和通用性,代价是头部冗余和解析开销,https 只是外面套了 TLS。
2. URL 用三段答完「哪台机器、哪个进程、哪个文件」
- 域名解析成 IP 解决找哪台主机,端口定位到进程,http 80、https 443、ssh 22,0 到 1023 要特权。
- 路径对应 web 根目录下的文件,上网的本质就是取数据和送数据两类 IO。
3. 空行切头部,长度字段切正文
- 空行只宣告头部结束,不携带长度;正文靠 Content-Length,未知长度用分块编码,最后一块长度为零。
- 这正是篇 8 说的「边界由应用层自己切」,HTTP 自带两把尺子。
4. 服务器把 URL 路径拼到 web 根目录上找文件
/是站点根目录,不是 Linux 根目录;真实路径等于webroot + 请求路径,路径为/时补index.html。- 拼接前必须规范化并校验前缀,否则
../能读到站点外的文件。
六、文末面试题
6.1 推导题(按本篇知识点)
1. 为什么 HTTP 用文本格式而不是二进制?代价是什么?
答(推导):文本格式两端都不需要编解码库,一个扫 CRLF 的状态机就能解析,人也读得懂抓包内容,跨语言门槛极低。代价是头部字段名重复传、数字要转成十进制字符串使体积变大、二进制内容得靠 Base64 编码涨约三分之一。HTTP/2 保留语义却改成二进制分帧,正是为了补这些代价。
2. 空行在 HTTP 报文里起什么作用?只有空行够不够确定一条报文的边界?
答(推导):空行标记头部结束,解析器读到连续两个 CRLF 就认为后面是正文,即使没有正文这个空行也不能省。但空行只解决头部边界,正文边界还得靠 Content-Length 声明字节数,或者用分块编码在零长度块处结束,再或者靠连接关闭表示结束。两把尺子合起来,一条报文才能从字节流里被准确切出来。
3. 浏览器输入一个域名到看到页面,中间经过哪几步,各自对应 URL 的哪一段?
答(推导):先查浏览器缓存、系统缓存和 hosts,再向本地 DNS 递归查询拿到 IP,对应 URL 里的域名;接着用 IP 加端口创建 socket 并 connect,端口来自 URL 或协议默认值;https 还要做 TLS 握手;连接建好后发出GET /path HTTP/1.1,路径来自 URL;服务器按 webroot 拼路径取文件,返回状态行、头部、空行和正文,浏览器解析 HTML 后再为图片、脚本发起后续请求。
6.2 真题
1. HTTP 请求报文和响应报文分别由哪几部分组成?
答(推导 · 已对照解析,转述):请求报文由请求行、请求头部、空行、请求体四部分组成,请求行里是方法、URL 和协议版本,GET 的请求体通常为空。响应报文由状态行、响应头部、空行、响应体四部分组成,状态行里是协议版本、状态码和原因短语。空行是两种报文共同的分界,头部与正文靠它隔开。
【真题·转述自 计算机网络面试题】
2. HTTP 状态码 301 和 302 有什么区别?
答(推导 · 已对照解析,转述):两者都是重定向,都会在响应头 Location 里给出新地址。301 是永久重定向,资源以后都该用新 URL 访问,适合域名更换、站点迁移,缓存可以记住结果;302 是临时重定向,这次先去新地址,以后仍用原地址,适合未登录跳登录页。历史实现里浏览器跟随这两者时可能把 POST 改写成 GET,要保持方法不变得用 307 或 308。
【真题·转述自 计算机网络面试题】
3. GET 和 POST 有什么区别?GET 是安全和幂等的吗?
答(推导 · 已对照解析,转述):GET 的语义是取资源,参数放在 URL 查询串里,可以被缓存、收藏、写进历史记录;POST 的语义是提交数据,数据放在请求体里,通常不缓存。GET 安全且幂等,因为它是只读操作;POST 不安全也不幂等,重复提交可能创建多条记录。要注意「安全」指不修改资源,不是指加密,GET 的参数在 URL 里明文可见,敏感信息不该放查询串。
【真题·转述自 计算机网络面试题】
一次网页访问说到底就三件事:URL 答清哪台机器、哪个端口、哪个文件,HTTP 用空行和长度字段切边界,服务器把路径拼到 web 根目录取出文件。评论区聊聊:你第一次抓包时,有没有意识到
GET / HTTP/1.1就是秘密的起点?