我想通过HTTP提供一些流数据。另外,我想压缩数据以节省带宽。我可以分别简单地压缩每个流响应,但是就我而言,每个客户端(扇出)的数据流基本相同,因此为每个连接压缩相同的数据似乎浪费了CPU时间。我的计划是抢先压缩流数据的每个块,以便随时连接的客户端可以开始读取下一个块(这是以降低压缩效率为代价的,但是只要单个数据块足够大,这应该没事)。
兼容的HTTP客户端显然应该能够在Content-Encoding: gzip
响应but the answers to this question indicate web browsers do not中接受多个压缩文件。但是,根据我对DEFLATE / zlib的了解,您可以发送Z_FULL_FLUSH
0x0000FFFF
字节来重置流,该流应具有可单独解压缩的块的相同效果。
我已经在node.js中设置了一个简单的POC,它将消息作为服务器发送事件流进行流传输,但是我无法通过网络浏览器来读取数据。它会打开连接,但永远不会刷新数据。为了简单起见,我使用Z_NO_COMPRESSION
。
var http, zlib, gzip, numClients;
http = require('http');
zlib = require('zlib');
gzip = zlib.createDeflateRaw({
flush: zlib.Z_SYNC_FLUSH,
level: zlib.Z_NO_COMPRESSION
});
numClients = 0;
setInterval(function(){
if (numClients > 0) {
gzip.write("data: hi\n\n");
}
}, 1000);
http.createServer(function(req, res){
res.socket.setTimeout(Infinity);
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Content-Encoding': 'deflate',
'Transfer-Encoding': 'identity',
'Access-Control-Allow-Origin': '*'
});
res.write('\x78\x01'); // write zlib magic number
gzip.pipe(res);
numClients++;
res.on('close', function(){
numClients--;
gzip.unpipe(res);
});
res.on('error', function(){
numClients--;
gzip.unpipe(res);
});
}).listen(8080);
numClients++;
gzip.pipe(process.stdout);
和一个简单的客户:
<!DOCTYPE html5>
<html lang=en>
<meta charset=utf-8>
<title>hi</title>
<script>
var es = new EventSource("http://localhost:8080/");
es.addEventListener('data', console.log);
es.addEventListener('open', console.log);
es.addEventListener('error', console.log);
</script>
字节看起来像这样,(通过
curl -N localhost:8080
传递的xxd
):0000000: 7801 000a 00f5 ff64 6174 613a 2068 690a x......data: hi.
0000010: 0a00 0000 ffff 000a 00f5 ff64 6174 613a ...........data:
0000020: 2068 690a 0a00 0000 ffff 000a 00f5 ff64 hi............d
0000030: 6174 613a 2068 690a 0a00 0000 ffff 000a ata: hi.........
0000040: 00f5 ff64 6174 613a 2068 690a 0a00 0000 ...data: hi.....
0000050: ffff 000a 00f5 ff64 6174 613a 2068 690a .......data: hi.
0000060: 0a00 0000 ffff 000a 00f5 ff64 6174 613a ...........data:
我是否需要为DEFLATE压缩器添加额外的帧以检测刷新点?
编辑:我添加了zlib magic number使http流成为有效的DEFLATE流,但是
网络浏览器仍然无法清除障碍。但是,如果添加了幻数,则将
gzip
流通过zlib.createInflate()
流回管道的效果很好。我也知道http流没有被缓冲,因为curl -N localhost:8080
将显示原始字节。 最佳答案
zlib流需要通过最后一个块和完整性检查来终止。最后一个块由在块开始处设置的“最后一位”表示。在这种情况下,由于倒数第二个块是存储的块,因此将您置于字节边界,因此最后一个块可以是01 00 00 ff ff。
然后,您需要对未压缩的数据进行Adler-32检查,将其作为流的最后四个字节,紧跟在最后一个块之后。