大概目前是这个情况,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
之后,读到的块就都是对的了(当然我也知道用同步肯定是不合适的)。
我的问题有两个
- 为什么会有这种情况,写 buffer 并不是原子操作?
- 如果我不想每次都开一个新的 buffer,而是想尽可能重复利用一个 Buffer,又要异步读,有什么好办法吗?(之前试过random-access-file,也遇到一样的问题)
谢谢
汗一个~~ 你的 err 判断了么? bytesRead 用了么?
@myy 判断了,没有错。。。
。。。理论上,整个代码都是单线程的,是否“原子操作”其实不重要。。。
我觉得你可能出错的地方: 1、认为每次读肯定会把Buffer读满,于是没有用上 bytesRead 2、认为socket发送buffer必定会一次性发送掉全部指定的长度,其实不一定…
这不是找虐的节奏嘛。 写的速度赶不上读的速度,缓冲区已经溢出了。 要是来个上千上万个并发,主机立马宕掉。读写服务要使用缓存。
- 你的意思是bytesRead并没有读1024个字节吗?
- 和 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