AI开发学习笔记
计算机网络
工作流程图

TCP三握四挥
TCP 三次握手(建立连接)
三次握手的目的是建立可靠的双向连接,确认双方的收发能力都正常

客户端 (Client) 服务端 (Server) | | | 1) SYN, seq=x | |--------------------------------------->| 第一次握手 | | | 2) SYN+ACK, seq=y, ack=x+1 | |<---------------------------------------| 第二次握手 | | | 3) ACK, ack=y+1 | |--------------------------------------->| 第三次握手 | | 连接建立 (ESTABLISHED) 连接建立 (ESTABLISHED)
每一步的含义:
| 次数 | 方向 | 报文 | 含义 |
| 第1次 | 客户端→服务端 | SYN | "我想连接,我的初始序号是 x" |
| 第2次 | 服务端→客户端 | SYN+ACK | "收到,同意连接,我的初始序号是 y,确认你的 x" |
| 第3次 | 客户端→服务端 | ACK | "收到你的确认,正式开始" |
为什么需要三次?两次无法让服务端确认客户端的"接收能力"正常,可能因网络延迟的旧连接请求导致服务端错误建立连接。三次握手能避免历史失效连接和资源浪费。
TCP 四次挥手(断开连接)
四次挥手的目的是释放双向连接。因为 TCP 是全双工的,每个方向都要单独关闭。
客户端 (Client) 服务端 (Server) | | | 1) FIN, seq=u | |--------------------------------------->| 第一次挥手 | | (服务端进入 CLOSE_WAIT) | 2) ACK, ack=u+1 | |<---------------------------------------| 第二次挥手 | | | (服务端可能还在发数据) | | | | 3) FIN, seq=w | |<---------------------------------------| 第三次挥手 | | | 4) ACK, ack=w+1 | |--------------------------------------->| 第四次挥手 | (客户端 TIME_WAIT,等 2MSL) | | | 连接关闭 (CLOSED) 连接关闭 (CLOSED)
每一步的含义:
| 次数 | 方向 | 报文 | 含义 |
| 第1次 | 客户端→服务端 | FIN | "我没有数据要发了,请求关闭" |
| 第2次 | 服务端→客户端 | ACK | "知道了"(但我可能还有数据要发) |
| 第3次 | 服务端→客户端 | FIN | "我也发完了,请求关闭" |
| 第4次 | 客户端→服务端 | ACK | "知道了,再见" |
为什么挥手是四次而握手是三次?握手时服务端可以把 SYN 和 ACK 合并成一个报文;挥手时服务端收到 FIN 后,可能还有数据没发完,所以 ACK 和 FIN 要分开发送,无法合并,于是变成四次。
简单总结:握手挥手是两台主机的操作系统内核在传输层用 TCP 协议私下完成的"打招呼"和"道别",应用程序只是触发它,路由器只是帮忙传话。
HTTP和HTTPS
地址(URL)→ HTTP 请求报文 的加工过程
地址是人写给浏览器看的"简短指令",报文是浏览器写给服务器看的"完整说明书"。 中间这层"翻译加工"由浏览器完成。
┌─────────────────────────┐ │ 你输入的地址 (URL) │ ← 短短一行 │ https://www.example.com │ │ /news?id=123 │ └─────────────────────────┘ │ ▼ 浏览器拆解 + 加工 + 补充 ┌─────────────────────────┐ │ 浏览器加工 │ │ · 域名 → Host │ │ · 路径 → 请求行 │ │ · 自动补:浏览器型号 │ │ · 自动补:可接受格式 │ │ · 自动补:本地 Cookie │ │ · 自动补:连接方式 │ └─────────────────────────┘ │ ▼ 组装成完整报文 ┌───────────────────────────────────────────┐ │ 发出去的 HTTP 请求报文 │ ├───────────────────────────────────────────┤ │ GET /news?id=123 HTTP/1.1 ← 来自地址 │ │ Host: www.example.com ← 来自地址 │ │ ───────────────────────────────────────── │ │ User-Agent: Mozilla/5.0 ... ← 浏览器补 │ │ Accept: text/html ... ← 浏览器补 │ │ Accept-Language: zh-CN ... ← 浏览器补 │ │ Cookie: session_id=abc123 ... ← 浏览器补 │ │ Connection: keep-alive ← 浏览器补 │ └───────────────────────────────────────────┘
一句话总结这张图:报文里只有头两行真正来自你输入的地址,剩下一大半都是浏览器根据自己的状态、配置、本地存储自动塞进去的。所以地址短、报文长。
报文存在哪里:报文没有固定的存放位置,它像一封信一样在"写信人内存 → 邮路 → 收信人内存"之间流动,读完即焚,不会被永久保存(服务器日志只记录摘要,不是报文本身)。

Token
流派 1:不透明 token(没有含义的随机字符串,用户信息不在 token 里,存在服务器的数据库/缓存里)
流派 2:自包含 token(本身就装着用户信息)—— JWT (JSON Web Token)就属于这一类
请求头(Header) 放的是 “关于请求的元数据”(比如谁发的、什么格式、多长)
请求体(Body) 放的是 “请求要发送的实际数据”(比如用户名、密码、图片、文件)
Token 是“身份证”,只放核心且不可变的信息(userId、username)。需要更多信息时,拿身份证号去查档案室(Redis/DB)。
Authorization:Bearer eyJhbGciOiJIUzI1Ni...(token)
你前面学的HTTP Header:
Method URL Headers Body
Token就属于:
Header
中的一个字段。
JWT 是什么
JWT = JSON Web Token,是一种有标准格式的 token。它的特点是:把用户信息直接编码进 token 字符串里,并且用签名保证不被篡改。
一个 JWT 长这样,由三段组成,用点 . 隔开:
eyJhbGciOiJ.eyJzdWIiOiIxMDAxI.SflKxwRJSMeKKF2QT4 └─── Header ──┘└──── Payload ────┘└─── Signature ──┘ (头部) (载荷) (签名)
这三段分别是:
第一段 Header(头部)——说明用什么算法签名。解码后是:
json
{ "alg": "HS256", "typ": "JWT" }
第二段 Payload(载荷)——真正装数据的地方,存用户信息。解码后类似:
json
{ "sub": "1001", // 用户ID "name": "张三", "role": "admin", "exp": 1718000000 // 过期时间 }
第三段 Signature(签名)——最关键的防伪部分。服务器用一个只有自己知道的密钥,对前两段做加密运算生成签名。
Header + Payload ──┐ ├──► 用服务器密钥运算 ──► 签名 服务器密钥(secret)─┘ (只有服务器有密钥)
token(令牌,抽象概念) "一张证明已登录的通行证" │ ┌─────────────┴─────────────┐ ▼ ▼ 不透明 token 自包含 token (Session Token) 信息装在 token 里 空壳,要查数据库 │ ▼ ┌──────────┐ │ JWT │ ← 最主流的标准实现 │ 三段式+签名 │ └──────────┘

在 JWT 鉴权体系中,Filter 或 Interceptor 会先解析 Token,得到当前登录用户信息(如 userId、username、roles、permissions 等),然后封装成 LoginUser 对象存入 ThreadLocal。后续 Controller、Service、DAO 都可以通过 ThreadLocal 获取当前用户,而不需要层层传参。请求结束后需要调用 remove() 清理,避免线程池复用导致用户信息串号。正常情况下 ThreadLocal 绝对不应该存密码。
接口设计(RESTful API)
一、DTO数据
很多初学者以为:路径 = 地址(只是个标识),数据 = 都在请求体里。
实际上,一个 HTTP 请求里,前端能往后端传数据的位置有四个,路径只是其中之一:
POST /orders/123/cancel?reason=outofstock HTTP/1.1 └──────┬──────┘ └────────┬─────────┘ ①路径参数 ②查询参数 (Path) (Query) Authorization: Bearer eyJhbGc... ← ③请求头 (Header) Content-Type: application/json { ← ④请求体 (Body) "note": "用户主动取消", "refund": true }
所以"前端接收的数据"和"路径"的关系是:路径承载了一部分数据(哪个订单、做什么动作),另外几部分数据放在查询参数、请求头、请求体里。它们是分工协作的,合起来才是完整的一次请求。
二、数据各个位置分别放什么
理解了"该把什么放哪",你就理解了接口设计的精髓。
① 路径参数(Path)—— 定位"哪个资源、做什么"
以 POST /orders/{id}/cancel 为例:
POST /orders/ {id} /cancel │ │ │ │ 动作 资源类型 具体哪个 子操作 "订单" 订单ID
-
/orders表示"订单"这类资源。 -
{id}是路径参数,运行时会被替换成具体的订单号,比如/orders/123/cancel。它的作用是精确定位到某一个资源——是 123 号订单,不是别的。 -
/cancel表示对这个订单执行"取消"动作。
所以路径里的 {id} 是实打实的数据,后端会把它取出来用("去数据库找 123 号订单")。它不是装饰,是定位数据。
关键判断标准:能唯一标识资源的、属于资源层级结构的东西,放路径里。订单 ID、用户 ID 这种就该放路径。
② 查询参数(Query,问号后面的)—— 修饰、筛选、配置
放在 ? 后面,用来对操作做"补充说明",常见于筛选、排序、分页:
GET /orders?status=paid&page=2&sort=time └─────────┬──────────────┘ 筛选已支付的、第2页、按时间排序
-
它通常是可选的,不影响"定位哪个资源",只影响"返回什么样的结果"。
-
你那个取消的例子里,如果取消需要带个原因,也可以写成
POST /orders/123/cancel?reason=outofstock。
关键判断标准:可选的、用于过滤/排序/分页/配置的参数,放查询参数里。
③ 请求头(Header)—— 元信息、身份认证
放的是"关于这次请求本身"的信息,而不是业务数据。最典型的就是身份令牌(还记得前面学的 token 吗?就放这里):
Authorization: Bearer eyJhbGc... ← 我是谁、有没有权限 Content-Type: application/json ← 我发的是什么格式
关键判断标准:身份认证、内容格式、缓存控制等"元信息",放请求头里。
④ 请求体(Body)—— 真正的业务数据
这是放"操作所需的实质内容"的地方,通常是 JSON。比如取消订单时,附带的备注、是否退款:
json
{ "note": "用户主动取消", "refund": true, "refundAmount": 99.00 }
关键判断标准:创建/修改资源所需的、结构复杂的业务数据,放请求体里。注意:GET 请求一般不带请求体,所以 GET 的数据都靠路径 + 查询参数传。
三、URL 设计规范
这是日常工作中最常用的部分。规则不多,但要遵守:
用名词,且用复数
✅ GET /books ❌ GET /getBooks
✅ GET /books/15 ❌ GET /book?id=15(也能用,但风格上前者更标准)
用路径层级表达从属关系
GET /authors/7/books 7 号作者的所有书
GET /orders/100/items/3 100 号订单里的第 3 个条目
层级别嵌套太深,一般两层到顶。再深就拆开:GET /comments?bookId=15 比 /authors/7/books/15/comments 好维护。
过滤、分页、排序用查询参数,不要塞进路径
GET /books?category=sci-fi&sort=-publishedAt&page=2&size=20
路径定位"是哪个/哪类资源",查询参数描述"要怎么筛选这批资源"。
实在表达不了的动作,可以妥协
REST 不是教条。像"搜索""登录""转账"这种很难映射成资源增删改查的操作,业界常见做法是把动作名词化或者直接加动词子路径:
POST /search
POST /accounts/42/transfer
POST /auth/login
能 REST 就 REST,不能就务实,没人会因为这个扣你工资。
四、HTTP请求方式
五、状态码:用 HTTP 自带的语义表达结果
反模式是所有响应都返回 200,然后在 body 里塞 {"code": 50012, "msg": "失败"},客户端得自己解析才知道成败。REST 风格是让 HTTP 状态码先说话:
成功类(2xx)
200 OK —— 通用成功
201 Created —— 创建成功(POST 之后返回它,并在 Location 响应头里给出新资源的 URL)
204 No Content —— 成功但没内容可返回(DELETE 成功后的典型选择)
客户端的锅(4xx)
400 Bad Request —— 请求格式或参数不对
401 Unauthorized —— 没登录/没带凭证(名字起得不好,其实是"未认证")
403 Forbidden —— 登录了,但你没权限干这事
404 Not Found —— 资源不存在
409 Conflict —— 冲突(比如重复创建同名资源)
422 Unprocessable Entity —— 格式对但业务校验不过(比如年龄填了 -1)
429 Too Many Requests —— 触发限流
服务端的锅(5xx)
500 Internal Server Error —— 服务端炸了
503 Service Unavailable —— 暂时不可用(过载/维护)
记住 401 vs 403、400 vs 422 这两对的区别,能避开大部分状态码误用。
六、错误响应体也要设计
状态码说明"错了、谁的错",body 说明"具体错在哪"。给个统一的错误格式:
{ "timestamp": "2026-06-11T10:30:00Z", "status": 422, "error": "VALIDATION_FAILED", "message": "图书价格不能为负数", "path": "/api/v1/books", "details": [ { "field": "price", "issue": "must be >= 0" } ] }
要点:机器能读的错误码(error)+ 人能读的说明(message)+ 字段级细节(details)。整个 API 的错误格式必须统一,不要 A 接口一种格式 B 接口另一种。
企业级设计
很多互联网公司会这样:
{ "success": false, "code": "USER_PASSWORD_ERROR", "message": "用户名或密码错误", "timestamp": "2026-06-12T11:30:00Z" }
例如:
{ "success": false, "code": "USER_NOT_FOUND", "message": "用户不存在", "timestamp": "2026-06-12T11:30:00Z" }
统一格式后:
成功:
{ "success": true, "data": { ... } }
失败:
{ "success": false, "code": "USER_NOT_FOUND", "message": "用户不存在" }
七、速记清单
-
URL 是名词复数,动作靠 HTTP 方法表达
-
GET 绝不改数据;PUT/DELETE 幂等可重试,POST 不幂等要防重
-
状态码先说话:201 创建、204 无内容、401 未认证、403 无权限、422 业务校验失败
-
错误响应全局统一格式
-
无状态:凭证放 Authorization 头,不依赖服务端 Session
-
URL 带版本号 /api/v1/...
-
分页过滤排序走查询参数
数据库学习
候选键是“所有能当主键的选项”,主键是“你选中的那一个”

java 高级后端学习路径
如果你未来想走 Java 高级后端(Spring Cloud、微服务、分布式系统、架构设计),下一步应该学习的是:
1. DDD(领域驱动设计) 2. 状态机设计 3. RBAC权限模型 4. 幂等设计 5. 分布式事务 6. 缓存设计 7. MQ驱动架构 8. 微服务接口设计
因为这些才是高级工程师和 CRUD 工程师之间真正的差距。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐





所有评论(0)