网络原理(HTTP篇)
网络原理HTTP
前言HTTPHTTP的工作流程抓包工具抓取HTTP报文HTTP报文格式
请求报文具体细节首行URLURL的基本格式URL encode
方法
报头(header)HostContent-Length 和 Content-TypeUser-Agent(UA)RefererCookie(重要)
前言
如图:HTTP/3.0之前 是基于TCP实现的。到了HTTP3.0版本,则是基于UDP实现的
HTTP
HTTP,全称为 “超文本传输协议”,现在最新版本为 HTTP/3.0,但主流使用的仍然是HTTP/1.1版本。
什么叫超文本? 我们知道,文本文件其实就是字符串文件(即能在 UTF8/GBK 等码表上找到合法字符的字符串)
而超文本文件,就不仅仅是字符串,还能携带一些图片(如HTTP、HTML)
超文本之后,还有富文本(Word文档),能够支持的格式更多了~
HTTP的工作流程
HTTP/1.1 采用 持续连接 方式。服务器在发送响应后,仍然保持这条连接,使同一个客户端(浏览器)和该服务器可以继续在这条连接上传输后续的HTTP请求报文和响应报文。
HTTP还使用了 流水线 工作方式以提高效率。即浏览器在收到HTTP响应报文之前就能够连续发送多个请求报文。这样就能节省很多个RTT时间,使得TCP连接中的空闲时间很少。
HTTP的交互是非常经典的 “一问一答” 模式:
抓包工具
借助一个抓包工具fiddler 来学习HTTP的报文格式
wireshark:功能很全,可以抓各种协议数据包,但使用起来也相对复杂
fiddler:专注于抓取 HTTP 的包
注:使用fiddler时,需要进行设置:因为网络上大部分的请求都是基于 HTTPS(HTTPS在HTTP基础上进行了加密,安装证书就可以让fiddler对HTTPS的报文进行解密)。
注:打开一个网站,浏览器和服务器之间的 HTTP 交互并不是只有一次,通常会有很多次。第一次交互是拿到这个页面的 HTML,HTML还会依赖CSS和JS等,HTML被浏览器加载后,又会触发一些其他的http请求,获取到CSS、js等,当执行js时,js代码里又可能触发很多的http请求。经过多次这样拉扯之后,才会呈现浏览器里所看到的内容。
抓取HTTP报文
随便进入一个网站: 蓝色的表示返回的是一个 html,这个往往是访问一个网站的入口请求,选中这个请求并双击
这个是HTTP请求
这是请求的响应报文
用记事本打卡响应报文内容:会看到乱码
这里的数据被压缩成乱码。因为网络传输中,带宽是一个比较贵的硬件资源,为了节省带宽,就可以把响应数据进行压缩(一般都是压缩响应,请求报文不太需要),压缩和解压缩的过程是需要消耗时间和CPU的
解压缩之后就能看到正常的响应内容数据了
注:这种灰色的请求与响应,是由于浏览器和服务器之间要进行多次网络交互,为了提升效率,就会把一些固定不变的内容在浏览器本地的硬盘上进行缓存(如css、图片、js很少发生改变的数据)。保存到硬盘上后,后续再请求,就可以直接从硬盘上读取数据,减少了网络交互的开销。(可以使用 ctrl + F5 强制刷新,强制读取服务器数据)
HTTP报文格式
HTTP请求报文格式,包含4个部分:
首行。首行分为三个部分:方法(method)、URL、HTTP的版本号,三者用空格分割 请求头(Header) 从第二行开始,红框圈住部分就叫请求头。类似于TCP报头/IP报头,携带了重要的属性信息(只不过TCP/IP是以二进制携带的,HTTP是以文本内容携带) HTTP的请求头部分是通过键值对来组织的(Key-Value),每个键值对占一行。键和值之间使用 : 加上 (空格) 来分割的。键值对有哪些,分别是什么含义,都是HTTP协议规定的,后续详解。 空行:表示一个报文的结束标记 正文(body):http的载荷部分(有的http请求有body,有些则没有,这很正常)
HTTP响应报文格式,包含4个部分:
首行:三个部分(版本号、状态码、状态码描述)
状态码表示这一次请求是成功还是失败,失败的原因是什么。 就像卸载Python不开管理员权限就会出现2503/2502状态码
响应头。也是由键值对组成(Key-Value),通过冒号+空格来分割(: ) 空行:报文结束标记 正文(body):数据载荷部分 可以看到响应的载荷是html
请求报文具体细节
首行
URL
URL(唯一资源定位符):描述一个网络上的资源位置
URI(唯一资源标识符):只是一个标识,用来区别于其他资源的标识。(角度不同,有时URI也可以表示URL)
这两个东西表示的含义是差不多的,严格说URI的范围比URL更广一些。URL特指你这个东西在网络的哪里。
URL的基本格式
协议方案名:顾名思义,指该网站所用的协议
登录信息(已弃用):毕竟将用户名、密码直接放在这上面是很不安全的操作
服务器地址:由于DNS域名解析系统,这里显示的是域名,用对应的IP地址也可以访问到
服务器端口号:可以指定,也可以不写。不带端口号,浏览器就会取默认值(因为这些服务器都很有名,http: 80;https: 443)
带层次的文件路径:标识资源在网络上的位置
想知道资源位置,要做的:
通过 IP地址 知道服务器在哪通过 端口号 知道对应程序在哪最后通过这个路径知道是访问哪个资源(这和之前章节说的 “文件” 又串起来了,绝对路径就是表示一个文件,知道那个文件在哪)
查询字符串:针对请求的内容做的补充说明
查询字符串,是客户端给服务器传递信息的重要途径,也是以键值对的方式来组织的。因为每个网站业务逻辑不同,需要补充的信息也是不同的,所以键值对的内容都是由程序员自定义的
结合上述的 IP地址、端口号、路径、查询字符串,就可以 “精确” 描述出一个网络资源的位置了
片段标识符:标识当前页面的某个部分,通过不同的片段标识可以完成页面内的跳转。
URL encode
查询字符串(query string):是程序员自动义的键值对。但在URL中,本身有些符号具有特殊含义(如 / 、: 等),如果两者的符号冲突,就会导致一些很严重的bug(比如网页跳转失败!)
所以我们就需要对这类特殊符号,在自定义的时候,将其 “转义”
PS:对于汉字来说,也是要进行转义的!汉字的 UTF8/GBK 等编码值,可能其中某个字节恰好和某个符号的 ASCII 码值冲突!
给一个特殊符号的例子:打开包含 C++ 字样的网站,可以看到 %2B%2B,因为 + 在ASCII码表中,用 16进制 表示,就是 2B,并且加上 % 表示这是转义后的内容
再来看看汉字的情况:
注:网页中的URL看到的就是汉字本身,而不是转义后的内容,因为网页将它经过了处理,一旦将该URL复制粘贴出来,就不是汉字,而是转义后的内容了
方法
为什么说这些语义仅是作者的一厢情愿? 因为别忘了,HTTP协议是工作在应用层的,这些方法的作用是可以由程序员自定义的。也就是说,完全可以用 GET 来上传数据,POST 来获取数据。怎么写,就靠程序员自行约定了。
报头(header)
header 的整体格式是 “键值对” 结构
Host
Host:表示服务器主机的地址和端口(URL里其实也已经有Host了)
这里的 Host 与 URL 中的 IP地址、端口等信息,绝大部分情况下都是一样的。
少数情况可能不同。
少数情况:如使用翻墙代理软件,URL中的IP和端口指向的是代理服务器,而Host中指向的则是最终服务器
Content-Length 和 Content-Type
Content-Length:表示 body 中的数据长度
通过这个长度,就可以处理 “粘包问题”,因为HTTP协议底层是基于TCP实现的!
如果是没有 body 的请求 / 响应,则直接使用 “空行” 作为数据包之间的分割符。 如果有 body,空行就不是结束标记了,而是从空行开始读取 body 数据,此时就以 Content-Length 来作为数据包之间的边界。
Content-Type:表示请求中的 body 的数据格式。body 可以传输很多种格式,包括程序员也可以自己约定任意的格式,但有些格式是非常常见的,需要去了解。
请求中的常见格式:
application/json:body 就是 json 格式的数据 application/x-www-form-urlencoded:称为 “form表单”,通过 HTML 中的 form 标签构造出来的一种格式,这个格式的特点是把 query string 放到 body 中。(这个格式也可以用来上传文件) multipart/form-data:主要是上传文件时使用的。(这种form表单提交数据的方式越来越少了,现在还是 json 偏多)
响应中的常见格式:
text/plain:纯文本 text/html:html text/css:css application/javascript:js application/json image/png image/jpg …
注:正常来说,响应报文只要有 body,都会有 Content-Type;但也有例外,如果响应报文确实没有 Content-Type,也没有 body,此时有些容错能力很强的浏览器,也能尽可能将这些数据正确显示出来(如 Chrome 浏览器)。
User-Agent(UA)
显而易见:UA描述了用户使用啥样的设备进行上网
Referer
Referer:描述了这个页面是从哪个页面跳转过来的。(也就是能知道当前页面的上一级页面是啥)
Cookie(重要)
Cookie:一种浏览器本地持久化存储数据的机制(即将数据存到硬盘里)
浏览器作为计算机应用层软件,能否直接读写本地硬盘文件? 当然可以,系统提供了 API 来操作文件,作为一个应用层程序,当然可以调用这些 API 了。
那么浏览器上运行的网页,能否通过浏览器提供的 API 来读写本地硬盘文件? 理论上是可以的,但是浏览器禁止了这种做法。因为要保障安全性,总不能点进一个网站,你的电脑就直接中病毒了吧。
但是,有些网站,将一些信息保存在浏览器,是一种刚需,比如登录网站的用户信息。 所以浏览器提供了 Cookie 这样的 API,能够有限度地存储数据,而不是直接访问本地文件系统。(类似于 Cookie 的机制,还有 LocalStorage、IndexDB)
Cookie 也是按照键值对的格式来保存信息。
Cookie最大的作用,就是让服务器对这个客户端有一个清楚的认识。
关于 Cookie 的几个重要结论:
Cookie 从哪里来? 通常都是客户端首次访问服务器,服务器返回给浏览器的 Cookie 到哪里去了? Cookie 会存储在浏览器本地主机的硬盘上,后续每次访问服务器都会带上 Cookie(让服务器对这个客户端的信息有个认识,从而返回这个客户端以前在这个页面存留的对应信息) Cookie 中存的是什么? 存的是键值对数据,同 query string 一样,这里的内容是由程序员自定义完成 Cookie 在浏览器中是如何组织的? 就跟给数据分目录一样,Cookie 是通过域名来分类的。(这也好理解,每个人在不同的网页上,肯定是用不同的账户) Cookie 的作用? 用来在客户端保存数据,其中最主要的就是保存用户的身份标识。 上传数据给服务器,服务器就能返回这个用户对应的数据信息。
PS:浏览器中保存的账户密码,是另一个保存机制,并不是 Cookie。