quic-go实现屏幕广播程序
最近在折腾quic-go, 突然想起屏广适合用udp实现,而http3基于quic-go,后者又基于udp, 所以玩一下。
先贴出本机运行效果图:

功能(实现)说明:
1.服务器先启动作为共享屏幕方,等待客户端连接上来
2.客户端连接
3.客户端和服务器建立连接后,服务器主动打开stream
在一个for 循环中:每秒操作30次下面操作:
4.服务器开始抓取本机屏幕内容,转换成Image5.数据传输协议:Image字节长度 + Image内容
6.客户端按上述协议接收数据,解析成Image对象,放界面上展示
服务端代码:
package mainimport ("bytes""context""crypto/rand""crypto/rsa""crypto/x509""encoding/binary""encoding/pem""fmt""github.com/quic-go/quic-go""image""image/png""log""math/big""os""time""crypto/tls""github.com/kbinani/screenshot"
)const addr = "localhost:4000"var currentDir, _ = os.Getwd()var quicConf = &quic.Config{Allow0RTT: true,MaxIdleTimeout: 40 * time.Second,InitialStreamReceiveWindow: 1 << 20, // 1 MBMaxStreamReceiveWindow: 6 << 20, // 6 MBInitialConnectionReceiveWindow: 2 << 20, // 2 MBMaxConnectionReceiveWindow: 12 << 20, // 12 MB
}func main() {//listener, err := quic.ListenAddr(addr, generateTLSConfig(), quicConf)listener, err := quic.ListenAddr(addr, generateTLSConfig2(), quicConf)if err != nil {log.Fatal(err)}fmt.Println("Server listening on", addr)for {// 接受客户端连接sess, err := listener.Accept(context.Background())if err != nil {log.Fatal(err)}fmt.Println("New client connected")go handleConnection(sess)}
}func handleConnection(sess quic.Connection) {stream, err := sess.OpenStream()if err != nil {log.Fatal(err)}fmt.Println("New stream opened:", stream.StreamID())defer stream.Close()var b []bytefor {// 捕获桌面屏幕img, err := captureScreen()if err != nil {log.Fatal(err)}// 将图像编码为 PNG 格式var buf bytes.Buffererr = png.Encode(&buf, img)if err != nil {log.Fatal(err)}// magic校验//n, err := stream.Write([]byte{0x05, 0x19})//if err != nil {// log.Fatal(err)//}b = buf.Bytes()//var headLenBuf = make([]byte, 4)//binary.BigEndian.PutUint32(headLenBuf, uint32(len(b)))//_, err = stream.Write(headLenBuf)err = binary.Write(stream, binary.BigEndian, uint32(len(b)))if err != nil {log.Fatal(err)}// 将图像数据发送到客户端_, err = stream.Write(b)if err != nil {log.Fatal(err)}// 每秒捕获并传输一帧time.Sleep(1 * time.Second / 30)}
}func captureScreen() (image.Image, error) {bounds := screenshot.GetDisplayBounds(0) // 捕获主屏幕img, err := screenshot.CaptureRect(bounds)if err != nil {return nil, err}return img, nil
}/*
*
openssl req -x509 -newkey rsa:4096 -keyout privkey.pem -out cert.pem -days 365 -nodes
*/
func generateTLSConfig() *tls.Config {// 使用自签名证书// goland运行使用它cert, err := tls.LoadX509KeyPair(currentDir+"/screenbroadcast/cert.pem", currentDir+"/screenbroadcast/privkey.pem")// 命令行运行使用它//cert, err := tls.LoadX509KeyPair("cert.pem", "privkey.pem")if err != nil {log.Fatal(err)}return &tls.Config{Certificates: []tls.Certificate{cert},NextProtos: []string{"h3-29"},}
}func generateTLSConfig2() *tls.Config {key, err := rsa.GenerateKey(rand.Reader, 1024)if err != nil {panic(err)}template := x509.Certificate{SerialNumber: big.NewInt(1)}certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)if err != nil {panic(err)}keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)if err != nil {panic(err)}return &tls.Config{Certificates: []tls.Certificate{tlsCert},NextProtos: []string{"h3-29"},}
}
客户端代码:
package mainimport ("bytes""context""crypto/tls""encoding/binary""fmt""github.com/quic-go/quic-go""image""image/png""io""log""time""github.com/faiface/pixel""github.com/faiface/pixel/pixelgl"
)const addr = "localhost:4000"var headLenBuf = make([]byte, 4)func main() {pixelgl.Run(run)
}func run() {tlsConf := &tls.Config{InsecureSkipVerify: true,NextProtos: []string{"h3-29"},}quicConfig := &quic.Config{MaxIdleTimeout: 40 * time.Second,KeepAlivePeriod: 30 * time.Second, // 使用quic的心跳机制}// 创建 QUIC 连接到服务器sess, err := quic.DialAddr(context.Background(), addr, tlsConf, quicConfig)if err != nil {log.Fatal(err)}// 接收一个 QUIC stream:没错,是server主动推送数据过来,先发起的open streamstream, err := sess.AcceptStream(context.Background())if err != nil {log.Fatal(err)}// 创建窗口显示接收的屏幕图像cfg := pixelgl.WindowConfig{Title: "Screen Broadcast",Bounds: pixel.R(0, 0, 1024, 680),VSync: true,Resizable: true,}win, err := pixelgl.NewWindow(cfg)if err != nil {log.Fatal(err)}for !win.Closed() {// 接收图像数据img, err := receiveImage(stream)if err != nil {if err == io.EOF {break}log.Fatal(err)}// 将图像转换为 pixel.Picturepic := pixel.PictureDataFromImage(img)// 绘制图像sprite := pixel.NewSprite(pic, pic.Bounds())win.Clear(pixel.RGB(0, 0, 0))sprite.Draw(win, pixel.IM.Moved(win.Bounds().Center()))win.Update()}
}func receiveImage(stream quic.Stream) (image.Image, error) {//_, err := io.ReadFull(stream, headLenBuf[:2])//if err != nil {// return nil, err//}//if headLenBuf[0] != 0x05 && headLenBuf[1] != 0x19 {// return nil, errors.New("invalid magic")//}_, err := io.ReadFull(stream, headLenBuf)if err != nil {fmt.Println("video Error reading:", err.Error())return nil, err}headLen := binary.BigEndian.Uint32(headLenBuf)var buf bytes.Buffer// 从 QUIC stream 读取图像数据_, err = io.CopyN(&buf, stream, int64(headLen))if err != nil {return nil, err}// 解码 PNG 图像img, err := png.Decode(&buf)if err != nil {return nil, err}return img, nil
}
下面开始说其中涉及到的坑:
当我本机(mac m1) OS版本为 12.1 时,运行服务器程序失败:
../../../../go/pkg/mod/github.com/kbinani/screenshot@v0.0.0-20240820160931-a8a2c5d0e191/darwin.go:9:10: fatal error:
'ScreenCaptureKit/ScreenCaptureKit.h' file not found
#include <ScreenCaptureKit/ScreenCaptureKit.h>
网上说升级系统到12.3+,因为ScreenCaptureKit 是 macOS 12.3 及更高版本中引入的 API,用于捕获屏幕内容。但是我升级到12.7.6后仍然报错…

然后看github.com/kbinani/screenshot源码:我当前下载的screenshot版本需要14.4+ ?
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > MAC_OS_VERSION_14_4
FYI:我不敢升级到15版本,,,不敢。。。只是小版本升级
最后解决办法:使用低版本的screenshot:
去官网:https://pkg.go.dev/github.com/kbinani/screenshot@v0.0.0-20240820160931-a8a2c5d0e191/example?tab=versions

使用低版本的2023试试:
jelex@jelexxudeMacBook-Pro screenbroadcast % go get github.com/kbinani/screenshot@v0.0.0-20230831090513-3e604f0f372a
最后果然没问题了!
坑二:client程序无法交叉编译打包

我没有在windows电脑上验证,如果有使用windows版本的golang使用者看到本篇后,是否可以帮忙打包验证?
坑三:打包服务端程序成exe,在另一台电脑上运行,本机mac 作为客户端连接后没反应,直到超时报错退出:
2024/10/09 15:29:43 timeout: no recent network activity
是否有道友愿意联调?FYI: 我周边没有golang开发者,他们电脑上没安装golang环境…
或者有大佬知道这个问题能直接赐教吗?
相关文章:
quic-go实现屏幕广播程序
最近在折腾quic-go, 突然想起屏广适合用udp实现,而http3基于quic-go,后者又基于udp, 所以玩一下。 先贴出本机运行效果图: 功能(实现)说明: 1.服务器先启动作为共享屏幕方,等待客户端连接上来 2.客户端连接 3.客户…...
C#操作SqlServer数据库语句
操作数据库语句 操作数据库语句需要搭配数据库的连接Connection类 和下达SQL命令Command类 1. ExecuteNonQuery ExecuteNonQuery 方法主要用来更新数据。通常使用它来执行Update、Insert和Delete语句,最后执行sql语句的时候可以用一个整形变量来接收,返…...
Linux之实战命令33:mount应用实例(六十七)
简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【…...
论文精读:基于概率教师学习的跨域自适应目标检测(ICML2022)
原文标题:Learning Domain Adaptive Object Detection with Probabilistic Teacher 中文标题:基于概率教师学习的域自适应目标检测 代码地址: GitHub - hikvision-research/ProbabilisticTeacher: An official implementation of ICML 2022 p…...
thinkphp 学习记录
1、PHP配置 (点开链接后,往下拉,找到PHP8.2.2版本,下载的是ZIP格式,解压即用) PHP For Windows: Binaries and sources Releases (这里是下载地址) 我解压的地址是:D:\…...
Leetcode 24 Swap Nodes in Pairs
题意:给定一个list of nodes,要求交换相邻的两个节点 https://leetcode.com/problems/swap-nodes-in-pairs/description/ Input: head [1,2,3,4] Output: [2,1,4,3] 首先你需要思考,我要交换两个节点,对于每个节点,向…...
选择 PDF 编辑器时要考虑什么?如何选择适用于 Windows 10 的 PDF 编辑器
选择 PDF 编辑器时要考虑什么? 随着技术的出现,您在网上浏览时肯定会遇到一些 PDF 软件。但是,选择PDF 编辑器时需要考虑什么?如果您是重度用户并将在您的工作场所使用它,建议您找到专业、使用方便且能够帮助您完成任…...
33-Golang开发入门精讲
├──33-Golang开发入门精讲 | └──1-Golang语法精讲 | | ├──1-介绍-go语言 | | ├──2-介绍-go语言中的面向对象 | | ├──3-第1阶段:走进Golang | | ├──4-第1阶段:走进Golang | | ├──5-第2阶段:变量与…...
研发中台拆分之路:深度剖析、心得总结与经验分享
背景在 21 年,中台拆分在 21 年,以下为中台拆分的过程心得,带有一定的主观,偏向于中小团队中台建设参考(这里的中小团队指 3-100 人的团队),对于大型团队不太适用,毕竟大型团队人中 …...
SWIFT Payment
SWIFT stands for Society for Worldwide Interbank Financial Telecommunication SWIFT——环球银行金融电信协会 SWIFT Payment Useful Link ISO 20022https://www.iso20022.org/https://www.swift.com/standards/iso-20022MT and MX Equivalence Tableshttps://www2.swift…...
数据结构之红黑树实现(全)
一、红黑树 红黑树是一种自平衡的二叉搜索树,它通过约束节点的颜色和结构来保持平衡。红黑树是由 Rudolf Bayer 在1972年发明的,被认为是一种优秀的平衡树结构,广泛应用于各种数据结构和算法中。 1.红黑树的性质 1. 每个结点是红的或者黑的…...
冷热数据分离
优质博文:IT-BLOG-CN 一、背景 随着机票业务的快速发展,订单量持续增长对业务性能带来影响,需要进行冷热数据分离。目前机票订单模块主要使用Mysql(InnoDB)作为数据库存储,历史订单信息状态修改频率低并占用大量数据库存储空间&…...
朝花夕拾:多模态图文预训练的前世今生
Diffusion Models专栏文章汇总:入门与实战 前言:时间来到2024年,多模态大模型炙手可热。在上一个时代的【多模态图文预训练】宛若时代的遗珠,本文的时间线从2019年到2022年,从BERT横空出世讲到ViT大杀四方,…...
亳州自闭症寄宿制学校,关注孩子的学习和生活
在特殊教育领域,自闭症儿童的教育与成长一直是社会各界关注的焦点。近年来,随着对自闭症认识的加深,越来越多的寄宿制学校应运而生,致力于为这些特殊的孩子提供全面、个性化的教育服务。在安徽亳州,这样的学校正努力为…...
Root me CTF all the day靶场ssrf+redis漏洞
Rootme CTF all the day靶场ssrfredis漏洞 一、环境介绍1、漏洞地址2、漏洞介绍 二、 搭建环境三、测试过程3.1 读取系统文件3.2 探测开放的服务器端口(dict协议)3.3 redis未授权访问3.3.1 利用redis来写ssh密钥(gopher协议写入)3.3.2 利用redis写定时任…...
C#中Json序列化的进阶用法
本文所有json序列化,都使用的Newtonsoft.Json包 1 JsonIgnore 在 Newtonsoft.Json 中,如果你不想将某些属性转换为 JSON 字符串,可以使用多种方法来实现。以下是几种常见的方法: 1.1 使用 [JsonIgnore] 特性 [JsonIgnore] 特性…...
IO相关的常用工具包
常用工具包Commons-io Commons-io是apache开源基金组织提供的一组有关IO操作的开源工具包。 作用:提高IO流的开发效率。 使用步骤: 1、在项目中创建一个文件夹:lib 2、将jar包复制粘贴到lib文件夹 3、右键点击jar包,选择Add as Library--->点击OK …...
Spring Boot集成RBloomFilter快速入门Demo
在大数据处理和缓存优化的场景中,布隆过滤器(Bloom Filter)因其高效的空间利用和快速的查询性能而被广泛应用。RBloomFilter是布隆过滤器的一种实现,通常用于判断一个元素是否存在于一个集合中,尽管它存在一定的误判率…...
布局性能优化
布局使用不当回导致卡顿、掉帧、响应慢等问题 一、布局流程 1、应用侧会根据前端UI描述创建后端的页面节点树,其中包含了处理UI组件属性更新、布局测算、事件处理等逻辑 2、页面节点树创建完成后,UI线程会对每个元素进行测算(Measure&#…...
智云人才推荐与管理系统
1.产品介绍 产品名称:智云人才推荐与管理系统 主要功能: 智能人才匹配引擎 功能描述:利用先进的人工智能算法,根据企业岗位需求(如技能要求、工作经验、教育背景等)自动从海量人才库中筛选并推荐最合适的…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
