学习目标:
- 能区分 CS 架构与 BS 架构,并说明各自适用场景
- 理解 HTTP 协议的定位、作用与基本工作流程
- 掌握 URL、请求报文、响应报文、状态码等核心概念
- 能区分 GET、POST 等常见请求方法的使用场景
- 理解 HTTP 的无状态特性,以及 Cookie / Session 的基本作用
本章重点:
- HTTP 在网络体系中的位置与请求-响应模型
- URL 的组成、请求报文和响应报文结构
- 常见状态码与请求方法的实际含义
- 长连接、无状态、Cookie / Session 的基础理解
1. CS架构(Client/Server)
CS架构即客户端/服务器架构,是一种典型的两层架构模式。
- 安装专用软件
- QQ客户端
- 游戏客户端
- 业务逻辑
- 数据管理
- 响应请求
┌─────────────┐ ┌─────────────┐ │ 客户端 │ ⇄ ⇄ ⇄ │ 服务器 │ │ (Client) │ 网络 │ (Server) │ ├─────────────┤ ├─────────────┤ │• 安装专用软件│ │• 业务逻辑 │ │• QQ客户端 │ │• 数据管理 │ │• 游戏客户端 │ │• 响应请求 │ └─────────────┘ └─────────────┘
1.1 典型应用场景
- 📱 即时通讯软件(QQ、微信PC版)
- 🎮 网络游戏(英雄联盟、王者荣耀)
- 🗄️ 数据库管理工具(Navicat、SQL Developer)
1.2 优缺点
| 优点 | 缺点 |
|---|---|
| 响应速度快,体验流畅 | 需要安装专用客户端 |
| 功能丰富,可离线使用部分功能 | 跨平台需要单独开发 |
| 安全性较高 | 升级维护成本高 |
2. BS架构(Browser/Server)
BS架构即浏览器/服务器架构,是当今Web应用的主流架构。
- 无需安装
- Chrome
- Edge
- 业务逻辑
- 返回HTML
- CSS/JS
┌─────────────┐ HTTP ┌─────────────┐ │ 浏览器 │ ⇄ ⇄ ⇄ │ 服务器 │ │ (Browser) │ 协议 │ (Server) │ ├─────────────┤ ├─────────────┤ │• 无需安装 │ │• 业务逻辑 │ │• Chrome │ │• 返回HTML │ │• Edge │ │• CSS/JS │ └─────────────┘ └─────────────┘
2.1 典型应用场景
- 🌐 各类网站(新闻、博客、电商)
- 📱 Web应用(在线文档、网盘)
- 🔧 管理系统(OA、ERP、CRM)
2.2 优缺点
| 优点 | 缺点 |
|---|---|
| 无需安装,随时随地访问 | 依赖网络,离线功能有限 |
| 跨平台,一处开发处处运行 | 浏览器兼容性需要考虑 |
| 升级维护简单(服务器端) | 性能受浏览器限制 |
3. CS vs BS 对比
| 对比维度 | CS架构 | BS架构 |
|---|---|---|
| 客户端 | 专用软件 | 浏览器 |
| 安装 | 需要下载安装 | 无需安装 |
| 更新 | 客户端需单独更新 | 服务器更新即可 |
| 跨平台 | 需要单独开发 | 天然跨平台 |
| 性能 | 响应快,体验好 | 受浏览器和网络影响 |
| 维护成本 | 高(多端维护) | 低(服务端维护) |
💡 发展趋势:现代应用趋向于混合架构:核心功能用CS(如微信PC版),轻量功能用BS(如微信小程序)
4. HTTP协议概述
4.1 什么是HTTP?
HTTP(HyperText Transfer Protocol,超文本传输协议)是用于传输超媒体文档(如HTML)的应用层协议。
4.2 HTTP发展历史
HTTP/0.9 (1991) → HTTP/1.0 (1996) → HTTP/1.1 (1997) → HTTP/2 (2015) → HTTP/3 (2022)
↑ ↑
只有GET 当前主流版本4.3 HTTP在TCP/IP中的位置
┌────────────────────────────────────────────────────────────┐ │ 第4层 - 应用层 (Application Layer) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ HTTP │ │ FTP │ │ SMTP │ │ DNS │ │ │ │ (网页) │ │ (文件传输)│ │ (邮件) │ │ (域名解析)│ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ ├───────┼────────────┼────────────┼────────────┼──────────────┤ │ 第3层 - 传输层 (Transport Layer) │ │ │ │ │ │ │ │ └────────────┴──────┬─────┴────────────┘ │ │ ↓ │ │ ┌──────────────┐ │ │ │ TCP / UDP │ ◄── 端口号(80/443) │ │ └──────┬───────┘ │ ├───────────────────────────┼────────────────────────────────┤ │ 第2层 - 网络层 (Network Layer) │ │ ↓ │ │ ┌──────────────┐ │ │ │ IP 协议 │ ◄── IP地址 │ │ └──────┬───────┘ │ ├───────────────────────────┼────────────────────────────────┤ │ 第1层 - 数据链路层 (Data Link Layer) │ │ ↓ │ │ ┌──────────────────────────┐ │ │ │ 以太网 / WiFi / 物理网络 │ ◄── MAC地址 │ │ └──────────────────────────┘ │ └────────────────────────────────────────────────────────────┘
各层职责:
| 层级 | 名称 | 核心功能 | 典型协议/设备 |
|---|---|---|---|
| 4 | 应用层 | 为用户应用程序提供服务 | HTTP、FTP、SMTP、DNS |
| 3 | 传输层 | 端到端连接,可靠传输 | TCP、UDP |
| 2 | 网络层 | 寻址和路由选择 | IP、路由器 |
| 1 | 数据链路层 | 物理传输,帧封装 | 以太网、交换机 |
5. HTTP工作流程
完整请求-响应流程
┌─────────────────────────────────────────────────────────┐ │ 1️⃣ 客户端与服务器建立TCP连接(三次握手) │ │ 2️⃣ 客户端发送HTTP请求报文 │ │ 3️⃣ 服务器处理请求,生成响应 │ │ 4️⃣ 服务器返回HTTP响应报文 │ │ 5️⃣ 客户端接收响应,渲染页面 │ │ 6️⃣ 关闭TCP连接(四次挥手) │ └─────────────────────────────────────────────────────────┘
请求-响应时序图:
客户端 服务器
│ │
│---------- 1. 建立连接 ------------>|
│ │
│---------- 2. 请求报文 ------------>│
│ (Request) │
│ │
│<--------- 3. 响应报文 ------------│
│ (Response) │
│ │
│---------- 4. 关闭连接 ------------>│
│ │6. URL详解
6.1 URL的组成部分
https://www.example.com:8080/path/to/page?name=value#section └───┘ └─────────────┘ └──┘ └────────────┘ └────────┘ └───┘ │ │ │ │ │ │ │ │ │ │ │ └─ 锚点 │ │ │ │ └─ 查询参数 │ │ │ └─ 路径 │ │ └─ 端口 │ └─ 域名 └─ 协议
6.2 各部分的含义
| 部分 | 说明 | 示例 |
|---|---|---|
| 协议 | 访问资源使用的协议 | http、https、ftp |
| 域名 | 服务器的地址 | www.baidu.com |
| 端口 | 服务监听的端口号 | 80(HTTP默认)、443(HTTPS默认) |
| 路径 | 服务器上的资源位置 | /index.html、/api/users |
| 查询参数 | 传递给服务器的额外数据 | ?page=1&size=10 |
7. HTTP请求
7.1 请求报文结构
▶ 请求行(Request Line)
GET /index.html HTTP/1.1
▶ 请求头(Request Headers)
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
Cookie: sessionId=xxx
▶ 空行
▶ 请求体(Request Body)【可选】
username=admin&password=1237.2 HTTP请求方法
GET vs POST 对比
| 特性 | GET | POST |
|---|---|---|
| 用途 | 获取数据 | 提交数据 |
| 数据位置 | URL参数中(?key=value) | 请求体中 |
| 数据大小 | 有限制(URL长度限制) | 无限制 |
| 安全性 | 参数暴露在URL中 | 相对安全 |
| 缓存 | 可被缓存 | 默认不被缓存 |
| 幂等性 | 幂等(多次请求结果相同) | 非幂等 |
常见请求方法
| 方法 | 用途 |
|---|---|
| GET | 获取资源 |
| POST | 提交数据,创建资源 |
| PUT | 更新资源(全量) |
| DELETE | 删除资源 |
| PATCH | 更新资源(部分) |
| HEAD | 获取响应头 |
7.3 常见请求头
| 请求头 | 说明 |
|---|---|
Host |
指定服务器域名和端口 |
User-Agent |
客户端信息(浏览器类型、版本) |
Accept |
客户端可接受的响应类型 |
Content-Type |
请求体的媒体类型 |
Cookie |
携带的Cookie数据 |
8. HTTP响应
8.1 响应报文结构
▶ 状态行(Status Line)
HTTP/1.1 200 OK
▶ 响应头(Response Headers)
Content-Type: text/html
Content-Length: 1234
Set-Cookie: sessionId=xxx
Cache-Control: no-cache
▶ 空行
▶ 响应体(Response Body)
<html>...HTML内容...</html>8.2 HTTP状态码
状态码分类
| 范围 | 类别 | 含义 |
|---|---|---|
| 2xx | 成功 | 请求被成功处理 |
| 3xx | 重定向 | 需要进一步操作 |
| 4xx | 客户端错误 | 请求有语法错误或无法完成 |
| 5xx | 服务器错误 | 服务器处理请求时出错 |
常见状态码
| 状态码 | 名称 | 说明 |
|---|---|---|
| 200 | OK | 请求成功 |
| 201 | Created | 资源创建成功 |
| 301 | Moved Permanently | 永久重定向 |
| 302 | Found | 临时重定向 |
| 304 | Not Modified | 使用缓存 |
| 400 | Bad Request | 请求格式错误 |
| 403 | Forbidden | 禁止访问 |
| 404 | Not Found | 资源不存在 |
| 500 | Internal Server Error | 服务器内部错误 |
| 502 | Bad Gateway | 网关错误 |
| 503 | Service Unavailable | 服务不可用 |
8.3 常见响应头
| 响应头 | 说明 |
|---|---|
Content-Type |
响应体的媒体类型 |
Content-Length |
响应体的字节长度 |
Set-Cookie |
服务器设置的Cookie |
Location |
重定向的目标URL |
Cache-Control |
缓存控制策略 |
9. HTTP连接管理
9.1 短连接 vs 长连接
HTTP/1.0 短连接
建立连接 → 请求-响应 → 关闭连接
建立连接 → 请求-响应 → 关闭连接
建立连接 → 请求-响应 → 关闭连接每个请求都要新建TCP连接,效率低。
HTTP/1.1 长连接(Keep-Alive)
建立连接 → 请求1-响应1 → 请求2-响应2 → ... → 请求N-响应N → 关闭连接一个TCP连接可发送多个请求,效率高。
9.2 无状态特性
HTTP是无状态协议,服务器不会保存客户端的状态信息。每次请求都是独立的,服务器无法识别请求是否来自同一个客户端。
9.3 Cookie和Session
由于HTTP是无状态协议,Cookie和Session技术用于在多次请求之间保持用户状态。
简要介绍:
| 特性 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端浏览器 | 服务器端 |
| 典型用途 | 记住登录状态、用户偏好 | 保存用户会话数据 |
| 安全性 | 较低(可被篡改) | 较高 |
Cookie工作流程:
第1次访问 服务器设置Cookie 后续访问
浏览器 ─────────> │ ┌─────────────┐ <───────── 浏览器
请求 │ │ 生成Cookie │ 请求+Cookie
│ └─────────────┘
│ │
│<────────┘
│ 响应(Set-Cookie)📚 注意:Cookie和Session的详细原理、安全机制以及实际应用将在后续会话技术专题课程中深入讲解。本节仅作概念性介绍。
本章小结
本章核心概念:HTTP 是 Web 世界中最基础的应用层协议,浏览器与服务器之间的大多数交互,都是基于“请求—响应”模型完成的。
你现在应该掌握:
- 能区分 CS 架构与 BS 架构,并说出各自特点
- 能拆解一个 URL,并说明各组成部分的作用
- 能读懂 HTTP 请求报文和响应报文的基本结构
- 能识别常见请求方法与状态码的业务含义
- 能理解 HTTP 无状态的特点,以及 Cookie / Session 为什么会出现
总结
| 知识点 | 核心内容 |
|---|---|
| CS架构 | 客户端/服务器,需安装专用软件 |
| BS架构 | 浏览器/服务器,无需安装 |
| HTTP协议 | 应用层协议,基于请求-响应模式 |
| URL组成 | 协议+域名+端口+路径+参数+锚点 |
| 请求方法 | GET获取,POST提交,PUT更新,DELETE删除 |
| 状态码 | 2xx成功,3xx重定向,4xx客户端错误,5xx服务器错误 |
| 连接管理 | HTTP/1.1长连接,无状态协议,Cookie维持会话 |
数据交换格式是指不同系统、设备或平台之间传递数据时采用的标准化数据组织形式,其核心作用是确保数据发送方和接收方能够准确理解数据的结构、含义和内容。
通俗来讲:就是不同设备、软件之间 “传递信息的统一写法”。就像寄快递时,不同东西要用不同包装(信封、纸箱、泡沫盒),数据交换格式就是给数据选个合适的 “包装方式”,让接收方顺利拆开看懂。
目前主要使用的是JSON(JavaScript Object Notation):格式要求严格但规则清晰

2.1 JSON介绍
JSON(JavaScript Object Notation)
是一种轻量级的数据交换格式,源于 JavaScript 对象语法,但独立于编程语言。它易读、易写,同时易于机器解析和生成,是网络数据传输(如 API 接口)和配置文件的首选格式。
举个例子,来定义一个Java类,并且提供对应的JSON字符串示例
Java类
// 定义对应类
class Company {
private String company;
private List<Employee> employees;
// getters/setters
}
class Employee {
private String name;
private int age;
primate boolean married;
// getters/setters
}JSON
{
"company": "王道",
"employees": [
{"name": "雪茄", "age": 31,"married":false},
{"name": "石头", "age": 32,"married":true}
]
}2.2 格式要求
JSON的格式要求
- 整体结构:要么是 “对象”,要么是 “数组”
JSON 的最外层只能是两种形式:
- 对象:用
{ }包裹,里面是 “键值对”(类似 “属性:值”),比如:{ "name": "张三", "age": 20 } - 数组:用
[ ]包裹,里面是一系列值(可以是数字、字符串、对象等),比如:[ "苹果", "香蕉", { "name": "橙子" } ]
- 键值对:键必须用双引号,值有类型限制
- 键(key):必须用双引号
""包裹,不能用单引号或不加引号,比如{"name": "张三"}正确,{'name': "张三"}或{name: "张三"}错误。
- 值(value)
:只能是以下几种类型:
- 字符串(必须用双引号
""包裹,比如"hello") - 数字(整数或小数,比如
123、3.14,不能加引号) - 布尔值(
true或false,小写,不加引号) - 空值(
null,小写,不加引号) - 嵌套的对象(
{ }包裹) - 嵌套的数组(
[ ]包裹)
- 分隔与结尾:用逗号分隔,不能有多余逗号
- 多个键值对或数组元素之间,必须用逗号
,分隔,比如:{ "name": "张三", "age": 20 }(正确) - 最后一个元素后面不能有逗号,比如:
{ "name": "张三", "age": 20, }(错误,多了个逗号)
2.3 常用Json解析
- fastjson是阿里巴巴的开源JSON解析库
- Gson是Google提供的JSON解析库
- Jackson是SpringBoot默认序列化JSON解析库
性能方面,Jackson和FastJson差距很小,Jackson是SpringBoot默认的序列化库,也是最稳定的一个,FastJson由于频繁被曝出漏洞且作者没有那么多精力维护,所以默认序列方式还是选择Jackson最好。
分别对应的依赖
<!--Gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!--jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>我们使用一下Jackson来完成JSON转换
2.4 Jackson常规使用
首先实例化一个Jackson中用来做序列化的对象ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();然后使用ObjectMapper提供的方法完成转换
| 方法名 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| writeValueAsString(Object object):String | Object:被转换的对象 | String:转换的结果 | 将Object转换为JSONString |
| readValue(String content,Class\<T> valueType):T | String content:被转换的字符串;Class\<T> valueType:指定接收返回值的类型 | 泛型:在第二个参数被指定的类型 | 将JSONString |
接着构建一个场景:提供一个User对象,体现出这几项信息
姓名,密码,年龄,爱好(多个),房产(多个)
我们定义的User类如下
/**
* @Data提供getter/setter方法
*/
@Data
public class User {
String username;
String password;
Integer age;
String[] hobbies;
List<House> houseList;
}
/**
* @AllArgsConstructor 提供有参构造方法的同时提供无参构造方法
* @NoArgsConstructor 提供无参构造方法
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class House {
String location;
Double price;
}接收初始化一个User实例,用来做接下来的转换
private static User user = new User();
static {
user.setUsername("李雷");
user.setPassword("123456");
user.setAge(25);
user.setHobbies(new String[]{"唱","跳","RAP","篮球"});
List<House> houses = new ArrayList<>();
houses.add(new House("软件新城C13二楼", 1_000_000.0));
houses.add(new House("软件新城C13四楼", 1_250_000.0));
user.setHouseList(houses);
}接着使用的转换代码如下
public static void main(String[] args) throws JsonProcessingException {
// 将user实例转换为JSON字符串
String userJsonString = objectMapper.writeValueAsString(user);
System.out.println("JSON:" + userJsonString);
// 将JSON字符串转换为user实例
User user = objectMapper.readValue(userJsonString, User.class);
System.out.println("toString:" + user);
}打印结果如下
JSON:{"username":"李雷","password":"123456","age":25,"hobbies":["唱","跳","RAP","篮球"],"houseList":[{"location":"软件新城C13二楼","price":1000000.0},{"location":"软件新城C13四楼","price":1250000.0}]}
toString:User(username=李雷, password=123456, age=25, hobbies=[唱, 跳, RAP, 篮球], houseList=[House(location=软件新城C13二楼, price=1000000.0), House(location=软件新城C13四楼, price=1250000.0)])注意:一定要增加上无参构造和getter/setter方法
2.5 指定日期格式
比如我们在User中增加一个成员变量Date birthday,我们重新完成转换
@Data
public class User {
String username;
String password;
Integer age;
String[] hobbies;
List<House> houseList;
Date birthday;
}user.setBirthday(new Date());转换
String userJsonString = objectMapper.writeValueAsString(user);
System.out.println("JSON:" + userJsonString);{
"username":"李雷",
"password":"123456",
"age":25,
"hobbies":["唱","跳","RAP","篮球"],
"houseList":[
{"location":"软件新城C13二楼","price":1000000.0},
{"location":"软件新城C13四楼","price":1250000.0}
],
"birthday":1678762314011
}大家可以看到的birthday的值是时间戳,我们通常看到的一些时间的话,通常看到是“2015-09-27”,“2009-03-26 15:26:32”之类的,也是针对于birthday这样的Date类型的成员变量,我们在值转换过程中可以指定格式
然后在初始化的时候增加以下代码
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));{"username":"李雷","password":"123456","age":25,"hobbies":["唱","跳","RAP","篮球"],"houseList":[{"location":"软件新城C13二楼","price":1000000.0},{"location":"软件新城C13四楼","price":1250000.0}],"birthday":"2022-03-14"}@JsonFormat给特定的成员变量指定类型