前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >告别等待!HTTP分块Chunk传输让客户端响应更迅速数据即时呈现

告别等待!HTTP分块Chunk传输让客户端响应更迅速数据即时呈现

作者头像
Tinywan
发布2024-03-22 10:37:06
6240
发布2024-03-22 10:37:06
举报
文章被收录于专栏:开源技术小栈开源技术小栈

0x01 前言

HTTP分块传输(Chunked Transfer Encoding)是一种HTTP协议在数据传输时的编码格式,它允许将数据分成若干个块进行传输。每个传输的块都包含大小信息和实际的数据内容。让服务器发送大型文件或流数据时不必一开始就发送全部内容,而是可以分成一块一块的数据来发送。这样可以节省带宽和内存,特别是对于需要长时间连接的情况。

分块传输编码

代码语言:javascript
复制
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
 
25\r\n
This is the data in the first chunk.
\r\n
This is the second chunk in the response.
\r\n
The final chunk of data
\r\n
0\r\n
\r\n

在这个例子中,服务器使用Transfer-Encoding: chunked头部来告知客户端它将使用分块传输编码。响应的主体被分成多个块,每个块前面都有一个十六进制的数字标记其大小,紧跟着是十六进制的换行符\r\n。最后一个块的大小为0,表示数据已经结束,随后的\r\n是HTTP响应的最后的空行。

HTTP分块传输常用于在不知道响应内容长度情况下传输数据。例如,当服务器需要生成大量数据或数据需要动态生成时,它可以使用HTTP分块传输来在响应正在生成时向客户端传输数据。

HTTP分块传输中,每个块使用十六进制数表示的大小信息开头,并以'\r\n'换行符结束。该大小信息表示块包含的字节数。块的实际数据由该大小信息所指定的数量的字节组成,并以'\r\n'结束。最后一个块的大小信息为0,后面跟着一个空白的'\r\n',表示所有的块都已传输完成。

HTTP分块传输不仅适用于响应内容的传输,还可以用于请求数据的发送,在渗透的过程中,当我们遇到网站存在waf的情况,我们就可以利用HTTP分块传输来绕过waf的检测。

0x02 传输格式

HTTP分块传输是HTTP协议的一种数据编码方式,其传输格式如下:

代码语言:javascript
复制
chunk-size [ chunk-extension ] CRLF
chunk-data CRLF
...
chunk-size [ chunk-extension ] CRLF
chunk-data CRLF
0 CRLF
footer1-name: footer1-value CRLF
footer2-name: footer2-value CRLF
CRLF

其中,chunk-size表示当前块的字节数,并以十六进制数字表示,chunk-extension可选,表示当前块的扩展信息,使用分号和值进行分隔。CRLF表示回车换行符。chunk-data表示当前块的数据内容。所有块的数据内容累加起来就是完整的实体正文。

最后一个长度为0的块表示传输结束。

在传输过程中,每个块都需要以chunk-size开始并加上后缀CRLF,接下来是chunk-data,最后也要以CRLF结束。

如果有多个块,则依次传输,每个块之间也要用CRLF分隔。

在所有块发送完成后,可以选择添加一个或多个实体报头字段,这些字段被称为报尾或尾部(footers)。报尾同样需要以CRLF结束。

总之,HTTP分块传输用于在HTTP协议中动态传输数据,其传输格式是由块大小和块数据组成,可以在数据生成过程中逐步传输数据,提高效率和安全性。

0x03 应用案例

客户端和服务端在进行HTTP分块传输时,需要注意以下几点:

  1. 客户端需要在请求头部添加Transfer-Encoding: chunked,告知服务端使用分块传输方式。
  2. 服务端需要在响应头部添加Transfer-Encoding: chunked,告知客户端使用分块传输方式。
  3. 服务端需要将所有数据按照块的格式进行封装并发送给客户端。

服务端

服务端使用workerman/http-client实现。workerman/http-client 是一个异步http客户端组件。所有请求响应异步非阻塞,内置连接池,消息请求和响应符合PSR7规范。

使用 Moonshot 提供基于 HTTP 的 API 服务接入。【更多了解 Openai 异步客户端接入国产大模型 Kimi

安装
代码语言:javascript
复制
composer require workerman/http-client
发送HTTP分块Chunk数据
代码语言:javascript
复制
<?php
/**
 * @desc HTTP分块Chunk传输响应给客户端
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/3/21 22:53
 */
declare(strict_types=1);

require_once __DIR__. '/../vendor/autoload.php';

use Workerman\Connection\TcpConnection;
use Workerman\Http\Client;
use Workerman\Protocols\Http\Chunk;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Worker;

$worker = new Worker('http://0.0.0.0:8782/');
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    $http = new Client();
    $http->request('https://api.moonshot.cn/v1/chat/completions', [
        'method' => 'POST',
        'data' => json_encode([
            'model' => 'moonshot-v1-8k',
            'stream' => true,
            'messages' => [['role' => 'user', 'content' => 'PHP语言实现一个冒泡排序算法']],
        ]),
        'headers' => [
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer sk-xxxxxxxxxxxxxxxx',
        ],
        'progress' => function ($buffer) use ($connection) {
            // 返回空的chunk代表响应结束
            $connection->send(new Chunk($buffer));
        },
        'success' => function ($response) use ($connection) {
            // 返回空的chunk代表响应结束
            $connection->send(new Chunk(''));
        },
    ]);
    $response = new Response(200, [
        'Transfer-Encoding' => 'chunked',
    ], '');
    // 设置跨域问题
    $response->header('Access-Control-Allow-Origin', '*');
    $connection->send($response);
};
Worker::runAll();
运行服务

启动workerman提供服务

代码语言:javascript
复制
# php chunk.php start
Workerman[chunk.php] start in DEBUG mode
------------------------------------------- WORKERMAN --------------------------------------------
Workerman version:4.1.15          PHP version:7.4.27           Event-Loop:\Workerman\Events\Event
-------------------------------------------- WORKERS ---------------------------------------------
proto   user            worker          listen                  processes    status
tcp     root            none            http://0.0.0.0:8782/    1             [OK] 
--------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.

服务地址:http://127.0.0.1:8782

客户端

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>开源技术小栈 Stream Chunked HTTP Data</title>
</head>
<body>
<h1>HTTP分块Chunk传输响应给客户端</h1>
<div id="data-container"></div>

<script>
    // 定义接收数据的URL
    const url = 'http://127.0.0.1:8782/';

    // 发起请求并处理分块数据
    function fetchChunkedData() {
        fetch(url, {
            method: 'GET',
            headers: {
                    'Accept': 'text/plain; chunks=true' // 某些服务器可能需要这个头来明确请求分块数据
                }
            })
            .then(response => {
                if (!response.ok) {
                    throw new Error('Network response was not ok ' + response.statusText);
                }
                return response.body;
            })
            .then(body => {
                // 将ReadableStream转换为阅读器
                const reader = body.getReader();
                processChunks(reader);
            })
            .catch(error => {
                console.error('There has been a problem with your fetch operation:', error);
            });
    }

    // 处理分块数据
    function processChunks(reader) {
        // 读取数据块
        reader.read().then(({done, value}) => {
            if (done) {
                console.log('Stream complete');
                return;
            }

            // 将Uint8Array转换为字符串
            const chunk = new TextDecoder('utf-8').decode(value);
            // 将数据块显示在页面上
            const dataContainer = document.getElementById('data-container');
            dataContainer.insertAdjacentHTML('beforeend', chunk);

            // 递归读取下一个数据块
            processChunks(reader);
        });
    }

    // 调用函数开始获取数据
    fetchChunkedData();
</script>
</body>
</html>

效果

服务端输出

客户端输出

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 前言
  • 0x02 传输格式
  • 0x03 应用案例
    • 服务端
      • 安装
      • 发送HTTP分块Chunk数据
      • 运行服务
    • 客户端
      • 效果
      相关产品与服务
      API 网关
      腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档