使用Socket.io实现多客户端文件共享时陷入死循环问题求助
Socket.io多客户端文件共享死循环问题修复
问题根源
- 重复绑定事件监听:每次调用
shareFile都会给socket新增fs-share事件监听,导致同一事件触发多次回调,引发循环。 - Server端事件逻辑错误:原代码中
fs-start事件触发后,Server广播fs-meta事件,导致客户端陷入fs-meta→emit fs-start→Server广播fs-meta的无限循环。 - 无传输终止逻辑:当文件块传输完毕(
chunk.length === 0)时,未移除事件监听,也未停止后续触发逻辑,死循环持续。 - 语法与资源错误:存在选择器引号缺失、CDN地址拼写错误(
cloudfare→cloudflare)的问题。
修复后的代码
1. server.js
const express = require("express"); const app = express(); const socketio = require("socket.io"); app.use(express.static(__dirname + '/public')); const expressServer = app.listen(3000); const io = socketio(expressServer) io.on('connection',(socket)=>{ // 广播文件元数据给所有客户端 socket.on('file-meta',function(data){ io.emit('fs-meta',data.metadata); }) // 转发接收端的下块请求给发送端 socket.on('request-next-chunk',function(){ io.emit('send-next-chunk'); }) // 广播文件块给所有客户端 socket.on('file-raw',function(data){ io.emit('fs-share',data.buffer); }) })
2. sender.html
<div class="file-input"> <label for="file-input">Select files for sharing</label> <input type="file" id="file-input"> </div> <script src="/socket.io/socket.io.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/downloadjs/1.4.1/download.min.js"></script> <script> const socket = io('http://localhost:3000'); let fileShare = {}; // 修复选择器引号缺失问题 document.querySelector('#file-input').addEventListener("change",function(e){ let file = e.target.files[0]; if(!file){ return; } let reader = new FileReader(); reader.onload = function(e){ let buffer = new Uint8Array(reader.result); shareFile({ filename:file.name, total_buffer_size:buffer.length, buffer_size:524288 },buffer); } reader.readAsArrayBuffer(file); }) // 存储当前传输的状态,避免函数参数作用域问题 let currentTransfer = {}; function shareFile(metadata,buffer){ currentTransfer.metadata = metadata; currentTransfer.buffer = buffer; // 移除旧监听,避免重复绑定 socket.off('send-next-chunk'); // 绑定传输下块的监听 socket.on('send-next-chunk', sendNextChunk); // 发送文件元数据 socket.emit("file-meta",{metadata:metadata}); } function sendNextChunk(){ const metadata = currentTransfer.metadata; let buffer = currentTransfer.buffer; let chunk = buffer.slice(0, metadata.buffer_size); currentTransfer.buffer = buffer.slice(metadata.buffer_size, buffer.length); if(chunk.length !== 0){ socket.emit("file-raw",{buffer:chunk}); } else { // 传输完毕,移除监听并清空状态 socket.off('send-next-chunk'); currentTransfer = {}; } } socket.on("fs-meta",function(metadata){ if(Object.keys(metadata).length === 0) return; // 忽略空的元数据信号 fileShare.metadata = metadata; fileShare.transmitted = 0; fileShare.buffer = []; // 请求发送第一块数据 socket.emit("request-next-chunk"); }); socket.on("fs-share",function(buffer){ fileShare.buffer.push(buffer); fileShare.transmitted += buffer.byteLength; if(fileShare.transmitted === fileShare.metadata.total_buffer_size){ download(new Blob(fileShare.buffer), fileShare.metadata.filename); fileShare={}; }else { // 请求下一块数据 socket.emit("request-next-chunk"); } }); </script>
关键修复说明
- 避免重复绑定:每次开始传输前用
socket.off()移除旧的事件监听,确保同一时间只有一个回调生效。 - 修正事件流程:将原有的
fs-start拆分为request-next-chunk(接收端请求下块)和send-next-chunk(发送端响应传输),解决Server错误广播导致的循环。 - 添加终止逻辑:当文件块传输完毕时,移除事件监听并清空传输状态,彻底终止循环。
- 修复语法错误:修正了选择器引号缺失和CDN地址拼写问题。
内容的提问来源于stack exchange,提问作者Deepak Kumar
相关产品推荐
相关产品推荐

