当前位置: 首页 > news >正文

详细说明http协议特别是conten-length和chunk编码,并且用linux的命令行演示整个过程

下面把 HTTP 请求体的两种主流传输方式——Content-LengthTransfer-Encoding: chunked——讲清楚;再给一套你在 Linux 命令行就能复现的端到端演示(含“最小服务器 + 客户端原始包 + curl 流式上传”)。照抄命令即可跑通。


一、HTTP 基本结构(快速复盘)

<起始行>        // 例:POST /upload HTTP/1.1
<首部>          // 若干行 “Key: Value”// 重要:每行以 \r\n 结尾
\r\n             // 空行:首部结束
<消息体/请求体>   // 可为空

HTTP/1.1 如何知道“请求体到哪儿结束”? 有三种合法方式(必须至少有一种):

  1. Content-Length:首部声明字节数,服务器据此读固定长度。

  2. Transfer-Encoding: chunked:分块传输。体被切成若干 chunk,每块前先写十六进制长度行,结尾用0 块收尾。

  3. 连接关闭(HTTP/1.0 或显式 Connection: close 且无 1/2),靠对端断开判定结束(现代服务端很少用于请求体)。

注意:在 HTTP/1.1 中,Content-LengthTransfer-Encoding: chunked 互斥,不能同时出现。


二、Content-Length:一次说清

  • 用在:发送方提前知道整个体的大小(例如完整 JPEG/JSON)。

  • 规则:首部里写 Content-Length: N,体就必须恰好 N 字节;多/少 1 个字节都是协议错误。

  • 优势:简单、高效、好缓存。

  • 常见坑

    • 体里不能额外多出 \r\n(很多串口/封装函数会自动追加,导致“宣称 512 实发 514/539”)。

    • N 与实际长度不符 → 服务器 4xx/5xx,或客户端阻塞/截断。


三、Chunked:流式说清

  • 用在:发送方一边产生数据,一边发送(边拍照边上传、未知总长)。

  • 格式(每块):

    <chunk-size-HEX>\r\n
    <chunk-data-RAW>\r\n
    ...
    0\r\n
    \r\n
    

    例:发送 5 字节 Hello

    5\r\n
    Hello\r\n
    0\r\n
    \r\n
    
  • 特点:不需要 Content-Length;可以追加trailer 首部(需要 Trailer: 声明)。

  • 常见坑

    • 每块长度是十六进制1024 字节应写 400\r\n),且每块数据后还有 \r\n

    • 最后必须发 0 块0\r\n\r\n)。缺它,服务器会一直等,最终 408/超时。

    • 一些代理/网关对 HTTP/2 会改用帧分段(HTTP/2 没有 chunked 这个概念),但请求侧写法不变,由库/代理负责转换。


四、Linux 命令行完整演示

A. 准备一个最小“镜像”服务器(仅打印你发来的原始包)

方式 1(推荐):socat 打印所有收发

# 安装:Ubuntu/Debian -> sudo apt-get install socat
socat -v -v tcp-l:8080,reuseaddr,fork -  # 监听 8080,打印客户端发来的所有字节

方式 2(简易):nc 监听(不打印十六进制方向箭头,够用)

# BusyBox/常见发行版自带
nc -lv 8080

这只是“原始包观察器”。收到请求后你还需要回一段固定响应,避免客户端卡住。我们下面的示例会把响应一并通过管道发出。


B. 用 Content-Length 方式发送一个 POST(纯手工包)

1) 构造一个“固定响应”的服务器(能回 200 OK)

开一个终端执行:

# 简单回“HTTP 200 OK”的服务器:收到请求后回固定响应(含 Content-Length)
while true; do \{ printf 'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 2\r\nConnection: close\r\n\r\nOK'; } \| socat -v -v - tcp-l:8080,reuseaddr; \
done

(你会在终端看到 socat 打印的客户端→服务器服务器→客户端的原始字节)

2) 客户端:计算体长并发送

在另一个终端:

body='hello=world&n=42'                       # 体内容(17字节)
len=$(printf "%s" "$body" | wc -c)            # 计算字节数{ printf "POST /upload HTTP/1.1\r\n"printf "Host: localhost:8080\r\n"printf "Content-Type: application/x-www-form-urlencoded\r\n"printf "Content-Length: %d\r\n" "$len"printf "Connection: close\r\n"printf "\r\n"printf "%s" "$body"                          # 注意:这里只能发 RAW,不能多发 \r\n
} | nc 127.0.0.1 8080

观察点

  • 服务器端窗口会打印你发来的原始请求;Content-Length: 17 与体字节数严格一致

  • 客户端会收到 OK


C. 用 chunked 方式发送(纯手工包)

关闭前面的 socat 服务器,换一个“回包服务器”(一样即可):

while true; do \{ printf 'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 2\r\nConnection: close\r\n\r\nOK'; } \| socat -v -v - tcp-l:8080,reuseaddr; \
done

客户端:按块发送(两块 + 终止 0 块)

chunk1='Hello '
chunk2='World!'
# 计算长度 -> 转十六进制
h1=$(printf "%s" "$chunk1" | wc -c | awk '{printf "%X",$1}')
h2=$(printf "%s" "$chunk2" | wc -c | awk '{printf "%X",$1}'){ printf "POST /stream HTTP/1.1\r\n"printf "Host: localhost:8080\r\n"printf "Transfer-Encoding: chunked\r\n"printf "Content-Type: text/plain\r\n"printf "Connection: close\r\n"printf "\r\n"printf "%s\r\n" "$h1"      # 块1长度(十六进制)+ CRLFprintf "%s\r\n" "$chunk1"  # 块1数据 + CRLFprintf "%s\r\n" "$h2"      # 块2长度(十六进制)+ CRLFprintf "%s\r\n" "$chunk2"  # 块2数据 + CRLFprintf "0\r\n\r\n"         # 终止块
} | nc 127.0.0.1 8080

观察点

  • 服务器端窗口能看到“Transfer-Encoding: chunked”首部,以及你发送的块格式(长度行是 ASCII 十六进制数字)。

  • 客户端仍收到 OK。如果你漏了最后的 0\r\n\r\n,服务器会卡住等你结束。


D. 用 curl 演示“自动 chunked”(从标准输入流式上传)

curl 无法提前获知体长度(例如从管道/标准输入 @-),在 HTTP/1.1 下会自动选择 chunked

1) 起一个能回显请求信息的“迷你服务器”

下面是一行 Python(不依赖第三方库),把请求信息打印到终端并回 200:

python3 - <<'PY'
import sys, socket
s=socket.socket(); s.bind(('0.0.0.0',8080)); s.listen(1)
print('listening 8080...')
while True:c,_=s.accept(); data=b''# 读到一个空行,拿到首部(简单做法,演示用)while b'\r\n\r\n' not in data:data+=c.recv(4096)sys.stdout.buffer.write(b'\n--- REQUEST ---\n'+data); sys.stdout.flush()# 回一个固定响应c.sendall(b'HTTP/1.1 200 OK\r\nContent-Length:2\r\nConnection: close\r\n\r\nOK')c.close()
PY

2) 用 curl 发送“未知长度”的体(自动 chunked)

printf 'Hello via curl streaming\n' \
| curl -v -X POST --http1.1 --data-binary @- http://127.0.0.1:8080/stream

观察点

  • curl -v 输出会显示:> Transfer-Encoding: chunked(以及可能的 Expect: 100-continue)。

  • 服务器终端能看到完整首部与(如果你扩展了 server 继续读 body)分块体。上面简化的 server 没继续读体,只演示首部;在真实服务端会按 chunked 规则逐块读取。

想让 curl 用 Content-Length:把数据存成文件,curl -v --data-binary @file,curl 会自动算长度并发 Content-Length(若 HTTP/1.1 且来源是 stdin,才会偏向 chunked)。


五、对照你的嵌入式场景(重点提醒)

  • Content-Length 模式:计算整个 JPEG/表单体的总长度,填到首部;数据发送必须“裸字节”,不能自动追加 CRLF。

  • Chunked 模式:每块先告诉对端块长(十六进制),再发原始块数据,块后 \r\n,最后 0\r\n\r\n;很多 蜂窝 AT 模组会替你写“块头/块尾”,你只需:

    1. 用命令声明“下一块有 N 字节”;

    2. 原样发送 N 个字节(裸发、不加 CRLF)

    3. 按手册发送终止(有的要你发 0\r\n\r\n,有的只要一个 \r\n)。

  • 两种方式都要避免:把数据当“AT 命令”发(会自动加 \r\n),导致宣称 N 实发 N+2,服务端 500/客户端 ERROR。


六、排错速查(遇到 4xx/5xx/超时)

  • Content-Length 不符:服务器日志常见 “request body too large/too small/invalid content-length”。

  • Chunked 终止缺失:没有 0\r\n\r\n,服务器等到超时。

  • 额外 CRLF:体尾多出 2 字节,下一条请求/命令被“吞/粘”。

  • HTTP/2 差异:HTTP/2 没有 chunked 首部,改用帧;客户端(curl/库)会自动处理,不要手写 Transfer-Encoding 给 HTTP/2。


需要我把上面的命令做成一页式测试脚本test_http.sh),你拷过去一键跑三种演示(Content-Length、chunked、curl 流式)吗?也可以顺手给你一个能完整接收 chunked 并回显体长度的 30 行小 Python 服务器以便联调。

http://www.dtcms.com/a/342606.html

相关文章:

  • Python读取和设置PNG图片的像素值
  • 软件漏洞扫描的测试内容(二)
  • DzzOffice V2.3.7 核心功能升级与关键问题修复,体验全面优化!
  • 计算机网络-1——第一阶段
  • 【苹果软件】Prism Mac 9.4苹果系统免费安装包英文版 Graphpad Prism for Mac 9.4软件免费下载与详细图文教程!!
  • UGUI源码剖析(12):实战演练——从零构建一个健壮的Gradient顶点特效
  • 虚幻基础:目标值之间的过渡
  • 数字货币发展存在的问题:交易平台的问题不断,但监管日益加强
  • C++ string类(c_str , find和rfind , npos , find_first_of)
  • DeepSeek V3.1正式发布,专为下代国产芯设计
  • 【LeetCode 热题 100】322. 零钱兑换——(解法二)自底向上
  • 2025年物流大数据分析的主要趋势
  • 血缘元数据采集开放标准:OpenLineage Dataset Facets
  • Python-Pandas GroupBy 进阶与透视表学习
  • 如何用算力魔方4060安装PaddleOCR MCP 服务器
  • 实现自己的AI视频监控系统-第一章-视频拉流与解码3
  • JavaWeb前端03(Ajax概念及在前端开发时应用)
  • Windows下,将本地视频转化成rtsp推流的方法
  • 高效处理NetCDF文件经纬度转换:一个纯CDO驱动的Bash脚本详解
  • GitHub 热榜项目 - 日榜(2025-08-21)
  • 009.Redis Predixy + Sentinel 架构
  • 深度卷积神经网络AlexNet
  • 【NVIDIA-B200】生产报错 Test CUDA failure common.cu:1035 ‘system not yet initialized‘
  • Docker 搭建 Gitlab 实现自动部署Vue项目
  • NW755NW776美光固态闪存NW863NX595
  • 【永洪BI】报告脚本-JavaScript使用【完整版】
  • Vue 项目中父子传值使用Vuex异步数据不更新问题
  • Postman来做API安全测试:身份验证缺陷漏洞测试
  • 药品追溯码(溯源码)采集系统(二):门诊发药后端
  • 【Linux系统】进程信号:信号的产生和保存