Golang WebSocket保存多文件时连接异常断开问题排查
WebSocket连接在Golang服务器保存文件后断开(错误1001: going away)
问题描述
尝试通过WebSocket在Golang服务器上保存多个音频文件,但每次创建单个文件后,WebSocket连接就会断开,报错websocket: close 1001 (going away)。移除文件保存的代码块后,循环可以正常运行。请排查客户端导致连接断开的原因。
服务端代码
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { fmt.Println("Error upgrading to WebSocket:", err) return } defer conn.Close() for i := 0; ; i++ { questionaire:= questionaires[i].msg //a couple of questions to be sent to client if i == 0 { // send initial message to frontend sendServerMessageToClient(conn, questionaire) } else { messageType, msg, err := conn.ReadMessage() if err != nil || err == io.EOF { fmt.Println(err) // this line here logs the websocket error break } response = "" if messageType == websocket.BinaryMessage { // if this blocks of code here is removed, it works, otherwise restarts the websocket connection fileName := fmt.Sprintf("answer%d.wav", i) file, err := os.Create(fileName) if err != nil { fmt.Println(err) } _, err = file.Write(msg) if err != nil { fmt.Println(err) } // til here // handle the saved file and call to speech to text api response = "yes" //assumed the response is "yes" } switch response { case "yes": if i < len(questionaires) { sendServerMessageToClient(conn, questionaire) } else { sendServerMessageToClient(conn, "end") return } case "no": sendServerMessage(conn, "Exiting") return default: i-- } } }
客户端代码
.... <div id="statusLabel">Status: Waiting for server...</div><br><br> <audio controls id="audioPlayer"></audio><br><br> <button onclick="startRecording()">Start Recording</button> <button onclick="stopRecording()">Stop Recording</button> <script> const statusLabel = document.getElementById('statusLabel'); const audioPlayer = document.getElementById('audioPlayer'); let mediaRecorder; let audioChunks = []; let ws; function startRecording() { navigator.mediaDevices.getUserMedia({ audio: true }) .then((stream) => { audioChunks = []; statusLabel.textContent = "Status: Recording..."; mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { audioChunks.push(event.data); ws.send(event.data); } }; mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); const audioUrl = URL.createObjectURL(audioBlob); audioPlayer.src = audioUrl; }; mediaRecorder.start(); }) .catch((error) => console.error('Error accessing microphone:', error)); } function stopRecording() { statusLabel.textContent = "Stopped recording"; ws.send("stop"); // Notify the server to stop recording mediaRecorder.stop(); } // Establish WebSocket connection ws = new WebSocket("ws://golang-server.com/ws"); ws.onmessage = (event) => { statusLabel.textContent = `${event.data}`; }; ws.onclose = (event) => { statusLabel.textContent = "Connection closed"; }; </script> ...
问题排查与修复方案
客户端侧核心问题
分片发送音频数据导致服务器阻塞超时
MediaRecorder.start()默认会每隔几百毫秒触发ondataavailable事件,客户端每次收到数据就调用ws.send(event.data)发送小二进制块。服务器在处理文件写入时(磁盘IO操作)会阻塞ReadMessage()循环,无法及时响应客户端的后续消息,客户端因长时间未收到服务器反馈,主动触发连接断开(1001错误)。发送的"stop"文本消息未被服务器处理
点击停止时客户端发送字符串"stop"(文本类型),但服务器仅处理BinaryMessage类型,此时response为空进入default分支,i--导致循环卡住,服务器无法继续发送消息,客户端长期无响应后主动断开。
修复建议
客户端修改
- 合并音频后一次性发送:移除
ondataavailable中的ws.send(event.data),仅在onstop时发送完整的音频Blob,减少频繁通信对服务器的压力:
mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { audioChunks.push(event.data); // 移除此处的ws.send(event.data) } }; mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); const audioUrl = URL.createObjectURL(audioBlob); audioPlayer.src = audioUrl; // 一次性发送完整音频 ws.send(audioBlob); };
- 调整停止逻辑:如果需要保留"stop"通知,需确保服务器能处理文本消息,或者改为在发送完完整音频后由服务器判断流程结束。
服务端配套修改
- 关闭文件句柄:文件写入后必须关闭,避免句柄泄漏和磁盘缓存阻塞:
fileName := fmt.Sprintf("answer%d.wav", i) file, err := os.Create(fileName) if err != nil { fmt.Println(err) continue } defer file.Close() // 新增:确保文件关闭 _, err = file.Write(msg) if err != nil { fmt.Println(err) }
- 处理文本消息:添加文本消息分支,处理客户端的"stop"指令:
if messageType == websocket.BinaryMessage { // 原文件保存逻辑 } else if messageType == websocket.TextMessage { msgStr := string(msg) if msgStr == "stop" { sendServerMessage(conn, "Exiting") return } }
- 添加心跳机制:服务器定期发送Ping帧,客户端回复Pong,避免因长时间无通信被客户端判定为连接失效。
内容的提问来源于stack exchange,提问作者San
相关产品推荐
相关产品推荐

