You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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>

三、关键说明

  1. 连接管理:用connections映射存储不同ID的WebSocket连接,确保可以精准向指定客户端推送消息。
  2. 实时推送逻辑:通过StdoutPipe和StderrPipe实时读取命令输出,再调用SendMessageToClient推送给客户端。
  3. 客户端接收:前端通过WebSocket的onmessage事件监听后端推送的消息,实时渲染到页面。
  4. 测试步骤:
    • 启动后端服务
    • 浏览器访问http://你的机器IP:5000/index.html
    • 页面加载后自动建立WebSocket连接,点击“开始执行命令”即可看到实时输出

内容的提问来源于stack exchange,提问作者siwm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 16:33:14