在goFiber中使用WebSocket遇问题:无法连接及消息发送困惑
问题核心分析
浏览器地址栏无法直接访问ws://协议地址,必须通过前端页面的JavaScript发起WebSocket连接,这是你看到“无法访问此网站”的直接原因。另外当前代码仅实现了echo模式(收消息再发回),无法主动向客户端推送命令执行输出,需要针对性改造。
一、后端代码改造
1. 完善ws包,添加连接管理与主动推送能力
package ws import ( "bufio" "log" "os/exec" "sync" "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2" ) // 连接管理器:存储不同ID对应的WebSocket连接 var ( connections = make(map[string]*websocket.Conn) mu sync.Mutex ) var WsUpgrade = func(c *fiber.Ctx) error { if websocket.IsWebSocketUpgrade(c) { c.Locals("allowed", true) return c.Next() } return fiber.ErrUpgradeRequired } // 处理WebSocket连接:注册连接、监听断开 var WsHandleConnection = func(c *websocket.Conn) { id := c.Params("id") log.Printf("客户端 %s 已连接", id) // 注册连接到管理器 mu.Lock() connections[id] = c mu.Unlock() // 断开连接时清理资源 defer func() { mu.Lock() delete(connections, id) mu.Unlock() c.Close() log.Printf("客户端 %s 已断开", id) }() // 可选:监听客户端消息(比如接收控制指令) var ( mt int msg []byte err error ) for { if mt, msg, err = c.ReadMessage(); err != nil { log.Printf("读取客户端 %s 消息失败: %v", id, err) break } log.Printf("收到客户端 %s 消息: %s", id, msg) } } // 向指定ID的客户端发送消息 func SendMessageToClient(id string, message []byte) error { mu.Lock() conn, exists := connections[id] mu.Unlock() if !exists { return fiber.ErrNotFound } return conn.WriteMessage(websocket.TextMessage, message) } // 执行命令并实时推送输出到指定客户端 func ExecuteCommandAndSendOutput(id string, cmdStr string) error { cmd := exec.Command("bash", "-c", cmdStr) stdout, err := cmd.StdoutPipe() if err != nil { return err } stderr, err := cmd.StderrPipe() if err != nil { return err } if err := cmd.Start(); err != nil { return err } // 实时推送标准输出 go func() { scanner := bufio.NewScanner(stdout) for scanner.Scan() { line := scanner.Bytes() if err := SendMessageToClient(id, append([]byte("[stdout] "), line...)); err != nil { log.Printf("推送输出到客户端 %s 失败: %v", id, err) break } } }() // 实时推送标准错误 go func() { scanner := bufio.NewScanner(stderr) for scanner.Scan() { line := scanner.Bytes() if err := SendMessageToClient(id, append([]byte("[stderr] "), line...)); err != nil { log.Printf("推送错误到客户端 %s 失败: %v", id, err) break } } }() return cmd.Wait() }
2. 修改main.go,添加命令触发接口
package main import ( "fiber-app/pkg/ws" "log" "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2" "github.com/gofiber/template/html/v2" ) func main() { engine := html.New("./views", ".html") appconf := fiber.Config{Views: engine} app := fiber.New(appconf) app.Static("/", "./public") app.Use("/ws", ws.WsUpgrade) // WebSocket连接路由 app.Get("/ws/progress/:id", websocket.New(ws.WsHandleConnection)) // 触发命令执行的HTTP接口:访问/start/123即可向ID为123的客户端推送输出 app.Get("/start/:id", func(c *fiber.Ctx) error { id := c.Params("id") // 替换为你需要执行的命令 go func() { if err := ws.ExecuteCommandAndSendOutput(id, "ls -l && sleep 2 && echo '命令执行完成'"); err != nil { log.Printf("执行命令失败: %v", err) } }() return c.SendString("已开始执行命令,输出将推送到WebSocket客户端") }) log.Fatal(app.Listen(":5000")) }
二、前端HTML页面实现(创建./public/index.html)
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>命令执行实时输出</title> </head> <body> <h1>命令执行实时输出</h1> <div id="output"></div> <button onclick="startCommand()">开始执行命令</button> <script> // 替换为你的服务器IP const wsUrl = 'ws://你的机器IP:5000/ws/progress/123'; let ws; // 初始化WebSocket连接 function initWebSocket() { ws = new WebSocket(wsUrl); ws.onopen = () => { addOutput('WebSocket连接已建立'); }; // 接收后端推送的消息并显示 ws.onmessage = (event) => { addOutput(event.data); }; ws.onclose = () => { addOutput('WebSocket连接已断开'); // 可选:自动重连 setTimeout(initWebSocket, 3000); }; ws.onerror = (error) => { addOutput('WebSocket错误: ' + error); }; } // 向页面追加输出内容 function addOutput(text) { const outputDiv = document.getElementById('output'); const p = document.createElement('p'); p.textContent = text; outputDiv.appendChild(p); } // 触发命令执行 function startCommand() { fetch('http://你的机器IP:5000/start/123') .then(res => res.text()) .then(data => addOutput(data)) .catch(err => addOutput('请求失败: ' + err)); } // 页面加载时自动初始化连接 window.onload = initWebSocket; </script> </body> </html>
三、关键说明
- 连接管理:用
connections映射存储不同ID的WebSocket连接,确保可以精准向指定客户端推送消息。 - 实时推送逻辑:通过
StdoutPipe和StderrPipe实时读取命令输出,再调用SendMessageToClient推送给客户端。 - 客户端接收:前端通过WebSocket的
onmessage事件监听后端推送的消息,实时渲染到页面。 - 测试步骤:
- 启动后端服务
- 浏览器访问
http://你的机器IP:5000/index.html - 页面加载后自动建立WebSocket连接,点击“开始执行命令”即可看到实时输出
内容的提问来源于stack exchange,提问作者siwm
相关产品推荐
相关产品推荐

