我想通过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检查,将其作为流的最后四个字节,紧跟在最后一个块之后。

08-07 16:02
查看更多