在远程协作、在线教学、监控等场景中(当然啦!还有其他的用途,这里就不说了,懂得都懂),屏幕共享是一项非常重要的技术。本文将介绍如何使用Golang开发一个基于WebSocket的屏幕共享系统,实现远程查看电脑屏幕的功能。

技术原理

整个屏幕共享的流程如下:

  1. 定时截图:使用kbinani/screenshot 进行屏幕捕获,每隔100ms采集一次屏幕。
  2. 图片压缩:为了减少数据传输量,将截图压缩为 JPEG 格式。
  3. WebSocket传输:通过gorilla/websocket实现WebSocket服务器,将截图数据推送给浏览器客户端。
  4. 前端展示:浏览器通过WebSocket接收图像数据,并动态更新img标签以实时显示共享的屏幕内容。

后端实现

我们首先创建一个go项目并使用go编写一个WebSocket 服务器,用于捕获屏幕并实时发送给客户端,具体操作如下所示:


mkdir websocket-demo
cd websocket-demo/ && go mod init websocket-demo
# 安装 WebSocket 库
go get -u github.com/gorilla/websocket

# 安装屏幕截图库
go get -u github.com/kbinani/screenshot

然后键入如下代码,具体如下所示:

package main

import (
    "bytes"
    "fmt"
    "image/jpeg"
    "net/http"
    "time"

    "github.com/gorilla/websocket"
    "github.com/kbinani/screenshot"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true }, // 允许跨域
}

// 捕获屏幕并转换为JPEG
func captureScreen() ([]byte, error) {
    img, err := screenshot.CaptureDisplay(0) // 仅截取主屏幕
    if err != nil {
        return nil, err
    }

    buf := new(bytes.Buffer)
    jpeg.Encode(buf, img, &jpeg.Options{Quality: 50}) // 50% 质量压缩
    return buf.Bytes(), nil
}

// 处理WebSocket连接
func handleWS(conn *websocket.Conn) {
    defer conn.Close()

    for {
        time.Sleep(100 * time.Millisecond) // 每100ms发送一次(10FPS)
        imgData, err := captureScreen()
        if err != nil {
            fmt.Println("截图失败:", err)
            continue
        }

        // 发送图片数据
        if err = conn.WriteMessage(websocket.BinaryMessage, imgData); err != nil {
            fmt.Println("发送失败:", err)
            break
        }
    }
}

func main() {
    http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
        conn, err := upgrader.Upgrade(w, r, nil)
        if err != nil {
            fmt.Println("WebSocket连接失败:", err)
            return
        }
        go handleWS(conn) // 处理WebSocket连接
    })

    fmt.Println("WebSocket 服务器启动,访问 ws://localhost:9527/ws")
    http.ListenAndServe(":9527", nil)
}

代码解析

  • screenshot.CaptureDisplay(0):获取主屏幕的截图。
  • jpeg.Encode(buf, img, &jpeg.Options{Quality: 50}):将图片转换为JPEG格式并压缩,减少数据传输量。
  • websocket.Upgrader:用于升级HTTP连接为WebSocket连接。
  • handleWS(conn):不断循环截图并发送给WebSocket客户端。

⚠️注意: 需要提前安装好opencv相关依赖

前端实现

前端需要使用WebSocket接收屏幕图像数据,并动态更新img标签,具体事项如下所示:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>屏幕共享</title>
</head>
<body>
    <h2>实时屏幕共享</h2>
    <img id="screen" style="border: 1px solid black; width: 80%; max-width: 1024px;">

    <script>
        let ws = new WebSocket("ws://localhost:9527/ws");
        let img = document.getElementById("screen");

        ws.binaryType = "blob";
        ws.onmessage = function (event) {
            let url = URL.createObjectURL(event.data);
            img.src = url;
        };

        ws.onclose = function () {
            alert("连接已断开");
        };
    </script>
</body>
</html>

代码解析

  • let ws = new WebSocket(“ws://localhost:9527/ws”);:连接 WebSocket 服务器。

  • ws.onmessage = function (event) {…}:当收到屏幕图像数据时,创建 blob 对象,并更新 img 标签。

  • img.src = URL.createObjectURL(event.data);:动态显示截图。