异步读文件时读到的 buffer 不真实,不知有没有人碰到过
发布于 2个月前 作者 laike9m 412 次浏览 来自 问答

大概目前是这个情况,socket.io监听某个事件,回调函数是读取一个文件中的1024B,全部都用的是同一个 Buffer

var f1 = fs.openSync(path.join(path.dirname(__dirname), 'Advice.mp3'), 'r');
var bf1 = Buffer(1024);

socket.on('receive', function(msg){
    fs.read(f1, bf1, 0, 1024, msg.index * 1024, function(err, bytesRead, data){
        socket.emit('send_block', data);
    }); 
});

这个receive事件会被频繁调用,可能20ms一次。之后我发现读几万个块,其中总有几个块的内容不对,就是说和原始文件中的不同,而其它的块则没有问题。我排查了很久才发现是这里出了问题,改成readSync之后,读到的块就都是对的了(当然我也知道用同步肯定是不合适的)。

我的问题有两个

  1. 为什么会有这种情况,写 buffer 并不是原子操作?
  2. 如果我不想每次都开一个新的 buffer,而是想尽可能重复利用一个 Buffer,又要异步读,有什么好办法吗?(之前试过random-access-file,也遇到一样的问题)

谢谢

13 回复

汗一个~~ 你的 err 判断了么? bytesRead 用了么?

@myy 判断了,没有错。。。

。。。理论上,整个代码都是单线程的,是否“原子操作”其实不重要。。。

我觉得你可能出错的地方: 1、认为每次读肯定会把Buffer读满,于是没有用上 bytesRead 2、认为socket发送buffer必定会一次性发送掉全部指定的长度,其实不一定…

这不是找虐的节奏嘛。 写的速度赶不上读的速度,缓冲区已经溢出了。 要是来个上千上万个并发,主机立马宕掉。读写服务要使用缓存。

@myy

  1. 你的意思是bytesRead并没有读1024个字节吗?
  2. 和 socket 真的没关系,因为我判断的时候是直接用 bf1 和 fs 读的文件对应块来比较,然后发现他们不一样。。。

@tulayang 恩,一般这种要怎么做呢,我对 Node 没什么经验。。。

试试这个,自己写的话需要控制drain事件 还有pause resume等

socket.pipe(fs.createWriteStream(path.join(path.dirname(__dirname), 'Advice.mp3')));

btw 好像__dirname自动触发编辑器的"B"

可以先把mp3数据读入到buffer, recieve事件来时就发送这个buffer相应的块。这样速度还快一点。

@eeandrew 恩,这个思路我觉得挺好的,应该可行

myy说的是对的。 你new Buffer(1024)时,这1024个字节中是有随机数值的。 当一个mp3是1025个字节时,也许第一次正好读取1024个字节,正好覆盖掉你的bf1,当第二次读取时,实际只有第一个字节是mp3的第1025个字节, 其余的字节数是上一次读取时的数据,所以不正确。

@laike9m 建议用pipe,传说中的“排山倒海”大招~

@nodejser 对,我了解最后一个块的情况,实际代码当然考虑了这一点贴上来的只是demo。我感觉奇怪是因为每次读文件都有几个块不一样,不是确定的几个而是随机的,而且也都不是最后一块。

@myy 我不太确定这里是不是好用pipe,实际中的代码这一句socket.emit('send_block', data);中的data是一个包含了data的Object,例如{content: data, a: xxx, b: xxx},如果直接只是把data发出去大概可以pipe

回到顶部