📌 核心结论(TL;DR):
HTTP 缺陷:无状态、明文传输。
Cookie :数据存在客户端,服务器通过
Set-Cookie响应头写入,浏览器后续请求自动带上。Session :数据存在服务器端。服务器只给客户端发一个
SessionID,保证私密数据不泄露。HTTPS :HTTP + 加密层(TLS/SSL)。采用非对称加密协商密钥,对称加密传输数据。
CA 证书:解决"中间人攻击"的终极武器。浏览器内置 CA 公钥,用来验证服务器证书的真伪。
一、 为什么需要 Cookie 和 Session?
HTTP 协议是"无状态"的,每次请求都是独立的,服务器记不住你是谁。为了解决"保持登录状态"等问题,引入了 Cookie 和 Session。
二、 Cookie 与 Session 的恩怨情仇
1. Cookie(客户端存储)
-
原理 :服务器在响应头中添加
Set-Cookie: username=zhangsan。浏览器保存后,下次请求自动携带。 -
缺点:存在客户端,容易被篡改或窃取;如果直接存密码,极其危险。
2. Session(服务器端存储)
-
原理 :服务器生成随机
SessionID(如12345),保存在服务器内存中(记录用户信息)。响应给浏览器,让浏览器存进 Cookie。 -
优点:真实数据在服务器,客户端只有一个 ID,安全性大幅提升。
三、 代码实战:服务端下发 Cookie
cpp
// 在响应头中加入 Set-Cookie
snprintf(response, sizeof(response),
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Set-Cookie: username=zhangsan; expires=Fri, 18 Dec 2026 12:00:00 UTC; path=/\r\n"
"Content-Length: %ld\r\n"
"\r\n"
"%s", strlen(body), body);
踩坑:expires 的日期格式极其严格,必须是 UTC 或 GMT 格式,推荐使用 gmtime() 生成。
四、 HTTPS:不仅仅是加个 S
HTTP 是明文传输,运营商或黑客可以轻易进行中间人劫持。HTTPS 在 HTTP 和 TCP 之间加了一层加密协议(SSL/TLS)。
-
对称加密:加密解密用同一把钥匙,速度快,但钥匙容易泄露。
-
非对称加密:公钥加密,私钥解密。安全但极慢。
-
HTTPS 方案 :非对称加密 + 对称加密 + CA证书。
-
客户端请求服务器,服务器返回 CA 证书。
-
客户端校验证书(防中间人),获取服务端真实公钥。
-
客户端生成一个对称密钥,用服务端公钥加密发给服务端。
-
服务端用私钥解密拿到对称密钥,后续双方全用对称加密通信。
-
五、 为什么中间人改不了 CA 证书?
证书里包含了网站的域名、公钥等明文信息,以及 CA 机构用自己的私钥加密的签名。黑客没有 CA 的私钥,无法伪造签名;如果强行篡改明文,浏览器验签时哈希值对不上,立刻报警。
自我实战操作
由于完整的 HTTP 框架加上 Session 管理代码量极大,咱们直接在刚才的【极简 HTTP 服务器】代码上做修改,看看如何在浏览器里写入 Cookie。
bash
cd ~/linux_study/network_test
vim http_cookie_server.c
(基于上节课的代码,只改了构造响应报文那几行)
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8081
#define BUF_SIZE 4096
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_fd, 5);
printf("HTTP Cookie 服务器启动,访问: http://127.0.0.1:%d\n", PORT);
while (1) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
char buffer[BUF_SIZE] = {0};
int n = read(client_fd, buffer, sizeof(buffer) - 1);
if (n > 0) {
// 提取并打印浏览器发来的 Cookie
char *cookie_ptr = strstr(buffer, "Cookie:");
if (cookie_ptr) {
// 优化点1:把后面的 \r\n 截断,避免终端打印一堆乱码
char *end_ptr = strstr(cookie_ptr, "\r\n");
if (end_ptr) *end_ptr = '\0';
printf("【抓到 Cookie】 %s\n", cookie_ptr);
}
}
// 构造响应报文
const char *body = "<html><body><h1>Cookie Test Page</h1><p>刷新页面,看看终端是否抓到了Cookie</p></body></html>";
char response[2048]; // 确保缓冲区足够大
// 优化点2:使用 snprintf 防止溢出;修正了 2026-12-18 对应的星期是 Fri (周五)
snprintf(response, sizeof(response),
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Set-Cookie: username=zhangsan; expires=Fri, 18 Dec 2026 12:00:00 UTC; path=/\r\n"
"Content-Length: %ld\r\n"
"Connection: close\r\n"
"\r\n"
"%s", strlen(body), body);
write(client_fd, response, strlen(response));
close(client_fd);
}
return 0;
}
测试与排坑
bash
gcc http_cookie_server.c -o http_cookie_server
./http_cookie_server

操作步骤:
-
在浏览器访问
http://127.0.0.1:8081。 -
按下 F12 打开开发者工具,找到 "应用(Application)" -> "Cookie" 。你会看到里面已经存了
username=zhangsan。 -
刷新页面 。回到 Linux 终端,你会看到服务器打印出了
【抓到 Cookie】 Cookie: username=zhangsan。 -
排坑 :如果你发现 Cookie 没写进去,或者过期时间报错,检查
expires的时间格式。它必须是 Fri, 18 Dec 2026 12:00:00 UTC这种严格的 RFC 1123 格式(GMT 或 UTC),千万不能用中文,也不能用2026-12-18。