计算机网络

工作流程图

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 设计规范

这是日常工作中最常用的部分。规则不多,但要遵守:

  1. 用名词,且用复数

    ✅ GET /books ❌ GET /getBooks

    ✅ GET /books/15 ❌ GET /book?id=15(也能用,但风格上前者更标准)

    1. 用路径层级表达从属关系

      GET /authors/7/books 7 号作者的所有书

      GET /orders/100/items/3 100 号订单里的第 3 个条目

      层级别嵌套太深,一般两层到顶。再深就拆开:GET /comments?bookId=15 比 /authors/7/books/15/comments 好维护。

      1. 过滤、分页、排序用查询参数,不要塞进路径

        GET /books?category=sci-fi&sort=-publishedAt&page=2&size=20

        路径定位"是哪个/哪类资源",查询参数描述"要怎么筛选这批资源"。

        1. 实在表达不了的动作,可以妥协

          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": "用户不存在" }

                七、速记清单

                1. URL 是名词复数,动作靠 HTTP 方法表达

                2. GET 绝不改数据;PUT/DELETE 幂等可重试,POST 不幂等要防重

                3. 状态码先说话:201 创建、204 无内容、401 未认证、403 无权限、422 业务校验失败

                4. 错误响应全局统一格式

                5. 无状态:凭证放 Authorization 头,不依赖服务端 Session

                6. URL 带版本号 /api/v1/...

                7. 分页过滤排序走查询参数

                数据库学习

                候选键是“所有能当主键的选项”,主键是“你选中的那一个”

                java 高级后端学习路径

                如果你未来想走 Java 高级后端(Spring Cloud、微服务、分布式系统、架构设计),下一步应该学习的是:

                
                

                1. DDD(领域驱动设计) 2. 状态机设计 3. RBAC权限模型 4. 幂等设计 5. 分布式事务 6. 缓存设计 7. MQ驱动架构 8. 微服务接口设计

                因为这些才是高级工程师和 CRUD 工程师之间真正的差距。

                Logo

                AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

                更多推荐