如何通过 response.write 在 node.js 中正确发送缓冲区?
How to send the buffer correctly in node.js via response.write?
我正在尝试从 node.js 服务器发送二进制内容。为此,我分配了一个缓冲区并用我的内容填充缓冲区,然后在其上调用 response.write()
。一旦它 returns,我就用新内容重用缓冲区。但是,由于某些原因,它似乎无法正常工作。
这是服务器代码:
const http = require('http');
async function sendChunk( response, outbuf )
{
console.log( "Sending buffer: %s", outbuf );
// Send the buffer out. If it returns false,
// this means the kernel buffers are full,
// and we have to wait until they are available.
if ( await response.write( outbuf ) === false )
{
await new Promise(resolve => response.once('drain', ()=>{
resolve();
}));
}
}
async function sendData( response )
{
let outbuf = Buffer.alloc( 20 );
for ( let count = 0x45; count < 0x50; count++ )
{
for ( let i = 0; i < outbuf.length; i++ )
{
outbuf[i] = count;
}
await sendChunk( response, outbuf );
}
}
function webRequestHandler( request, response )
{
let body = [];
request.on('error', (err) => {
console.error(err);
return;
});
request.on('data', (chunk) => {
body.push(chunk);
});
response.on('error', (err) => {
console.error( "Error sending response: %s", err);
return;
});
// A whole body collected - process it
request.on('end', async () => {
// Handle the update; can return an error message
response.setHeader('Content-Type', 'text/plain');
await sendData( response );
response.end();
});
}
const webserver = http.createServer( webRequestHandler );
// Create the web service
webserver.on('error', function (err) {
console.log("[" + process.pid + "] " + JSON.stringify(err));
process.exit();
});
webserver.listen( { "host" : "127.0.0.1", "port" : 5252 }, () => {
console.log( "Server running" );
});
当通过 curl http://localhost:5252/
测试时,服务器打印以下内容:
Sending buffer: EEEEEEEEEEEEEEEEEEEE
Sending buffer: FFFFFFFFFFFFFFFFFFFF
Sending buffer: GGGGGGGGGGGGGGGGGGGG
Sending buffer: HHHHHHHHHHHHHHHHHHHH
Sending buffer: IIIIIIIIIIIIIIIIIIII
Sending buffer: JJJJJJJJJJJJJJJJJJJJ
Sending buffer: KKKKKKKKKKKKKKKKKKKK
Sending buffer: LLLLLLLLLLLLLLLLLLLL
Sending buffer: MMMMMMMMMMMMMMMMMMMM
Sending buffer: NNNNNNNNNNNNNNNNNNNN
Sending buffer: OOOOOOOOOOOOOOOOOOOO
然而客户收到的东西完全不同:
> curl http://localhost:5252/
EEEEEEEEEEEEEEEEEEEEOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
这是怎么回事?如果我在 sendChunk 中创建一个新缓冲区(outbuf 的副本),它确实有效。然而,这似乎是对 RAM 的浪费,对于来自 C 背景的人来说并没有真正意义,一旦你在套接字上调用 send()
,数据就会被复制,你可以像你一样重用源缓冲区希望。
node.js 有什么不同吗?我是否需要为 response.write() 创建一个专用缓冲区,一旦调用 write 就不能再触及它,即使 write 已经返回并且我等待 drain
事件?
我发布了 the bug report,它因一条重要评论而关闭:
You should be passing a callback to .write() to know when node is
finished with that chunk of memory instead of relying on the 'drain'
event.
Once you make that change, you will see the output on the client that
you're expecting.
确实,一旦 sendChunk
函数更改如下:
async function sendChunk( response, outbuf )
{
return new Promise( function( resolve, reject) {
if ( response.write( outbuf, ()=>{ resolve(); } ) === false )
{
console.log( "Draining buffer" );
response.once('drain', ()=>{
resolve();
});
}
});
}
所以我们只在函数回调中解决它,问题就消失了。这里的核心问题是response.write
是不可等待的,在调用回调之前returns。
应该更仔细地阅读文档。
我正在尝试从 node.js 服务器发送二进制内容。为此,我分配了一个缓冲区并用我的内容填充缓冲区,然后在其上调用 response.write()
。一旦它 returns,我就用新内容重用缓冲区。但是,由于某些原因,它似乎无法正常工作。
这是服务器代码:
const http = require('http');
async function sendChunk( response, outbuf )
{
console.log( "Sending buffer: %s", outbuf );
// Send the buffer out. If it returns false,
// this means the kernel buffers are full,
// and we have to wait until they are available.
if ( await response.write( outbuf ) === false )
{
await new Promise(resolve => response.once('drain', ()=>{
resolve();
}));
}
}
async function sendData( response )
{
let outbuf = Buffer.alloc( 20 );
for ( let count = 0x45; count < 0x50; count++ )
{
for ( let i = 0; i < outbuf.length; i++ )
{
outbuf[i] = count;
}
await sendChunk( response, outbuf );
}
}
function webRequestHandler( request, response )
{
let body = [];
request.on('error', (err) => {
console.error(err);
return;
});
request.on('data', (chunk) => {
body.push(chunk);
});
response.on('error', (err) => {
console.error( "Error sending response: %s", err);
return;
});
// A whole body collected - process it
request.on('end', async () => {
// Handle the update; can return an error message
response.setHeader('Content-Type', 'text/plain');
await sendData( response );
response.end();
});
}
const webserver = http.createServer( webRequestHandler );
// Create the web service
webserver.on('error', function (err) {
console.log("[" + process.pid + "] " + JSON.stringify(err));
process.exit();
});
webserver.listen( { "host" : "127.0.0.1", "port" : 5252 }, () => {
console.log( "Server running" );
});
当通过 curl http://localhost:5252/
测试时,服务器打印以下内容:
Sending buffer: EEEEEEEEEEEEEEEEEEEE
Sending buffer: FFFFFFFFFFFFFFFFFFFF
Sending buffer: GGGGGGGGGGGGGGGGGGGG
Sending buffer: HHHHHHHHHHHHHHHHHHHH
Sending buffer: IIIIIIIIIIIIIIIIIIII
Sending buffer: JJJJJJJJJJJJJJJJJJJJ
Sending buffer: KKKKKKKKKKKKKKKKKKKK
Sending buffer: LLLLLLLLLLLLLLLLLLLL
Sending buffer: MMMMMMMMMMMMMMMMMMMM
Sending buffer: NNNNNNNNNNNNNNNNNNNN
Sending buffer: OOOOOOOOOOOOOOOOOOOO
然而客户收到的东西完全不同:
> curl http://localhost:5252/
EEEEEEEEEEEEEEEEEEEEOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
这是怎么回事?如果我在 sendChunk 中创建一个新缓冲区(outbuf 的副本),它确实有效。然而,这似乎是对 RAM 的浪费,对于来自 C 背景的人来说并没有真正意义,一旦你在套接字上调用 send()
,数据就会被复制,你可以像你一样重用源缓冲区希望。
node.js 有什么不同吗?我是否需要为 response.write() 创建一个专用缓冲区,一旦调用 write 就不能再触及它,即使 write 已经返回并且我等待 drain
事件?
我发布了 the bug report,它因一条重要评论而关闭:
You should be passing a callback to .write() to know when node is finished with that chunk of memory instead of relying on the 'drain' event.
Once you make that change, you will see the output on the client that you're expecting.
确实,一旦 sendChunk
函数更改如下:
async function sendChunk( response, outbuf )
{
return new Promise( function( resolve, reject) {
if ( response.write( outbuf, ()=>{ resolve(); } ) === false )
{
console.log( "Draining buffer" );
response.once('drain', ()=>{
resolve();
});
}
});
}
所以我们只在函数回调中解决它,问题就消失了。这里的核心问题是response.write
是不可等待的,在调用回调之前returns。
应该更仔细地阅读文档。