ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

计算机网络篇9:应用层——HTTP 的文本报文结构,与 URL 定位到文件的完整链路

计算机网络篇9:应用层——HTTP 的文本报文结构,与 URL 定位到文件的完整链路


📚 本文收录于「流浪」的系列专栏

🐧Linux系统⚙️C++
📊数据结构与算法🐍Python
🔗LangChain & LangGraph🗄️MySQL 数据库
🌿Git 工具🌐计算机网络
🤖LLM💯大厂面试、八股
📚学习筑基专栏

🏠 博客主页:流浪 | 📝 原创首发于 CSDN


前言:《计算机网络篇8》说清了序列化与字节流的边界,边界归应用层自己切。真实的应用层协议长什么样?HTTP就是最标准的样本:URL 怎么定位到文件、报文排成哪四段、空行凭什么分开报头和正文,这一篇跟着一次点击走完。


一、HTTP 是应用层上的文本协议,报文靠约定好的字符串拼出来

1.1 HTTP 站在应用层,一次请求就是一条 TCP 连接上的字节流

1. HTTP 只规定报文长什么样,怎么送到由下面的 TCP 负责
  1. HTTP 全称 HyperText Transfer Protocol,工作在应用层,下面依次是 TCP 和 IP,这个位置把两件事分开了:报文格式归 HTTP,可靠传输归 TCP。
  2. 请求发出之前,浏览器先要和服务器建 TCP 连接,这一步就是《计算机网络篇7:TCP 网络编程常用接口与调用流程》里客户端 socket、connect 那一套,连不上就谈不上发请求。
  3. 连上之后浏览器把 HTTP 字节写进连接,服务器读出来按同样格式解析,再把响应写回去。
2. 点一下超链接,背后是一次 socket 通信
  1. 我们日常见到的页面上那个蓝色链接本质是浏览器里的一条记录,存着目标资源的 URL,点下去不是「打开网页」,而是发起一次新的请求。
  2. 一次请求对应一次连接上的收发,收发靠 socket 接口,这条链从用户点击一直落到内核的收发缓冲区:
    • 浏览器解析href(超文本引用),拿到 URL
    • 建连接或复用连接,把请求字节写进发送缓冲区
    • 服务器读字节、解析、回响应

HTTP 管「报文长什么样」,TCP 管「字节怎么可靠送到」,两者叠加才凑成一次完整的网页访问。

1.2 HTTP 的两面:为什么重要,以及它传的是超文本

1. 重要性在于它是所有 Web 交互的公共语言
  1. 浏览器地址栏回车、APP 下拉刷新、后端服务调接口,走的都是 HTTP,它是覆盖面最广的应用层协议。
  2. 覆盖面广的直接后果是:不管写 C++、Java 还是 Python,只要想和别人的服务对话,最后都要拼一段 HTTP 报文或解析一段 HTTP 报文。
2. 超文本传输指的是正文里能挂指向其他资源的链接
  1. 超文本(HyperText)的重心在「超」:一段文字里可以嵌一个指向别的资源的引用,顺着引用就能跳过去。
  2. HTML 就是最常见的超文本,文件本身只是纯字符,但标签里写进了图片、视频、样式表、脚本的地址,浏览器解析到标签再去拉对应资源。
  3. 所以 HTTP 传的不只是文字,图片、音频、视频在它眼里都是「一段字节加一个类型标记」。

HTTP 的重要性来自它是 Web 的公共语言,超文本传输则解释了为什么一次页面访问会牵出几十个请求。

1.3 HTTP 的结构靠特殊字符串划区,序列化就是字符串拼接

1. 空格、CRLF、空行三样东西把报文切成段
  1. 第一行用空格分成三段,段与段之间是单个空格,行尾是回车加换行(CRLF,即\r\n)。
  2. 后面每行是一个「字段名: 字段值」,同样以 CRLF 结尾,字段可以有很多行,顺序不影响语义。
  3. 字段写完后补一个单独的空行,也就是连续两个 CRLF,它宣告头部结束,后面才是正文。
2. 不依赖第三方库就能拼能拆,代价是冗余
  1. 《计算机网络篇8》讲过序列化是把结构化字段拼成一串字节,HTTP 用的是最朴素的拼法:字符串直接拼,字段之间用 CRLF 分开,不需要任何框架。
  2. 好处是任何语言几十行就能拼出合法请求,接收方也只要一个扫行尾的状态机就能拆开。
  3. 代价是体积和效率:整数 65535 在二进制里占两字节,写成文本要五字节,头部字段名还要在每个请求里重复。

HTTP 的序列化没有魔法,就是按约定把字段拼成 ASCII 字符串,CRLF 分行、空行分段,前提是双方都认这套字符约定。

1.4 https 在 http 外面套了一层加密,端口也从 80 换成 443

1. https 等于 http 加 TLS,明文变密文
  1. HTTPS 不是另一套报文格式,请求行、头部、正文一个字都没改,只是在 HTTP 和 TCP 之间插了 TLS,把整段字节加密后再交给 TCP。
  2. 加密之后,中间的路由器、代理、同一个 Wi-Fi 下的其他人抓到的都是密文,看不到请求路径和提交内容。
2. 80 与 443 只是默认值,不是两套协议
  1. HTTP 默认 80,HTTPS 默认 443,浏览器看协议名自动补端口,端口也可以改,改了必须在 URL 里显式写出来。
  2. 服务器同时开 80 和 443 很常见,80 一般只做一件事:把请求重定向到 443,让用户始终走加密通道。

https 是 http 报文外套了一层 TLS,格式没变,变的是传输通道和默认端口。


二、一条 URL 定位到全网唯一文件:域名解析成 IP,端口落到进程,路径落到文件

2.1 URL 由协议、主机、端口、路径四段组成

1. 一条 URL 拆开就是四个字段
  1. 完整形态是scheme://host:port/path?query#fragment,从左到右是协议、主机、端口、路径,后面可带查询串和片段标识。
  2. //右边第一段是主机,写域名是给人看的名字,写 IP 是给机器直接用的地址;冒号后是端口,省略按协议补默认值。
  3. 再往后的/a/b/c.html是路径,?k=v是查询串,井号后面是片段标识,片段只在浏览器内部用,不发给服务器。
2. 超链接是页面上的跳转元素,URL 是它指向的地址
  1. 超链接是 HTML 里的元素,形如<a href="...">文字</a>,href里放的就是一条 URL,点它等于让浏览器访问这个地址。
  2. URL 不一定在超链接里,地址栏输入、接口文档、curl后面跟的都是 URL。
  3. URI 是统一资源标识符的统称,URL 是其中带定位方式的一类,日常说的「网址」都是 URL。

URL 的四段各解决一件事:协议决定怎么通信,主机决定找哪台机器,端口决定找哪个进程,路径决定要哪个文件。

2.2 域名是给人用的名字,最终仍要换成 IP 才能路由

1. IP 难记还会变,域名把变化挡在人后面
  1. 点分十进制对人极不友好,记十个网站就到极限,服务器换机房时 IP 会变,让人跟着改地址不现实。
  2. 域名是一层人名化的中间层,用户只记www.douyin.com,对应 IP 由 DNS 查,服务器换地址只改解析记录。
  3. 域名不参与路由,路由器只认 IP,所以域名再好看,最终还是要解析成一个 IP 才能把包发出去。
2. 入口从记域名走到搜关键词,域名的重要性被搜索引擎分走
  1. 早期上网的入口是「记住网址」,书签和门户网站的网址目录承担导航职能,域名短好记就占优势。
  2. 后来入口变成「搜关键词」,用户不必知道域名,搜索引擎把「记地址」接了过去,国内早期做网络实名的周鸿祎和做搜索的李彦宏,抢的都是这个入口。
  3. 移动互联网又把入口挪进 APP 内部搜索框,域名从用户眼前消失,只在开发和运维层面存在。

域名解决的是人的记忆问题,不是机器的路由问题,最终一定落到 IP 上。

2.3 DNS 把域名解析成 IP,浏览器拿到 IP 才发起连接

1. 解析先看本机缓存,没有再问 DNS 服务器
  1. 浏览器有 DNS 缓存,操作系统也有,hosts 文件还能手动写死映射,命中任何一层都不必往外问。
  2. 都没命中时请求发给本地 DNS 服务器,由它替客户端去问根、顶级、权威域名服务器,一层层问到结果,这叫递归查询。
  3. 结果带生存时间(TTL),有效期内复用,过期重查,所以改解析不能立刻全网生效。

2. 拿到 IP 之后紧接着就是 connect
  1. 解析出 IP 只是知道「找哪台机器」,接下来创建 socket、把 IP 和端口填进地址结构、调用 connect 发起三次握手,和《计算机网络篇7》的客户端流程一致。
  2. 连接建立后才轮到 HTTP 报文出场,报文写进这条连接的发送缓冲区,由 TCP 负责切段、重传、按序交付。

DNS 只负责把名字换成地址,换完就退场,后面的连接、收发、重传全是 TCP 和 HTTP 的事。

2.4 端口是协议约定俗成的门牌号,知名端口固定不动

1. http 80、https 443、ssh 22 都是约定
  1. 一台主机上跑着很多进程,IP 只能定位到主机,定位到具体进程要靠端口,端口号写在 TCP 头里,内核据此把数据交给对应 socket。
  2. 知名服务端口是约定好的:HTTP 80、HTTPS 443、SSH 22、DNS 53、SMTP 25,客户端不写端口就按协议默认值补。
  3. 这套约定登记在 IANA 的端口表里,改了端口所有调用方都得跟着改,实际部署中只有内网服务才随便挑。
2. 0 到 1023 是特权端口,绑定要 root 权限
  1. 0 到 1023 属于系统保留区间,普通进程不能直接绑定,Linux 上要求具备 CAP_NET_BIND_SERVICE 能力,root 默认具备。
  2. 这条限制防的是普通用户伪装系统服务:如果谁都能绑 80,本地一个恶意程序就能顶掉真正的 Web 服务器,把请求全接走。
  3. 常见做法是让 Nginx 以 root 启动绑定 80 和 443,再把工作进程降权到普通用户。

端口是主机内部的分发依据,知名端口靠约定统一,特权端口靠权限兜底。

2.5 路径指向服务器上的一个文件,上网的本质是 IO

1. 资源就是文件,网页、图片、视频都不例外
  1. 从 HTTP 角度看,「资源」不是抽象概念,服务器返回的每个东西最终都来自磁盘上的一个文件,或者程序动态生成的一段字节。
  2. 上网这件事拆开只有两个方向:把远端数据取到本地(浏览、看视频、下载),把本地数据送到远端(发帖、上传、提交表单),两个方向都是 IO。
2. 三段合起来才定位到全网唯一的文件
组成对应解决的问题
http(s)端口(80 / 443)定位到主机上的哪个进程
域名IP定位到全网哪一台主机
路径一个文件路径定位到该主机上的哪个文件

缺一段就定位不到唯一文件:只有域名不知道找哪个服务,只有端口不知道找哪台机器,只有路径不知道去哪个目录取。

一条 URL 能定位到全网唯一的文件,是因为它把「哪台机器、哪个进程、哪个文件」按顺序答完了。


三、HTTP 报文分四段,空行把报头和正文分开

3.1 请求报文四段:请求行、请求头、空行、请求体

1. 请求行三段:方法、目标路径、协议版本
  1. 第一段是请求方法,常见的有 GET、POST、PUT、DELETE、HEAD;第二段是请求目标,通常就是 URL 的路径;第三段是协议版本。
  2. 版本号不是装饰,它决定对端按哪套规则解析:HTTP/1.1 默认复用连接,HTTP/1.0 默认一次一断。
2. 请求头一行一个字段,Host 和 Content-Length 最常用

  1. Host指定要访问的站点域名,同一台服务器上可能挂很多网站,服务器靠它区分请求的是哪一个。
  2. Content-Type说明正文格式,如application/json;Content-Length说明正文有多少字节。
  3. 此外还有User-Agent、Cookie、Accept、Connection等字段,名字大小写不敏感,顺序也不影响语义。
3. 请求体不是每次都有,GET 一般不带
  1. GET 的语义是「取」,参数放在查询串里,报文通常到空行就结束,后面没有正文。
  2. POST 的语义是「提交」,数据放在请求体里,长度由Content-Length声明。
  3. 带不带正文和有没有空行是两件事:没有正文,空行也不能省,解析器靠它确认头部结束。

请求报文的四段是一条单向流水:请求行说清要什么,头部补充说明,空行划界,正文承载数据。

3.2 响应报文四段:状态行、响应头、空行、响应体

1. 状态行三段:版本、状态码、原因短语

  1. 第一行形如HTTP/1.1 200 OK,依次是协议版本、三位状态码、原因短语,原因短语只给人看,程序判断结果看状态码。
  2. 状态码第一位决定大类,后两位是具体含义,服务器可以自定义后两位,大类不能乱用。
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. 响应头告诉浏览器怎么处理正文
  1. Content-Type决定浏览器怎么渲染:text/html当网页解析,image/png当图片显示,application/json交给脚本处理。
  2. Location配合 3xx 使用,指出接下来跳转到哪个地址;301 表示永久搬走,302 表示临时换地址,语义不同,缓存行为也不同。

响应报文是请求报文的镜像,状态行替代了请求行,其余三段一一对应。

3.3 空行只负责报头结束,正文多长由 Content-Length 决定

1. 空行是分隔符,不是长度声明
  1. 解析器的动作很机械:读第一行,再一行行读头部字段,读到空行就认为头部结束,剩下的字节都算正文。
  2. 空行不携带长度信息,它只回答「头部到哪里为止」,不回答「正文到哪里为止」。
  3. 请求里没有正文时空行照样要写:不写,服务器就不知道头部有没有结束,会一直等下一行。
2. 没有 Content-Length 时,靠连接关闭或分块编码定界
  1. 最常见的是Content-Length声明字节数,收满这么多字节就是一条完整报文,多出来的属于下一条。
  2. 长度事先不知道时用分块传输编码(Transfer-Encoding: chunked),每块前面写十六进制长度,最后用一个长度为 0 的块收尾。
  3. 还有一种古老方式:不声明长度,靠服务器关闭连接表示结束,它只为兼容 HTTP/1.0 保留,无法区分正常结束和中途断线。
    • Content-Length与Transfer-Encoding不允许同时出现在一条合法报文里
    • 同时收到时以分块编码为准,并按错误处理,防止请求走私
3. HTTP 自己就是应用层切边界的实例
  1. 《计算机网络篇8》的结论是 TCP 不保留报文边界,边界必须由应用层自己划,HTTP 是最标准的落地:头部靠空行切,正文靠长度字段切。
  2. 一条 HTTP 报文被 TCP 拆成几段送达,接收方拼回来之后仍要靠空行和长度字段重新找出边界,否则一样解析错位;所以「粘包」在 HTTP 里不成为问题,协议自带定界手段,自己写私有协议不带这些手段才会掉坑。

空行解决头部边界,长度字段解决正文边界,两把尺子合起来才让一条报文在字节流里可被还原。

3.4 文本格式让人能读机器好解析,代价是头部冗余

1. 抓包就能看懂,调试成本低
  1. 用nc连上服务器,手打三行就能发出合法请求,不需要任何工具库,这种可读性是二进制协议给不了的。
  2. 抓包时明文直接显示,请求方法、路径、头部字段一目了然,排查「为什么返回 404」非常快。
  3. 浏览器开发者工具的网络面板,展示的就是这套报文的解析结果。
2. 二进制要额外编码,头部也有冗余
  1. 文本协议只能传可见字符,图片、音频要走 multipart 或 Base64,后者让体积膨胀约三分之一;头部字段名每个请求重复传,HTTP/2 用头部压缩(HPACK)来治。
  2. 解析要逐字节扫描、数字要从字符串转回整数,比按固定偏移读二进制慢,这是可读性的对价。

选文本是用解析开销换可读性和通用性,这个取舍在 Web 场景划算,在极致吞吐场景未必。


四、请求路径落到磁盘:web 根目录、默认首页、路径拼接

4.1/是 web 根目录,不是 Linux 根目录

1. 服务器给每个站点划一个目录当根
  1. URL 开头那个斜杠容易被当成 Linux 的根目录/,实际上它是 Web 服务器给这个站点指定的站点根目录,通常叫 web 根目录。
  2. Nginx 里用root指令指定,自己写的服务器里就是一个字符串变量,比如webroot = "/wwwroot",对外的资源都放在这棵子树下。
  3. 站点根目录与系统根目录分离,等于把「能对外访问的范围」锁在一个目录里。
2. 不在这个目录下的文件,URL 再怎么写也访问不到
  1. 服务器收到/a/b/c.html,会把它拼到 web 根目录后面去找,找不到就返回 404,不会跑去系统根目录找。
  2. 图片、样式表、脚本、视频遵循同一规则,必须真实存在于 web 根目录的某条路径下,才可能被 URL 命中。
  3. 所以「部署网页」本质就是把文件放进 web 根目录,再让服务器指向它。

URL 里的/是站点的边界,不是文件系统的边界,这条区分决定了哪些文件能被外面看见。

4.2 真实路径等于 webroot 加上请求路径

1. 拼接公式只有一行
  1. 服务器把请求行第二段接到 web 根目录后面:real_path = webroot + url_path。
  2. 这里没有映射表,路径就是目录层级,/a/b/c.html对应webroot/a/b/c.html,一层不多一层不少。
  3. 这也是静态站点的目录结构经常和 URL 结构一致的原因:部署时怎么放,访问时就怎么拼。
2. 一个具体请求走一遍拼接
  1. 客户端请求GET /a/b/c.html HTTP/1.1,假设服务器配置webroot = "/wwwroot"。
  2. 拼出的真实路径是/wwwroot/a/b/c.html,服务器打开这个文件,把内容读出来作为响应正文。
  3. 如果服务器监听 80,完整地址就是http://主机:80/a/b/c.html,端口省略时浏览器自动补 80。

静态资源的访问没有黑魔法,URL 路径和磁盘路径之间就是一次字符串拼接。

4.3 不带路径时补默认首页 index.html 或 index.htm

1. 首页是服务器约定,不是协议规定
  1. 浏览器请求http://example.com/时路径只有一个/,服务器得自己决定给什么。
  2. 约定俗成的做法是找默认首页,优先index.html,没有就找index.htm,再没有就返回目录列表或 403。这个约定不在 HTTP 规范里,不同服务器可以配不同的默认文件名。
2. 补默认路径就是把 webroot、斜杠、index.html 拼起来
  1. 代码里通常准备homepage = "/index.html",请求路径是/时用首页路径替换它。
  2. 拼接结果是webroot + homepage,即/wwwroot/index.html,效果与直接请求这个文件完全一样。
  3. 有的服务器先检查文件是否存在再决定是否回退,顺序错了会出现「明明有首页却返回 404」。
constchar*homepage="/index.html";// 默认首页if(strcmp(url_path,"/")==0){// 请求根路径时替换url_path=homepage;}// /wwwroot + /index.html => /wwwroot/index.html

默认首页是服务器替用户补齐的那一段路径,补完之后仍走同一条拼接逻辑。

4.4 拼路径前必须挡住../,否则能读到站点外的文件

1. 目录穿越就是把路径逃出 webroot
  1. 直接把用户给的路径拼上去,请求/../../etc/passwd拼完就是/wwwroot/../../etc/passwd,..一层层往上跳,最终逃出 web 根目录。
  2. 这类问题叫目录穿越(Path Traversal),是 Web 服务器最经典的漏洞之一,后果是配置文件和密码文件都可能被读走。
  3. 只过滤字符串里的..不够,编码后的%2e%2e、多重斜杠、符号链接都能绕过简单检查。
2. 规范化之后校验前缀
  1. 正确做法是先做路径规范化(realpath 一类函数),把..、.、多余斜杠全部消掉,得到绝对路径。
  2. 再检查这个绝对路径是否以 web 根目录为前缀,不是就直接返回 403,不再打开文件。
  3. 校验要在打开文件之前做,顺序反了等于没做;缓冲区也要限长。

落到代码就两步:realpath规范化得到绝对路径,strncmp和 web 根目录比对前缀,比对失败直接 403。

路径拼接的安全前提是「先规范化、再校验前缀」,只做字符串替换挡不住目录穿越。

4.5 一段最小服务器骨架把前面四章串起来

1. http_mini_server 的调用顺序
  1. 服务端骨架沿用篇 7 的四步:socket 创建套接字、bind 绑定地址、listen 置为监听、accept 取出连接。
  2. 拿到连接 fd 后 read 一次,读到的就是请求报文的字节流,先按 CRLF 切行,第一行取出方法、路径、版本。
  3. 然后拼路径、读文件、拼响应头、写回连接。
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 是文本协议,靠约定的字符串划出结构
  1. 请求行、头部、空行、正文四段,用空格和 CRLF 分区,不依赖序列化库。
  2. 文本换来可读性和通用性,代价是头部冗余和解析开销,https 只是外面套了 TLS。
2. URL 用三段答完「哪台机器、哪个进程、哪个文件」
  1. 域名解析成 IP 解决找哪台主机,端口定位到进程,http 80、https 443、ssh 22,0 到 1023 要特权。
  2. 路径对应 web 根目录下的文件,上网的本质就是取数据和送数据两类 IO。
3. 空行切头部,长度字段切正文
  1. 空行只宣告头部结束,不携带长度;正文靠 Content-Length,未知长度用分块编码,最后一块长度为零。
  2. 这正是篇 8 说的「边界由应用层自己切」,HTTP 自带两把尺子。
4. 服务器把 URL 路径拼到 web 根目录上找文件
  1. /是站点根目录,不是 Linux 根目录;真实路径等于webroot + 请求路径,路径为/时补index.html。
  2. 拼接前必须规范化并校验前缀,否则../能读到站点外的文件。

六、文末面试题

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就是秘密的起点?

返回列表