资料库的webrtc文件传输

2023-11-01 13:30

本文主要是介绍资料库的webrtc文件传输,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、一个看似简单的事情往往不简单

一个简单的事情往往会倾注你的心血,也许你看到很简单往往其实没那么简单;其实想想今年业余时间的大把代码,真正能成品的好像并不多。

马上年底了,写下这篇文章。每一行程序就像写小说一样,不管好不好;代码都倾注了我的心血。

真正把一个东西做好;做到好用,用的人多,具有很好的利他性,并不是一个容易的事情,需要不断的量变。

webrtc演示网址:

文件下载测试

二、为什么放弃百度网盘

把数据存储到百度网盘上大该有半年时间,后来发现了一些致命的问题

1、发现分享外链到了10几万条之后就不能分享了

2、分享的百度外链经常会被屏蔽掉

3、分享速度不能过快,1小时只能分享100多条外链,如果分享的过快整个网盘的分享功能都会

4、百度账号需要实名认证,很容易被人认出来,作为一个技术人员我认为应该藏在幕后,尽量不要让别人认出来,这回避免很多不必要的问题

三、为什么不用云存储

4个T的存储,每月就要400元,一年就是5600元,而且不算流量费用一年就是5600,这个费用是不高的,最关键的是使用了云存储很

1、可能被黑客共计,如果别人使用curl去恶意刷流量现有的安全策略很可能被人把费用都刷去

2、每年存储费用是递增的

3、需要实名认证,一旦绑定域名,绑定着手机号,很可能被人找到

四、我的WebRtc架构设计

拜托传统的中继服务器,使用p2p打洞,调研了一些方案,本来打算使用 coturn + janus 的架构设计,但是发现janus c 写的,没有文件传输的插件,如果自己再用c去做一个插件真的是很累,于是就打算用golang 实现一个文件传输的网关。

大体步骤

1、启动网关

2、存储节点 连接到网关

3、用户浏览器请求网关,请求和存储节点交换信令

4、信令交换完成,使用dataChannel进行通信

五、网关设计

网关主要是一个websocket server 采用golang编写

网关设计主要分成了几大模块

1、room

room是一个公共领域,主要是做client和node的鉴权操作,如果鉴权成功,那么会进入Manager进行调度

2、抽象client和node

client和node 有一个共性是他们都具有连接属性,所以应该设计一个公共接口,把他们的共性抽象出来

type Lifecycle interface {Stop()Start()
}type Describable interface {Name() stringType() ConnType
}type Component interface {LifecycleDescribableAddCallback(ctx EventCallback)GetConnNumber() uint64SetContext(interface{})GetContext() interface{}Product(ctx Context)
}

通过添加一个AddCallback回调函数,确保不同模块的业务处理完全玻璃开,node的逻辑就在node里处理,client的逻辑只在client里处理,不能把不同模块的代码交叉处理,至于上下问的传输,统一抽象一个Context,里面抽象存储着我们需要的上下文信息,供给不同的回调函数以及协成之间传输使用。

type Context interface {GetData() []byteError() errorMessageType() intGetContext() interface{}SetContext(interface{})Name() string
}type EventCallback interface {OnReceive(Context, *sync.WaitGroup)OnError(Context, *sync.WaitGroup)OnClose(Context, *sync.WaitGroup)OnWriteComplete(Context, *sync.WaitGroup)
}type NodeCallback interface {OnReceive(Context, *sync.WaitGroup)OnError(Context, *sync.WaitGroup)OnClose(Context, *sync.WaitGroup)OnWriteComplete(Context, *sync.WaitGroup)
}

4、manager

负责整体调度,比如client进入manager后,查找当前可以用的node存储节点,找到后进行信令交换

节点调度,遍历查找拥有client最少的node节点,然后进行通信

// 选择最优线路
func (m *Manager) selectNode() *node.NodeClient {if len(m.nodeTree) == 0 {return nil}// 找一个挂载链接最少的节点var usableNode *node.NodeClientvar weight uint64for _, conn := range m.nodeTree {if uint64(conn.GetConnNumber()) <= weight {usableNode = conn.(*node.NodeClient)}}return usableNode
}

六、node节点收到信令后进行应答

1、创建RTCConnection

blockSize := 16384//前端页面会对sdp进行base64的encodeb, err := base64.StdEncoding.DecodeString(sdp)if err != nil {log.Error("error:%s", err)return nil}str, err := url.QueryUnescape(string(b))if err != nil {log.Error("error:%s", err)return nil}sdpDes := webrtc.SessionDescription{}fmt.Println(str)err = json.Unmarshal([]byte(str), &sdpDes)if err != nil {log.Error("json.Unmarshal err:%s", err)return nil}//创建pc, 并且指定stun服务器pc, err := webrtc.NewPeerConnection(webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:"},},},})stat, err := os.Stat("/home/zhanglei/Downloads/《实践论》(原文)毛泽东.pdf")if err != nil {log.Error("os.Stat %s error:%s", path, err)return nil}if offset > stat.Size() {log.Error("offset(%d) > stat.Size(%d)", offset, stat.Size())return nil}chunkSize := int(math.Ceil(float64(stat.Size() / int64(blockSize))))currentChunkSize := int(math.Ceil(float64(offset / int64(blockSize))))if err != nil {log.Error("%s", err)return nil}pc.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {fmt.Println("OnConnectionStateChange")fmt.Printf("Peer Connection State has changed: %s (answerer)\n", s.String())if s == webrtc.PeerConnectionStateFailed {fmt.Println("webrtc.PeerConnectionStateFailed")}})// Register data channel creation handlingpc.OnDataChannel(func(d *webrtc.DataChannel) {fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())// Register channel opening handlingd.OnOpen(func() {fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label(), d.ID())stat, err := os.Stat("/home/zhanglei/Downloads/《实践论》(原文)毛泽东.pdf")chunkSize = int(math.Ceil(float64(stat.Size() / int64(blockSize))))// 握手var chunk ChunkMessagechunk.Class = HANDSHAKEchunk.ChunkSize = uint64(chunkSize)handShakeBytes := serialize(&chunk)err = d.Send(handShakeBytes.Bytes())if err != nil {log.Error("%s", err)return}})// Register text message handlingd.OnMessage(func(msg webrtc.DataChannelMessage) {fmt.Printf("Message from DataChannel '%s': '%s'\n", d.Label(), string(msg.Data))data, err := unSerialize(msg.Data)if err != nil {log.Error("os.Open is : %s", err)return}if data.Class == ACK {handle, err := os.Open("/home/zhanglei/Downloads/《实践论》(原文)毛泽东.pdf")if err != nil {log.Error("os.Open is : %s", err)return}defer handle.Close()bufferBytes := make([]byte, blockSize)read, err := handle.Read(bufferBytes)if err != nil {log.Error("handle.Read is : %s", err)return}if read < blockSize {bufferBytes = bufferBytes[:read]}var chunk ChunkMessagechunk.Class = SENDchunk.ChunkSize = uint64(chunkSize)chunk.CurrentChunk = uint64(currentChunkSize)chunk.Data = bufferBytes// 打包发送err = d.Send(serialize(&chunk).Bytes())if err != nil {log.Error("%s", err)return}return}if data.Class == RECEIVE {handle, err := os.Open("/home/zhanglei/Downloads/《实践论》(原文)毛泽东.pdf")if err != nil {log.Error("os.Open is : %s", err)return}if data.CurrentChunk == uint64(chunkSize) {log.Info("data transfer finish")return}nextChunk := data.CurrentChunk + 1bytes := make([]byte, blockSize)read, err := handle.ReadAt(bytes, int64(nextChunk)*int64(blockSize))if err != nil {if !errors.Is(err, io.EOF) {log.Error("handle.Read is : %s", err)return}}if read < blockSize {bytes = bytes[:read]}var sendData ChunkMessagesendData.Class = SENDsendData.CurrentChunk = nextChunksendData.Data = bytessendData.ChunkSize = uint64(chunkSize)sendDataBytes := serialize(&sendData)log.Info(" read %d", nextChunk)err = d.Send(sendDataBytes.Bytes())if err != nil {log.Error("%s", err)return}// 最后一块if nextChunk == uint64(chunkSize) {d.Close()pc.Close()}return}})})_, err = pc.CreateDataChannel("sendDataChannel", nil)if err != nil {log.Error("error:%s", err)return nil}//channel.OnOpen(func() {////})//设置远端的sdpif err = pc.SetRemoteDescription(sdpDes); err != nil {log.Error("error:%s", err)return nil}//创建协商结果answer, err := pc.CreateAnswer(nil)if err != nil {log.Error("error:%s", err)return nil}pc.OnICECandidate(func(i *webrtc.ICECandidate) {fmt.Println("OnICECandidate")fmt.Println(i)})err = pc.SetLocalDescription(answer)if err != nil {log.Error("error:%s", err)return nil}//等待ice结束gatherCmp := webrtc.GatheringCompletePromise(pc)<-gatherCmp//将协商并且收集完candidate的answer,输出到控制台answerBytes, err := json.Marshal(*pc.LocalDescription())if err != nil {log.Error("error:%s", err)return nil}pc.OnICECandidate(func(candidate *webrtc.ICECandidate) {fmt.Println("OnICECandidate")fmt.Println(candidate)})t := &Transfer{sdp:              sdp,pc:               pc,offset:           uint64(offset),currentChunkSize: currentChunkSize,chunkSize:        chunkSize,answerSdp:        answerBytes,blockSize:        blockSize,}return t

2、打包传输

传输没有采用protobuf,自己写了个二进制传输

func serialize(data *ChunkMessage) *bytes.Buffer {data.Version = CodeVersionwriteBuffer := bytes.NewBuffer(nil)writeBuffer.Write([]byte{data.Version})writeBuffer.Write([]byte{data.Class})// ChunkSizebinary.Write(writeBuffer, binary.BigEndian, data.ChunkSize)// CurrentChunkbinary.Write(writeBuffer, binary.BigEndian, data.CurrentChunk)// DataLendata.DataLen = uint64(len(data.Data))binary.Write(writeBuffer, binary.BigEndian, data.DataLen)if len(data.Data) > 0 {// 添加bodywriteBuffer.Write(data.Data)}return writeBuffer
}//判断我们系统中的字节序类型
func systemEdian() binary.ByteOrder {var i int = 0x1bs := (*[int(unsafe.Sizeof(0))]byte)(unsafe.Pointer(&i))if bs[0] == 0 {return binary.LittleEndian} else {return binary.BigEndian}
}func unSerialize(data []byte) (*ChunkMessage, error) {buf := bytes.NewBuffer(data)fmt.Println(buf)var chunk ChunkMessagebinary.Read(buf, systemEdian(), &chunk.Version)binary.Read(buf, systemEdian(), &chunk.Class)binary.Read(buf, systemEdian(), &chunk.ChunkSize)binary.Read(buf, systemEdian(), &chunk.CurrentChunk)binary.Read(buf, systemEdian(), &chunk.DataLen)//chunkSize := uint64(unsafe.Pointer(&buf.Bytes()))//chunk.ChunkSize = chunkSizereturn &chunk, nil
}

7、js前端webrtc提供报价

创建webrtc连接

var pcConfig = {'iceServers': [{'urls': 'stun:',}]};localConnection = new RTCPeerConnection(pcConfig);receiveDataChannel = localConnection.createDataChannel("receiveDataChannel")receiveDataChannel.binaryType = "arraybuffer"receiveDataChannel.addEventListener('open', dataChannel.onopen);receiveDataChannel.addEventListener('close', dataChannel.onclose);receiveDataChannel.addEventListener('message', dataChannel.onmessage);receiveDataChannel.addEventListener('error', dataChannel.onError);try {this.offer = await localConnection.createOffer();} catch (e) {console.log('Failed to create session description: ', e);return}try {await localConnection.setLocalDescription(this.offer)} catch (e) {console.log('Failed to create session description: ', e);return}//eyJ1c2VyX3V1aWQiOiI1ZmZkNDE0N2JkMTMyNWNmMjYwNDAyMWYwODA5OWUyMyIsImxvZ2luX3RpbWUiOjE2Njg0NzgzOTEsIm5vd190aW1lIjoxNjY5OTgzNzkwLCJyYW5fc3RyIjoiZDA3MTczNzI3NjFjMzY0MGU2NmRlYWI5YmYyODZhNzYiLCJzaWduIjoiZjc3NzI0YjZmMTc3MzczNmVhZWFkMTM2NzllNTE0NTcifQ==transfer.ws.send((JSON.stringify(downloadRequest)));

前端对收到的数据进行序列化和反序列化

function serialize(data) {var bufLen = protoColMinSize;if (!data.Data) {bufLen += 0;} else {bufLen += data.Data.length;}data.Version = 1;var protocolBuf = new ArrayBuffer(bufLen);const bufView = new DataView(protocolBuf);bufView.setUint8(0, data.Version);bufView.setUint8(1, data.Class);if (!data.ChunkSize) {data.ChunkSize = 0}bufView.setBigUint64(2, BigInt(data.ChunkSize));if (!data.CurrentChunk) {data.CurrentChunk = 0}bufView.setBigUint64(10, BigInt(data.CurrentChunk));if (data.Data && data.Data.length > 0) {bufView.setBigUint64(18, BigInt(data.Data.length));} else {bufView.setBigUint64(18, BigInt(0));}console.log(protocolBuf)return protocolBuf;
}function unSerialize(bytes) {var versionView = new DataView(bytes).getUint8(0);// 最小长度var classByteView = new DataView(bytes).getUint8(1);// chunk 长度var chunkSizeView = parseInt(new DataView(bytes).getBigUint64(2));var currentChunkView = parseInt(new DataView(bytes).getBigUint64(10));var bodyLenView = parseInt(new DataView(bytes).getBigUint64(18));var returnData = {Version: versionView,Class: classByteView,ChunkSize: (chunkSizeView),CurrentChunk: currentChunkView,PayloadLength: bodyLenView,Payload: [],};if (bodyLenView > 0) {returnData.Payload = new Uint8Array(bytes, protoColMinSize, bodyLenView)}return returnData;
}

八、webrtc文件传输的优缺

1)优点

1、点对点传输,不经过中继服务器

2、民用带宽比较便宜,最差的情况下是带宽打满,不会出现很高的流量费用

3、可以自建存储,存储节点可以使用群辉,可以买自己服务器

4、不需要固定的ip地址

2)缺点

1、宽带线民用的不知道能申请几根

2、存储维护硬件也是一个麻烦的事情,硬盘很可能出现故障,运维也是一个头痛的事情

这篇关于资料库的webrtc文件传输的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/323326

相关文章

雷动WEBRTC产品

http://www.rtcpower.com/html/leidongwebrtc.html ; 1.前言      WebRTC是一项在浏览器内部进行实时视频和音频通信的技术,是谷歌2010年以6820万美元收购Global IP Solutions公司而获得一项技术。WebRTC实现了基于网页的视频会议,标准是WHATWG 协议,目的是通过浏览器提供简单的javascript就可以

Apple quietly slips WebRTC audio, video into Safari's WebKit spec

转自:http://www.zdnet.com/article/apple-quietly-slips-webrtc-audio-video-into-safaris-webkit-spec/?from=timeline&isappinstalled=0 http://www.zdnet.com/article/apple-quietly-slips-webrtc-audio-video-

WebRTC-nack机制详解

1.NACK的含义 丢包重传(NACK)是抵抗网络错误的重要手段。NACK在接收端检测到数据丢包后,发送NACK报文到发送端;发送端根据NACK报文中的序列号,在发送缓冲区找到对应的数据包,重新发送到接收端。NACK需要发送端,发送缓冲区的支持。 WebRTC中支持音频和视频的NACK重传。我们这里只分析nack机制,不分析jitterbuffer或者neteq的更多实现。 2.WebRTC

远程桌面文件传输异常或者取消传输后一直显示正在取消

环境: Window Servers 2008 R2 摘要说明: 本篇文章主要讲述当应用远程桌面进行文件传输时,若因网络等导致进程中断,再次传输时则不能进行文件传输;或者传输时取消传输,然后一直显示正在取消。此时可以通过重启window的rdpclip.exe进程来解决此问题 步骤 1.关闭rdpclip.exe进程 远程桌面连上上传输异常的服务器,打开资源管理器,在进程列关闭rdpc

828华为云征文|华为云Flexus X实例docker部署srs6并调优,协议使用webrtc与rtmp

828华为云征文|华为云Flexus X实例docker部署srs6并调优,协议使用webrtc与rtmp 华为云最近正在举办828 B2B企业节,Flexus X实例的促销力度非常大,特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务的需求,一定不要错过这个机会。赶紧去看看吧! 什么是华为云Flexus X实例 华为云Flexus X实例云服务是新

推荐一款好用的局域网文件传输工具

在当今这个数据驱动的时代,无论是企业还是个人,快速且安全地传输数据已经成为了一项基本需求。局域网内的文件传输工具因其高效和便捷的特性,成为了日常工作中不可或缺的工具。面对市场上众多的文件传输解决方案,用户们往往感到选择困难。今天,我们就来聊聊为什么企业和个人如此需要局域网文件传输工具,并推荐一款备受好评的局域网文件传输利器。 局域网文件传输软件的必要性 局域网文件传输的必要性和普遍需求

WebRTC协议下的视频汇聚融合技术:EasyCVR构建高效视频交互体验

视频汇聚融合技术是指将来自不同源、不同格式、不同网络环境的视频流进行集中处理、整合和展示的技术。随着视频监控、远程会议、在线教育、直播娱乐等领域的快速发展,视频数据的规模急剧增长,对视频处理能力和效率提出了更高要求。视频汇聚融合技术通过统一的平台或系统,实现了视频资源的有效整合与高效利用,为用户提供了更加丰富、灵活的视频服务体验。 一、EasyCVR视频汇聚平台关键技术 视频编解码:采用高效的

传输大咖37 | 教育培训行业文件传输难题及解决方案

教育培训行业在文件传输过程中面临着一系列挑战,这些挑战不仅影响了工作效率,也对数据安全构成了潜在威胁。本文将探讨教育培训行业在文件传输方面的痛点,并提出镭速传输系统作为解决方案。 教育培训行业文件传输的痛点 信息孤岛问题:教育机构内部不同部门或校区间信息共享困难,导致资源无法有效利用。 数据安全风险:存储有大量敏感数据,如学生个人信息、成绩等,一旦泄露后果严重。 法规遵从性:需严格

服务器间进行文件传输-SFTPSCP一篇搞定

1.简单介绍一下 在一些特殊场景,两台服务器之间需要进行文件传输的时候,或许我们会想到FTP,这也是我们常见的实现方式,但是如果我们不能操作远程服务器,无法判断远程服务器是否安装了FTP呢,众所周知,FTP使用的前提时确定服务器上配置了FTP服务,并且正在运行FTP服务器软件,这是最大的前提,如果我们不知道,那么就不能贸然的使用该方式。 我的需求是:我只知道对方服务器的ip、端口、用户名、密码

华为云征文|华为云Flexus X实例docker部署srs6并调优,协议使用webrtc与rtmp

华为云征文|华为云Flexus X实例docker部署srs6并调优,协议使用webrtc与rtmp 什么是华为云Flexus X实例 华为云Flexus X实例云服务是新一代开箱即用、体验跃级、面向中小企业和开发者打造的高品价比云服务产品。Flexus云服务器X实例是新一代面向中小企业和开发者打造的柔性算力云服务器,可智能感知业务负载,适用于电商直播、企业建站、开发测试环境、游戏服务器、音视