网站建设上海哪家公司好,黄骅市找工作,定制网站建设济南,个人网站 作品最近在折腾quic-go, 突然想起屏广适合用udp实现#xff0c;而http3基于quic-go#xff0c;后者又基于udp, 所以玩一下。
先贴出本机运行效果图#xff1a;
功能(实现)说明#xff1a;
1.服务器先启动作为共享屏幕方#xff0c;等待客户端连接上来 2.客户端连接 3.客户…最近在折腾quic-go, 突然想起屏广适合用udp实现而http3基于quic-go后者又基于udp, 所以玩一下。
先贴出本机运行效果图
功能(实现)说明
1.服务器先启动作为共享屏幕方等待客户端连接上来 2.客户端连接 3.客户端和服务器建立连接后服务器主动打开stream
在一个for 循环中每秒操作30次下面操作 4.服务器开始抓取本机屏幕内容转换成Image5.数据传输协议Image字节长度 Image内容6.客户端按上述协议接收数据解析成Image对象放界面上展示 服务端代码
package mainimport (bytescontextcrypto/randcrypto/rsacrypto/x509encoding/binaryencoding/pemfmtgithub.com/quic-go/quic-goimageimage/pnglogmath/bigostimecrypto/tlsgithub.com/kbinani/screenshot
)const addr localhost:4000var 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 (bytescontextcrypto/tlsencoding/binaryfmtgithub.com/quic-go/quic-goimageimage/pngiologtimegithub.com/faiface/pixelgithub.com/faiface/pixel/pixelgl
)const addr localhost:4000var 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/screenshotv0.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_4FYI我不敢升级到15版本不敢。。。只是小版本升级
最后解决办法使用低版本的screenshot 去官网https://pkg.go.dev/github.com/kbinani/screenshotv0.0.0-20240820160931-a8a2c5d0e191/example?tabversions 使用低版本的2023试试
jelexjelexxudeMacBook-Pro screenbroadcast % go get github.com/kbinani/screenshotv0.0.0-20230831090513-3e604f0f372a最后果然没问题了
坑二client程序无法交叉编译打包 我没有在windows电脑上验证如果有使用windows版本的golang使用者看到本篇后是否可以帮忙打包验证
坑三打包服务端程序成exe在另一台电脑上运行本机mac 作为客户端连接后没反应直到超时报错退出
2024/10/09 15:29:43 timeout: no recent network activity是否有道友愿意联调FYI: 我周边没有golang开发者他们电脑上没安装golang环境…
或者有大佬知道这个问题能直接赐教吗