🔍 开发者资源导航 🔍 |
---|
🏷️ 博客主页: 个人主页 |
📚 专栏订阅: JavaEE全栈专栏 |
内容:
前言
在当今互联网时代,HTTP协议作为应用层最核心的通信协议之一,几乎支撑着所有的Web应用、移动App以及API交互。无论是浏览网页、发送请求,还是进行数据交互,HTTP都扮演着至关重要的角色。
然而,HTTP协议看似简单,实则包含许多细节和设计理念。例如:
- 为什么GET和POST方法有不同的使用场景?
- 状态码200、404、500分别代表什么含义?
- Cookie是如何实现用户登录状态的保持的?
- URL的结构和编码规则是怎样的?
本篇文章将从HTTP协议的基本组成出发,逐步解析请求与响应的结构、常见报头的含义、GET与POST的区别,以及状态码的分类与作用。此外,我们还会介绍如何使用Fiddler抓包工具分析HTTP通信过程,帮助开发者更深入地理解网络通信机制。
应用层协议
在网络上TCP/IP协议用来负责数据的运输,那么我们运输的数据应该是什么的格式呢?为了保证双方都可以读懂对方发送的数据,因此我们需要约定一个“格式”来保障数据格式的一致性,而这就是应用层协议需要解决的问题。
如果说TCP/IP协议是网络中的
“物流系统"
,那么应用层协议就是数据的“使用说明书”
。
在应用层涉及到的网络通信协议,很多都是程序员自定制的。
自定义协议:
- 根据需求,明确传输那些信息
- 约定好信息组织的格式
约定好信息组织的格式有很多种方法:
- 行文本:使用某个符号作为分隔符,提前约定好第几个数据的含义,是最原始的一种方法。
账号,昵称,密码,好友数量\n
账号,昵称,密码,好友数量\n
- xml:xml和html很类似,但是它的标签内容是可以自定义的(html5现在也允许),可读性好,适合别人阅读,但是冗余信息太多了,在网络传输中要消耗更多的带宽,而在服务器中带宽是最贵的东西,因此现在并不是一种主流方案。
<note>
<id>George</id>
<name>John</name>
<passord>Reminder</passord>
<body>Don't forget the meeting!</body>
</note>
- json:当前最主流的方案,使用键值对的方式传递信息,既保持了可读性,消耗的带宽也比xml要少,因为json是单标签,xml是双标签的。但是仍然存在冗余信息。
{
id:1001,
name:"lyf",
pssord:122
}
- protobuf:基于二进制的格式,约定那几个字节存储什么内容,对数据进行压缩,不涉及到json/xml冗余信息了,带宽消耗最少,但是可读性变差了,相当于是用开发效率去换执行效率,性能要求高的场景需要使用,如果性能不要求,还是建议使用json。
当然除了这些自定义的协议之外,还有一些大佬已经搞好的,例如我们接下来要讲的HTTP协议。
HTTP协议
HTTP 诞⽣与1991年. ⽬前已经发展为最主流使⽤的⼀种应⽤层协议,被广泛用于web开发/app开发中,目前HTTP最新版已更新到3.0,但是目前最流行的仍然是20年前的1.1版本。HTTP采用一问一答模式,客户端发一个请求,服务器就返回一个响应,请求和响应一一对应。
抓包工具fiddler
如果要分析网络的通信过程,一个抓包工具是必不可少的,抓包工具可以让电脑上所有的网络通信必须先通过它再进行转发,而这个过程我们就可以分析他们发送和接收的内容。
这里我们选用fidder来作为我们的抓包工具,fidder是专门用来抓取http协议的工具,功能简单,使用也很简单。
我们下载时要下载经典版,最新版现在收费,安装流程这里并不过多赘述,当然你不安装也不影响本文章的后续内容。
打开之后进入的页面如下:
刚打开时左侧可能一下子蹦出来很多的信息,这是因为你后台的应用实际上并不安分,一直在和服务器之间进行沟通,而我们的抓包工具都给这些信息全部抓获了。
点开数据信息后,他给的信息我们可能看不懂,我们可以在这里点击raw选项,让其还原成最初始的HTTP语句。
响应数据经常是压缩过的二进制数据,展现成一段乱码,而该工具是自带解码的,因此我们只需要在出现这个选项时点击它即可解码成正常数据。
除此之外,我们还可以发现左侧的抓包信息的颜色参差不齐,不同的颜色代表不同的含义,红色表示报错,蓝色表示响应是一个网页,绿色表示得到了一个js格式的数据,灰色则表示这个响应的数据已经被缓存了。
协议组成
安装好抓包工具后,就可以对HTTP进行抓取了,http协议本质上是文本格式的协议,更加具体的信息可以查看http的rfc标准文档:rfc文档
请求信息
请求信息由四部分组成:
-
首行: 包括请求方法、URL以及HTTP版本号,
GET https://www.csdn.net/ HTTP/1.1
。 -
请求头: 键值对结构,每一行是一个键值对,键和值之间使用空格隔开,有哪些键,这些键可以取那些值,都是已经规定好的,
sec-ch-ua-platform: "Windows"
。 -
空行: 请求头的最后一行是一个空行,用来标识请求头结束了。
-
正文:有些请求没有正文,有些有。
以下是我使用fiddler抓取的一个请求信息,可当做参考:
POST https://eva2.csdn.net/v3/06981375190026432f77c01bfca33e32/lts/groups/dadde766-b087-42da-8e67-d2499a520ee7/streams/5da0dae3-481b-478b-92ca-3caa293e4a29/logs HTTP/1.1
Host: eva2.csdn.net
Connection: keep-alive
Content-Length: 1161
sec-ch-ua-platform: "Windows"
sec-ch-ua: "Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"
sec-ch-ua-mobile: ?0
X-Sdk-date: 20250603T115606Z
Lts-Sdk-Request-Id: e36bfe51ed0b409b9f51c7791a71a122
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Content-Type: application/json
Lts-Sdk-Version: 1.0.15
Accept: */*
Origin: https://mpbeta.csdn.net
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://mpbeta.csdn.net/
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
{"labels":{},"logs":[{"contents":[{"pid":"mpbeta","ref":"https://mpbeta.csdn.net/","curl":"https://mpbeta.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448","spm":"1011.2415","disabled":"true","extra":"{\"version\":\"new\"}","tos":0,"adb":0,"cCookie":"c_ab_test=1;c_session_id=10_1748950839573.109676;c_first_ref=default;c_first_page=https%3A//www.csdn.net/;c_dsid=11_1748950840378.769273;c_segment=2;c_sid=2027680f8c25c20fdd506cf78e2f2a67;c_page_id=default;c_pref=https%3A//editor.csdn.net/;c_ref=https%3A//www.csdn.net/;","t":1748951766,"screen":"1440*900","urn":"1748951766184-d4c434cd-3802-49aa-a879-d0357d710c5c","vType":"U010000","log_id":"5","sign":"0d3dc2f82f21b9c90a67228fc4ee4a91","lscreen":"1440*900","hca":"F268FC93106CB2BC","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36","cid":"10_18662895060-1748744031688-432578","uid":"to_mountain","sid":"10_1748950839573.109676","dc_sid":"2027680f8c25c20fdd506cf78e2f2a67","did":"10_18662895060-1748744031688-432578","utm":"","un":"to_mountain","fid":"20_56679795187-1748744032956-330580","__client_time__":1748951766188}]}]}
响应信息
响应信息的构成和请求很相似,也是由四部分组成:
-
首行: 包括版本号、状态码、状态码描述,
HTTP/1.1 200 OK
。 -
响应头(Header): 也是键值对结构,每一行是一个键值对,键和值之间使用空格隔开,键值对和请求有区别,有些重要的键值对在请求和响应中都存在,
Content-Type: text/html;
。 -
空行: 响应头的最后一行是一个空行,用来标识响应头结束了。
-
正文(Body):这个响应可以是html/json或者一些其他类型。
以下是抓取到的部分响应数据:
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 28 May 2025 09:10:48 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Vary: Accept-Encoding
Set-Cookie: SNUID=D0E28FDCB3B5859D3C3ABEC3B37C5F65; expires=Thu, 28-May-26 09:10:48 GMT; domain=.sogou.com; path=/
Pragma: No-cache
Cache-Control: max-age=0
Expires: Wed, 28 May 2025 09:10:48 GMT
UUID: f8a02e75-0760-4628-ab51-8d55bda3fee3
Content-Length: 15858
<!DOCTYPE html><html lang="cn"><head> <meta name="baidu_union_verify" content="efd6e8ce094119528f66c2d380f6ec94">
<meta name='360_ssp_verify' content='651669fb99b77a4e4efae7ec25d6796a' /> <meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no"><script>window._speedMark = new Date();
window.lead_ip = '111.61.81.98';
window.now = 1748423447974;
window.uuid = 'f8a02e75-0760-4628-ab51-8d55bda3fee3';
window.cuid = 'AAH0RLpVUgAAAAuiUwbjJAAASQU=';</script><script type="text/javascript">/*file=static/js/resourceErrorReport.js*/!function(a){var n=(new Date).getTime(),r=a.location.protocol;function c(e,t){var o=(new Date).getTime()-n;(new Image).src=["//pb.sogou.com/pv.gif?uigs_productid=wapapp&type=resource-
url组成
在上述的请求信息的首行中,我们提到过url这一词,其全称名为:统一资源定位系统,url是描述网络上的唯一资源的位置的,url不是http专属的概念,他可以给各种协议提供支持,是一个网络中比较通用的概念,可以用来标识给那个协议进行服务的。
url在我们生活中很常见,例如你访问的网址就是一个url。
https://mpbeta.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
其组成也可分为多个部分:
- 协议(Scheme):放在开头,例如上述的例子就是使用的https协议, 以
://
结尾。 - 域名(Host):标识资源所在的服务器地址,一般是ip地址或者域名,在网络中,ip和域名可以相互转换,这个过程通过DNS域名解析系统来完成的。。
- 路径(Path):在确认了主机和端口号后,就需要确定某个资源,这一般是带有层次结构的路径。
- 查询字符串(Query String):?后面是查询字符串,表示对要访问的资源进行补充说明,也是键值对结构,使用&进行分割,键和值直接使用=,此处的键值对怎么写时程序员自定义的。
- 片段(Fragment):指向资源内的某个特定部分(如页面锚点),现在用的少了,文档类的网站常见。
URL转义
在url中有一些特殊的符号表示不同的含义,例如::
、/
、&
之类,而我们的查询字符串是我们程序员自定义的,万一你里面包含有这些特殊的字符咋办?因此它对各种字符进行了转义,其中也包括了汉字等,每个字节取出来16进制表示,前面加上%,汉字用的是utf8编码。
?ie=utf-8&f=8&rsv_bp=1&tn=68018901_45_oem_dg&wd=%EF%BC%9A%27a
请求方法
HTTP中的请求方法用于表示这次的请求要干什么,下述内容看似很多,但是我们只需要了解GET以及POST方法即可,因为最初时只有GET和POST,后来逐渐才有其他的,因此GET和POST方法占据了日常中的大多数使用。
GET方法特点
GET ⼀般⽤于获取数据,例如获取html、css、js等操作,GET请求一般是没有body的,如果要通过GET发送数据,使用url中的query string传递过去,body部分一般为空。GET还通常建议设计成幂等
的(标准建议),POST则无要求。
幂等性是计算机科学和数学中的重要概念,指对同一个操作重复执行多次所产生的影响与执行一次的影响相同。
POST方法特点
多⽤于提交⽤⼾输⼊的数据给服务器(例如登陆⻚⾯),URL 的 query string ⼀般为空 (也可以不为空),body 部分⼀般不为空,通过body传输数据。
GET和POST的区别
首先GET和POST本质上并没有区别,它俩完全可以混用,用GET去登录页面,用POST获取数据也是没有任何问题的,它俩主要是是语义上的不同。
- 使用场景上:GET ⼀般⽤于获取数据, POST ⼀般⽤于提交数据。
- 传递数据方式上:GET 的 body ⼀般为空, 需要传递的数据通过 query string 传递, POST 的 query string ⼀般为空, 需要传递的数据通过 body 传递。
- 设计方式上:GET 请求⼀般是幂等的, POST 请求⼀般是不幂等的. (如果多次请求得到的结果⼀样, 就视为请求是幂等的)。
- 存储方式上:GET 可以被缓存, POST 不能被缓存(这⼀点也是承接幂等性,如果多次请求结果不一样存储就没意义了)。
注:上述使用的词是一般,并不代表是绝对的。
常见问题
1. POST比GET更安全?
并不是,这个问题的产生是由于在传输数据时,GET方法在传输数据时是在url中传输的,它会在上方的网址中显示出你输入的信息,可能会误以为它会泄露你的账号或者密码之类的。但是实际上安不安全是由你的传输是否加密决定的,如果你不加密即使使用的POST方法也很容易获取到你的数据,只要是明文传输,就不存在安全的情况。
2. GET传输数据有长度限制?
首先在HTTP标准中并没有规定长度限制,但是在上古时期存在这个情况,在以前浏览器的URL的长度是存在限制的,如果传输太多可能会被截断,但是现在的主流浏览器早就没有这种限制了。
3. GET只能传输文本,POST可以传输二进制?
GET方法的url里面确实只能放文本,但这并不代表它无法传输二进制,首先GET也不是完全不能带body,但是有些客户端/服务器可能不支持,其次我们也可以将二进制转换为 Base64 编码进行传输。
Restful风格
在Restful风格是设计服务器接口的一种习惯,在实现这些api的时候会用到其他的请求方法。
该风格是这么实现的:
POST:用于新增。
DELETE:用于删除。
PUT:用于修改。
GET:用于查询。
(其实这四个都可以进行增删改查,但是是这么规定的)
常见报头
Host
他是一个请求头,表示服务器主机的地址和端口,在 HTTP/1.1 中,所有请求必须包含 Host 头(否则返回 400 Bad Request),Host 头通常与请求 URL 中的域名一致,但可能不同(如反向代理场景)。
Host: rumt-zh.com
Content-Lenth & Content-Type
Content-Lenth:表示body 中的数据长度,单位是字节。
Content-Type:表示body中的数据格式。
这两个头在请求和响应都有,如果有body但没有这两个头就认为是非法的报文,它俩存不存在也取决于是否有body部分。
作用:
当请求或者响应存在多个时,如何进行区分它们呢?
header和body之间有一个空行,因此可以用空行区分header,而body的区分就是通过Content-Lenth的值,读取这个长度的值,并用Content-Type的格式进行解析。
Content-Type: application/x-javascript
Content-Length: 92633
User-Agent
简称UA,表示了用户使用的设备的浏览器和操作系统的情况。
例如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Mozilla:火狐浏览器创始人名字
Windows NT:windows版本
x64:cpu环境
AppleWebKit:浏览器内核
Chrome/136.0.0.0:浏览器版本
Safari/537.36:苹果手机的浏览器
作用:
可以用于区分用户使用的设备,例如PC端和手机端使用不同尺寸的页面来适配用户的使用。
Referer
描述了当前的页面的来源,这个页面从哪个页面跳转过来的,当然直接输入url/点收藏栏打开的页面是没有Referer的。
作用:
广告平台(如 Google Analytics)通过 Referer 统计用户来源,计算转化率。网站可以用 Referer 判断请求是否来自自己的域名,防止其他网站盗用资源(如图片、视频、API)。
Referer: https://facebook.com/ads/click?ad_id=123
Cookie
浏览器持久化存储在本地的一种方式,使用键值对进行存储。
首先浏览器是无法直接访问用户的文件的,因此诞生了Cookie这种方法进行存储,浏览器保存了这些Cookie后,后续给服务器发送请求的时候,把这些cookie键值对放到请求Cookie header中传输到服务器里面。
Cookie: SUID=62513D6F5B54A20B0000000067D6CFF4; cuid=AAH0RLpVUgAAAAuiUwbjJAAASQU=; SUV=00BA49EE6F3D516267D6CFF6C5F9E068; SMYUV=1745122380511658; SNUID=D9EB86D5BABD8DD6D231D1CABBEEA7DC; wuid=AAEV+t3cUwAAAAuipaVKygEAkwA=; arialoadData=false; front_screen_resolution=1440*900; front_screen_dpi=1; IPLOC=CN1308; LSTMV=257%2C410; LCLKINT=3344; ssuid=7396569824
键值对是程序员自定义的,所以看不懂很正常,大多数都是和业务相关的。
在我们的浏览器点击F12可以查看到我们本地存储的值。
作用:
一个典型的案例就是登录和用户认证,在大多数的网站中登录是只需要一次的,第二次进入不需要登录,而这就是通过保存会话信息实现的:
- 用户提交登录信息。
- 服务器验证,并发送一个会话id保存到客户端的Cookie中,服务器使用哈希表将会话id和会话信息保存到服务器中。
- 用户再次登录时发送会话id到服务器
- 服务器根据哈希表找到之前的会话信息,并返回给客户端。
Cookie是带有存储时间的,当超过存储时间就会自动删除,这也就是为什么有的网页登录一次后过一段时间还需要再登录。
除此之外你也可以自己尝试一下将自己网页里面的Cookie都删掉,观察一下是否需要重新登录。
状态码
状态码:返回出错原因的关键手段,良好的设置状态码,可以帮我们快速找到bug
状态码 | 名称 | 描述 |
---|---|---|
200 | OK | 请求成功,服务器已返回请求的数据。 |
302 | Move Temporarily | 临时重定向,服务器要求客户端访问另一个 URL(如域名跳转)。 |
403 | Forbidden | 访问被拒绝,服务器理解请求但拒绝执行(如权限不足)。 |
404 | Not Found | 资源未找到,URL 对应的资源在服务器上不存在。 |
405 | Method Not Allowed | 请求方法不被允许(如用 GET 访问只支持 POST 的接口)。 |
500 | Internal Server Error | 服务器内部错误,代码逻辑异常未被捕获(如未处理的异常)。 |
504 | Gateway Timeout | 网关超时,服务器作为网关或代理时未及时从上游服务器收到响应。 |
以上是一些常见的状态码,但是我们不必死记硬背,他们有规律,2开头可以认为成功,3开头都是重定向,4开头客户端出错,5开头服务器出错,1开头的状态码并没有被定义,除非在某些试验条件下,服务器禁止向此类客户端发送。
感谢各位的观看Thanks♪(・ω・)ノ,如果觉得满意的话留个关注再走吧。