《Web性能权威指南》-WebRTC-读书笔记
本文是《Web性能权威指南》第四部分——WebRTC的读书笔记。
第一部分——网络技术概览,请参考网络技术概览;
第二部分——无线网络性能,请参考无线网络性能;
第三部分——HTTP,请参考HTTP;
第四部分——浏览器API与协议的前四章,请参考浏览器API与协议。
WebRTC
Web Real-Time Communication,Web实时通信,WebRTC,由一组标准、协议和JS API组成,用于实现浏览器之间(端到端)的音频、视频及数据共享。WebRTC使得实时通信变成一种标准功能,任何Web应用都无需借助第三方插件和专有软件,而是通过简单的JavaScript API即可利用。
要实现涵盖音频和视频的电话会议等完善、高品质的RTC应用、端到端的数据交换,需要浏览器具备很多新功能:音频和视频处理能力、支持新应用API、支持好几种新网络协议。浏览器把前述这些复杂性抽象成三个主要API:
- MediaStream:获取音频和视频流;
- RTCPeerConnection:音频和视频数据通信;
- RTCDataChannel:任意应用数据通信。
WebRTC通过UDP传输数据。
标准和WebRTC的发展
WebRTC架构由十余个标准组成,涵盖应用和浏览器API,以及很多必要的协议和数据格式:
- W3C的Web Real-Time Communications(WEBRTC)Working Group负责制定浏览器API;
- IETF的Real-Time Communication in Web-browsers(RTCWEB)工作组负责定义协议、数据格式、安全及其他在浏览器中实现端到端通信必需的内容。
设计WebRTC时也会考虑已有通信系统:VOIP(VoiceOver IP)、各种SIP客户端、PSTN(Public Switched Telephone Network,公共交换电话网)等。
音频和视频引擎
要实现电话会议功能,浏览器必须访问系统硬件采集音频和视频;还需对它们分别加以处理以增强品质,保证同步,而且要适应不断变化的带宽和客户端之间的网络延迟调整输出的比特率。
接收端的处理过程相反,必须实时解码音频和视频流,并适应网络抖动和时延。
如上图,浏览器会负责:
- 音频流降噪和回声消除处理、优化的窄带或宽带音频编解码器编码、错误补偿算法消除网络抖动和丢包造成的损失;
- 视频的影像品质,选择最优的压缩和编解码方案,应用抖动和丢包补偿。
浏览器要动态调整其处理流程,以适应不断变化的音频和视频流及网络条件。
W3C的Media Capture and Streams规范规定一套JS API:
解读:
- MediaStream对象包含一或多个Track(MediaStreamTrack);
- MediaStream中的多个Track相互之间是同步的;
- 输入源可以是物理设备,如麦克风、摄像头、用户硬盘或另一端远程服务器中的文件;
- MediaStream的输出可被发送到一或多个目的地:本地的视频或音频元素、后期处理的JS代理,或远程另一端。
MediaStream对象表示一个实时的媒体流,以便应用代码从中取得数据,操作个别的Track和控制输出。所有的音频和视频处理,比如降噪、均衡、影像增强等都由音频和视频引擎自动完成。
getUserMedia()
,从底层平台取得音频和视频流的简单API;负责获准访问用户的麦克风和摄像机,并获取符合指定要求的流。取得流之后,还可以将它们提供给其他浏览器API:
- 通过Web Audio API在浏览器中处理音频;
- 通过Canvas API采集个别视频帧并加以处理;
- 通过CSS3和WebGL API为输出的流应用各种2D/3D特效。
当前的WebRTC实现使用Opus和VP8编解码器:
- Opus编解码器用于音频,支持固定和可变的比特率编码,适合的带宽范围为
6~510
Kbit/s。这个编解码器可以无缝切换,以适应不同的带宽。 - VP8编解码器用于视频编码,要求带宽为
100~2000+
Kbit/s,比特率取决于流的品质,如180p,360p,720p。
实时网络传输
实时通信讲究的就是及时、当下。因此,处理音频和视频流的应用一定要补偿间歇性的丢包:音频和视频编解码器可以填充小的数据空白,通常对输出品质的影响也很小。类似地,应用必须实现自己的逻辑,以便因传输其他应用数据而丢包或延迟时快速恢复。及时和低延迟比可靠更重要。
UDP协议才更适合用于传输实时数据。UDP提供下列不服务:
- 不保证消息交付:不确认,不重传,无超时;
- 不保证交付顺序:不设置包序号,不重排,不会发生队首阻塞;
- 不跟踪连接状态:不必建立连接或重启状态机;
- 不需要拥塞控制:不内置客户端或网络反馈机制。
UDP是浏览器实时通信的基础,但要完全达到WebRTC的要求,浏览器还需要位于其上的大量协议和服务的支持,如下图所示:
WebRTC的协议分层:
- ICE:Interactive Connectivity Establishment,RFC 5245
- STUN:Session Traversal Utilities for NAT,RFC 5389
- TURN:Traversal Using Relays around NAT,RFC 5766
- SDP:Session Description Protocol,会话描述协议,RFC 4566
- DTLS:Datagram Transport Layer Security,RFC 6347
- SCTP:Stream Control Transport Protocol,RFC 4960
- SRTP:Secure Real-Time Transport Protocol,RFC 3711
ICE、STUN和TURN是通过UDP建立并维护端到端连接所必需的。DTLS用于保障传输数据的安全,加密是WebRTC强制的功能。SCTP和SRTP属于应用层协议,用于在UDP之上提供不同流的多路复用、拥塞和流量控制,以及部分可靠的交付和其他服务。
RTCPeerConnection API
RTCPeerConnection接口负责维护每一个端到端连接的完整生命周期:
- 管理穿越NAT的完整ICE工作流;
- 发送自动(STUN)持久化信号;
- 跟踪本地流;
- 跟踪远程流;
- 按需触发自动流协商;
- 提供必要的API,以生成连接提议,接收应答,允许查询连接的当前状态等。
RTCPeerConnection把所有连接设置、管理和状态都封装在一个接口中。
DataChannel API用于实现端到端之间的任意应用数据交换,类似于WebSocket,但却是端到端交换。而且,底层传输机制的属性也是可定制的。每个DataChannel可以经过配置提供以下特性:
- 发送消息可靠或部分可靠的交付;
- 发送消息有序或乱序交付。
不可靠的乱序交付等同于原始UDP,即消息可能到达,也可能不到达,而且到达次序也没有保证。然而可让信道部分可靠,也就是设定重传的最大次数或时间限制:WebRTC的各个层负责处理确认和超时。
建立端到端的连接
打开XHR、EventSource或新WebSocket会话很简单:依赖于定义完善的HTTP握手机制协商连接参数,都假定客户端可访问到目标服务器(比如,服务器具有公开的可路由到的IP地址,或客户端和服务器都在一个内部网中)。
WebRTC两端则很可能分别位于自己的私有网络中,中间还隔着一或多层NAT。为了发起会话,首先必须找到两端的候选IP和端口,穿越NAT,检查连接,以期找到可用路径。
要想成功地建立端到端的连接,必须首先解决另外几个问题:
- 必须通知另一端想打开一个端到端的连接,以便它知道开始监听到来的分组;
- 必须找出两端之间建立连接所需的路由线路,并在两端传播这个信息;
- 必须交换有关媒介和数据流的必要信息,如协议、编码等。
WebRTC解决其中一个问题:内置ICE协议会执行必要的路由和连接检查。然而,发送通知(信号)和协商会话仍然要由应用负责。
发信号和协商会话
在检查连接或协商会话前,必须知道能否将信息发送到另一个端,另一端是否愿意建立连接。为此,必须发出一个信号,而另一端必须返回应答。问题来了:如果另一端没有监听数据包,怎么办?最低限度,需要一个共享的发信通道。
WebRTC把发送信号和协议的选择交给应用,而标准有意未给发送信号的过程提供建议或实现。这样可让现有通信基础设施中的其他发信协议能够互操作,包括如下几个协议:
- SIP:Session Initiation Protocol,会话初始协议,应用级发信协议,广泛用于通过IP实现的语音通话(VoIP)和视频会议;
- Jingle:XMPP协议的发信扩展,用于VoIP和视频会议的会话控制;
- ISUP:ISDN User Part,ISDN用户部分,全球各大公共电话交换网中用于启动电话呼叫的发信协议。
WebRTC应用可以选择已有的任何发信协议和网关,利用既有通信系统协商一次通话或视频会议。
发信服务器可以作为已有通信网络的网关,此时由网络负责将连接提议发送给目标端,然后再将应答返回给WebRTC客户端,以初始化信息交换。而应用也可使用自定义发信信道,可能由一或多台服务器和一个自定义通信协议构成:如果两端都连到同一个发信服务,那这个服务就可以为它们传递消息。
可与WebRTC互操作的通信网关,如开源的Asterisk。Asterisk有一个WebSocket模块,该模块支持将SIP作为发信协议,浏览器建立到Asterisk网关的WebSocket连接,然后两者通过交换SIP消息来协商会话。
SDP
Session Description Protocol,会话描述协议。
应用实现共享的发信通道后,接下来就可以发起WebRTC连接:
// 初始化共享的发信通道
var signalingChannel = new SignalingChannel();
var pc = new RTCPeerConnection({});
// 向浏览器请求音频流
navigator.getUserMedia({ "audio": true }, gotStream, logError);
function gotStream(stream) {// 通过RTCPeerConnection注册本地音频流pc.addstream(stream);// 创建端到端连接的SDP(提议)描述pc.createOffer(function(offer) {// 以生成的SDP作为端到端连接的本地描述pc.setLocalDescription(offer);// 通过发信通道向远端发送SDP提议signalingChannel.send(offer.sdp);});
}
function logError() { ... }
WebRTC使用SDP描述端到端连接的参数。SDP不包含媒体本身的任何信息,仅用于描述会话状况,表现为一系列的连接属性:要交换的媒体类型(音频、视频及应用数据)、网络传输协议、使用的编解码器及其设置、带宽及其他元数据。
SDP是一个基于文本的简单协议(RFC 4568),用于描述会话属性。WebRTC应用不必直接处理SDP。JSEP( JavaScript Session Establishment Protocol , JS会话建立协议)定义对RTCPeerConnection对象几个方法的简单调用,就把SDP所有的内部工作全都隐藏起来。生成提议之后,就可通过发信通道将它发送给远端。如何编码SDP取决于应用:SDP字符串可以像前面那样(作为简单的文本blob)直接传输,也可以将它编码成任意格式后再传输。Jingle协议提供从SDP到XMPP(XML)格式的映射。
要建立端到端的连接,两端都必须遵循一个对称的工作流,以交换各自音频、视频及其他数据流的SDP描述。
ICE
端与端之间往往有很多层防火墙和NAT设备阻隔。
查询操作系统获知IP地址(如果有多块网卡,就需要多个IP地址),将IP地址加端口号追加到生成的SDP字符串中。
WebRTC框架可处理大部分复杂工作:
- 每个RTCPeerConnection连接对象都包含一个ICE代理;
- ICE代理负责收集IP地址和端口;
- ICE代理负责执行两端的连接检查;
- ICE代理负责发送连接持久化信息。
设置好会话描述后(无论本地还是远程),本地ICE代理会自动开始发现本地端所有可能的候选IP和端口的进程:
- ICE代理向操作系统查询本地IP地址;
- 如果有配置,ICE代理会查询外部STUN服务器,以取得本地端的公共IP和端口号;
- 如果有配置,ICE代理会将TURN服务器追加为最后一个候选项;假如端到端的连接失败,数据将通过指定的中间设备转发。
每发现一个新候选项(一个IP加一个端口号),代理就会自动通过RTCPeerConnection对象注册它,并通过一个回调函数(onicecandidate)通知应用。ICE在完成收集工作后,也会再触发同一个回调函数,以通知应用。
var ice = {"iceServers": [// STUN服务器,配置为使用谷歌的公共测试服务器{"url": "stun:stun.l.google.com:19302"},// TURN服务器,用于端到端连接失败时转发数据{"url": "turn:user@turnserver.com", "credential": "pass"}]};
var signalingChannel = new SignalingChannel();
var pc = new RTCPeerConnection(ice);
navigator.getUserMedia({ "audio": true }, gotStream, logError);
function gotStream(stream) {pc.addstream(stream);pc.createOffer(function(offer) {// 应用本地会话描述:初始化ICE收集过程pc.setLocalDescription(offer);});
}pc.onicecandidate = function(evt) {// 预订ICE事件,监听ICE收集完成if (evt.target.iceGatheringState == "complete") {local.createOffer(function(offer) {console.log("Offer with ICE candidates: " + offer.sdp);// 生成SDP提议(此时包含发现的ICE候选项)signalingChannel.send(offer.sdp);});}
}
// 包含ICE候选项的提议:
// a=candidate:1862263974 1 udp 2113937151 192.168.1.73 60834 typ host // 本地端的私有ICE候选项
// a=candidate:2565840242 1 udp 1845501695 50.76.44.100 60834 typ srflx // STUN服务器返回的公有ICE候选项
ICE代理处理大部分复杂工作:ICE收集过程是自动触发的,STUN查找是在后台执行的,而发现的候选项也会自动通过RTCPeerConnection对象注册。上述过程完成后,可生成SDP提议,并通过发信通道发送给另一端。另一端接收到ICE候选项后,就可建立端到端的连接:只要RTCPeerConnection对象设置远程会话描述(包含另一端的一组候选IP和端口号),ICE代理就会执行连接检查,以确定能否抵达另一端。
ICE代理发送消息(STUN绑定请求),另一端接收之后必须以一个成功的STUN响应确认。如果这个过程完成,则代表有一条端到端连接的路由线路!相反,如果所有候选项都绑定失败,要么将RTCPeerConnection标记为失败,要么回退到靠TURN转发服务器建立连接。
ICE代理自动确定连接检查时候选项的次序和优先级:首先检查本地IP地址,然后是公共IP,最后才检查TURN。建立连接后,ICE代理周期性地向另一端发送STUN请求,以此保证连接的持久化。
Trickle ICE
ICE收集过程决不是瞬间就能完成的:取得本地IP地址很快,但查询STUN服务器需要经过到外部服务器的往返,还有另一次端到端的STUN连接检查。Trickle ICE是对ICE协议的扩展,用于在于实现端与端之间的增量收集和连接检查:
- 两端交换没有ICE候选项的SPD提议;
- 发现ICE候选项之后,通过发信通道发送到另一端;
- 新候选描述一就绪,立即执行ICE连接检查。
不等到ICE收集过程完成,而是依靠发信通道向另一端递增地交付更新,从而加快协商。
Trickle ICE导致发信通道的流量增加,但可显著减少初始化端到端连接的时间。所有WebRTC应用都应该考虑这一点:尽快发送提议,然后依次发现依次发送ICE候选项。
跟踪ICE收集和连接状态
内置的ICE框架负责候选项发现、连接检查、持久化等。开发者要做的只是在初始化RTCPeerConnection对象时指定STUN和TURN服务器。
iceGatheringState属性保存本地端候选项的收集状态:
- new:对象刚刚创建,还没有连网;
- gathering:ICE代理正在收集本地候选项;
- complete:ICE代理收集过程完成。
如上图,iceConnectionState属性中保存着端到端的连接状态:
- new:ICE代理正在收集候选项且/或正在等待远程候选项的到来;
- checking:ICE代理至少已经收到来自一个组件的远程候选项,而且正在检查候选项,但尚未发现连接;除检查外,可能仍然在收集;
- connected:ICE代理已经找到一条通过所有组件的可用连接,但仍在检查其他候选项,以确定是否存在更好的连接;此时仍有可能还在收集;
- completed:ICE代理已经完成收集和检查,且发现通过所有组件的连接;
- failed:ICE代理检查完所有候选项,但至少有一个组件的连接失败;其他一些组件的连接可能成功;
- disconnected:一或多个组件的活动检查失败,相对failed更严重,在不稳定的网络上可能会间歇性触发(不需要采取什么行动);
- closed:ICE代理已经关闭,不再响应STUN请求。
ICE代理最重要的目标,就是识别端到端之间的可行路径。可ICE代理不会就此止步。即便是连接已经建立,ICE代理也可能周期性地尝试其他候选项,以确定其他路径的性能是否更好。
Chrome提供一个工具,可检查任何WebRTC连接的工作流和状态。打开标签页chrome://webrtc-internals
,可检查所有打开的端到端连接,查看交换的SDP描述:
完整示例
初始化WebRTC连接
响应WebRTC连接
simpleWebRTC库:使用一个用于穿透NAT的公共STUN服务器初始化了RTCPeerConnection,使用getUserMedia请求音频和视频流,并初始化了连接到它自己发信服务器的WebSocket连接。
交付媒体和应用数据
要实现实时通信,还需要流量控制、拥塞控制、错误校验、带宽预测、延迟机制、通信加密等能力。
WebRTC又在UDP之上增加几层协议:
- DTLS:Datagram Transport Layer Security,数据报传输层安全,用于加密传输应用数据时针对要传输的媒体数据协商密钥;
- SRTP:Secure Real-Time Transport,安全实时传输,用于传输音频和视频流;
- SCTP:Stream Control Transport Protocol,流控制传输协议,用于传输应用数据。
通过DTLS实现安全通信
DTLS本质上就是TLS,为兼容UDP的数据报传输而做一些微小修改。
DTLS解决下列问题:
- TLS要求可靠的有序的适合分段的握手记录以协商信道;
- 如果在混合多个分组的基础上对记录分段,就不能保证TLS的完整性校验;
- 如果记录的顺序不对,也不能保证TLS的完整性校验。
DTLS对TLS记录协议的扩展,就是为每条握手记录明确添加分段偏移字段和序号。这样就满足有序交付的条件,也能让大记录可以被分段成多个分组并在另一端再进行组装。DTLS握手记录严格按照TLS协议规定的顺序传输,顺序不对就报错。DTLS还要处理丢包问题:两端都使用计时器,如果预定时间内没有收到应答,就重传握手记录。
记录序号、偏移值和重传计时器让DTLS在UDP之上实现握手。为保证过程完整,两端都要生成自已签名的证书,然后按照常规的TLS握手协议走。
完整的DTLS握手需要两次往返。即,建立端到端的连接会产生额外延迟。
WebRTC客户端自动为每一端生成自已签名的证书。因此,也就没有证书链需要验证。DTLS保证加密和完整性,但把身份验证工作留给应用。最后在满足握手要求的基础上,DTLS为处理常规记录可能出现的分段和乱序问题,又增加两条重要的规则:
- DTLS记录必须刚好放到一个网络分组中;
- 必须有一个分组密码用于加密记录数据。
常规TLS记录最大可以达到16KB。TCP可以处理分段和组装,但UDP不提供这些服务。结果,为适应UDP协议的乱序发送,也为了最大程度保持其语义,每个携带应用数据的DTLS记录都必须放到一个UDP分组中。类似地,由于它们潜在依赖记录数据的有序发送,因此也不允许使用流密码。
通过SRTP和SRTCP交付媒体
WebRTC以完全托管的形式提供媒体获取和交付服务:从摄像头到网络,再从网络到屏幕。WebRTC应用指定获取流的媒体约束,然后通过RTCPeerConnection对象注册它们。从此以后,就都是浏览器提供的WebRTC媒体和网络引擎的事:编码优化、处理丢包、网络抖动、错误恢复、流量、控制等。
RTP:Real-Time Transport Protocol,实时传输协议,由RFC 3550定义。
WebRTC实际上并不是通过IP网络实时交付音频和视频的第一个应用。WebRTC只是重用了VoIP电话使用的传输协议、通信网关和各种商业或开源的通信服务:
- 安全实时传输协议(SRTP,Secure RTP)通过IP网络交付音频和视频等实时数据的标准安全格式。
- 安全实时控制传输协议(SRTCP,Secure Real-time Control Transport Protocol)通过SRTP流交付发送和接收方统计及控制信息的安全控制协议。
SRTP为通过IP网络交付音频和视频定义标准的分组格式。SRTP本身并不对传输数据的及时性、可靠性或数据恢复提供任何保证机制,它只负责把数字化的音频采样和视频帧用一些元数据封装起来,以辅助接收方处理这些流。
解读:
- 每个SRTP分组都包含一个自动递增的序号,以便接收端检测和发现媒体数据是否乱序;
- 每个SRTP分组都包含一个时间戳,表示媒体净荷第一字节的采样时间,用于多个媒体流(如音频和视频)的同步;
- 每个SRTP分组都包含一个SSRC标识符,这是个别媒体流中每个分组的唯一流ID;
- 每个SRTP分组可以包含其他可选的元数据;
- 每个SRTP分组都包含加密的媒体净荷,以及(可选的)认证标签,后者用于验证分组的完整性。
SRTP分组中包含媒体引擎实时回放流必需的所有信息。而控制每个SRTP分组交付则是SRTCP协议的责任,SRTCP针对每个媒体流实现独立的外部反馈渠道。
SRTCP会跟踪发送及丢失字节和分组的数量,跟踪每个SRTP分组的序号、交错到达抖动,以及其他SRTP统计信息。然后,两端定时交换这些数据,以便调整每个流的发送速率、编码品质和其他参数。
SRTP和SRTCP直接在UDP之上运行,共同完成对应用提供的音频和视频流的实时适配和优化。WebRTC应用不会接触内部的SRTP或SRTCP协议:如果你要构建自定义的WebRTC客户端,那得直接操作这两个协议,否则浏览器会替你搭建好所有必要的基础设施。
关于SRTP和SRTCP,还需要考虑另外一些细节:
- SRTP和SRTCP都会加密应用净荷数据(WebRTC要求),但它们都没有提供协商密钥的机制!这就是为什么必须先进行DTLS握手的原因:DTLS握手会为两端确定一个共享密钥,随后的SRTP和SRTCP可以使用这个密钥。
- SRTP和SRTCP都要求对不同的流分配不同的端口,而这对于NAT或防火墙后面的客户端当然就是一个问题。为解决这个问题,WebRTC使用另一个多路复用扩展,以便向同一个目标端口交付多个流(以及相应的控制信道)。
- IETF还制定一个新的拥塞控制算法,该算法利用SRTCP的反馈对WebRTC应用生成的音频和视频流进行优化。
通过SCTP交付应用数据
WebRTC对RTCDataChannel接口及其传输协议有哪些要求:
- 传输层必须支持多个独立信道的复用:
- 每个信道必须支持有序或乱序交付;
- 每个信道必须支持可靠或不可靠交付;
- 每个信道可以支持应用定义的优先级。
- 传输层必须提供一个面向消息的API:
- 每条应用消息都可能在传输层被分段和组装。
- 传输层必须实现流量和拥塞控制机制。
- 传输层必须保证数据的机密性和完整性。
TCP、UDP与SCTP比较
对比项目 | TCP | UDP | SCTP |
---|---|---|---|
可靠性 | 可靠 | 不可靠 | 可配置 |
交付次序 | 有序 | 乱序 | 可配置 |
传输方式 | 面向字节 | 面向消息 | 面向消息 |
流量控制 | 支持 | 不支持 | 支持 |
拥塞控制 | 支持 | 不支持 | 支持 |
DataChannel
设置与协商
配置消息次序和可靠性
部分可靠交付与消息大小
使用场景及性能
音频、视频和数据流
多方通信架构
基础设施及容量规划
数据效率及压缩
性能检查表
注意事项:
- 发信服务
- 使用低延迟传输机制;
- 提供足够的容量;
- 建立连接后,考虑使用DataChannel发信。
- 防火墙和NAT穿越
- 初始化RTCPeerConnection时提供STUN服务器;
- 尽可能使用增量ICE,虽然发信次数多,但建立连接速度快;
- 提供STUN服务器,以备端到端连接失败后转发数据;
- 预计并保证TURN转发时容量足够用。
- 数据分发
- 对于大型多方通信,考虑使用超级节点或专用的中间设备;
- 中间设备在转发数据前,考虑先对其进行优化或压缩。
- 数据效率
- 对音频和视频流指定适当的媒体约束;
- 优化通过DataChannel发送的二进制净荷;
- 考虑压缩通过DataChannel发送的UTF-8数据;
- 监控DataChannel缓冲数据的量,同时注意适应网络条件变化。
- 交付及可靠性
- 使用乱序交付避免队首阻塞;
- 如果使用有序交付,把消息大小控制到最小,以降低队首阻塞的影响;
- 发送小消息(<1150字节),以便将分段应用消息造成的丢包损失降至最低;
- 对部分可靠交付,设置适当的重传次数和超时间隔;
- 正确地设置取决于消息大小、应用数据类型和端与端之间的延迟。
相关文章:
《Web性能权威指南》-WebRTC-读书笔记
本文是《Web性能权威指南》第四部分——WebRTC的读书笔记。 第一部分——网络技术概览,请参考网络技术概览; 第二部分——无线网络性能,请参考无线网络性能; 第三部分——HTTP,请参考HTTP; 第四部分——浏览…...
跨境电商独立站:打造你的全球品牌
什么是跨境电商独立站? 跨境电商独立站是指一个独立的电子商务网站,企业可以通过这个网站直接向全球消费者销售产品。与入驻亚马逊、eBay等第三方平台不同,独立站拥有完全自主权,可以自由定制店铺风格、营销策略,并直…...
基于uniapp微信小程序的旅游系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
怿星科技薛春宇丨智能汽车软件研发工具链国产化的挑战和探索
2024年7月25日,由上海良益企业管理咨询有限公司主办的“2024域控制器技术论坛“在上海成功举办,十位嘉宾做了精彩分享。“整零有道”将陆续刊出部分演讲的文字实录,以飨读者。 本期刊出怿星科技副总经理薛春宇的演讲实录:智能汽车…...
Flutter动画渐变
User experience is everything. One way to improve it is by making transitions between different UI elements smoother and more visually appealing. This is where the AnimatedCrossFade widget comes in handy. 用户体验就是一切。改善用户体验的方法之一就是让不同…...
Python毕业设计选题:基于Web学生会网站的设计与实现-django
开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 系统首页界面 用户注册界面 用户登录界面 校内报道界面 品牌活动界面 个人中心界面 …...
如何选购高性价比百元头戴式耳机?六大选购技巧加性价比耳机推荐
在日益繁忙的生活中,头戴式耳机已成为许多人享受音乐、放松心情的重要工具。然而,市面上的头戴式耳机种类繁多,价格各异,如何选购高性价比百元头戴式耳机?成为了许多消费者的难题。为了帮助大家更好地做出选择…...
Java爬虫的京东“寻宝记”:揭秘商品类目信息
开篇:Java特工的神秘任务 在这个数据驱动的时代,我们就像一群特工,穿梭在数字的海洋中,寻找着隐藏的宝藏——商品类目信息。今天,我们将带领你一起,用Java这把精密的瑞士军刀,深入京东的神秘领…...
React前端框架
React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发和维护。React 采用组件化的开发方式,使得开发者可以构建可复用的 UI 组件,从而提高开发效率和代码的可维护性。 React 的基本概念 组件:React 的核心概念是组件…...
React-query vs. 神秘新工具:前端开发的新较量
流畅的分页体验:AlovaJS的分页请求策略 在现代web应用中,分页是一个常见的功能需求。无论是浏览商品列表、查看文章集合,还是管理后台的数据表格,用户都需要一种高效且流畅的方式来浏览大量数据。然而,实现一个流畅且…...
TensorFlow面试整理-分布式
在深度学习的训练过程中,随着数据量和模型的复杂性增加,单个 GPU 或 CPU 无法满足高效训练的需求。TensorFlow 提供了强大的 分布式训练 功能,通过并行处理加速训练过程。分布式训练可以在多个 GPU、多个机器甚至是 TPU 上运行。以下是分布式训练的关键概念及其使用方法。 1…...
OceanBase 回收站机制详解
OceanBase 回收站机制详解 在 OceanBase 数据库中,回收站机制用于在执行 DROP 或 TRUNCATE 等操作后,临时保存被删除的对象,以便在需要时进行恢复。以下是对回收站机制的详细说明: 1. 不同租户对回收站的访问权限 SYS 租户 权…...
Java特工队:潜入京东,高效获取商品详情的绝密行动
在这个由代码和逻辑编织的电商世界里,京东商品详情就像是被锁在高塔中的神秘卷轴,等待着勇敢的Java特工队成员去解救。今天,我们要讲述的是如何装备你的Java代码装备,化身为一名编程界的特工,潜入京东的API网络&#x…...
车易泊相机 —— 智能车位管理的得力助手
在当今社会,停车问题日益成为城市管理和人们日常生活中的一大难题。寻找车位耗费时间、车位被非法占用、停车管理效率低下等问题层出不穷。然而,车易泊相机的出现,为车位管理带来了全新的解决方案。 一、车易泊相机的强大功能 车易泊相机是一…...
C++初阶(七)--类和对象(4)
目录 编辑 一、再谈构造函数 1.构造函数体赋值 2.初始化列表 二、类型转换 1.隐式类型转换 2.explicit关键字 3.类类型之间的对象隐式转换 三、static成员函数 1.概念 2.特性 3.面试题: 四、友元函数 1.基本介绍 2.回顾: 3.友元类&am…...
Python 爬虫的寻宝大冒险:如何捕获 API 数据的宝藏
在这个信息爆炸的数字时代,数据就像是隐藏在网络深处的宝藏,等待着勇敢的探险家去发现。今天,我们要讲述的是如何成为一名 Python 爬虫探险家,装备你的代码工具,深入 API 的迷宫,捕获那些珍贵的数据宝藏。 …...
电力物联网环境下的售电研究
泛在电力物联网打破了传统能源网络的壁垒,形成了能源共享、信息互通、数据开放的能源物联网。泛在电力物联网环境下,可再生能源接入更为容易。更加开放的能源接人、更加丰富的信息获取以及更加智能的电力设备,促进了电力市场的进一步开放。 …...
Oracle视频基础1.1.4练习
1.1.4 dbb,ddabcPMON,SMON,LGWR,CKPT,DBWna5,b4,c2,d3,e1ad,a,c,b,eOracle instance,Oracle databaseSGA,background processcontrol file,data file,online redo file 以下是一篇关于 Oracle 基础习题 1.1.4 的博客: Oracle 基础习题解析:1.1.4 本篇文…...
【水下生物数据集】 水下生物识别 深度学习 目标检测 机器视觉 yolo(含数据集)
一、背景意义 随着全球海洋生态环境的日益变化,水下生物的监测和保护变得愈发重要。水下生物种类繁多,包括螃蟹、鱼类、水母、虾、小鱼和海星等,它们在海洋生态系统中扮演着关键角色。传统的水下生物监测方法通常依赖于人工观察,效…...
【宠物狗狗数据集】 犬类品种识别 宠物狗检测 深度学习 目标检测(含数据集)
一、背景意义 随着人们对宠物狗的喜爱日益增加,犬种的多样性也逐渐受到重视。狗狗不仅是家庭的好伴侣,更在多个领域中发挥着重要作用,如导盲、搜救、疗愈等。因此,准确识别和分类各种犬种显得尤为重要。传统的犬种识别方法往往依赖…...
C语言中的数组并非指针:深入理解数组和指针的区别
前言 在C语言中,数组和指针是两个非常重要的概念,它们在很多方面有着紧密的联系,但也存在显著的区别。尽管数组名有时可以像指针那样使用,但它们本质上并不是一回事。理解这些差异对于编写正确和高效的代码至关重要。本文将深入探…...
Topaz Video AI for Mac 视频无损放大软件安装教程【保姆级,操作简单轻松上手】
Mac分享吧 文章目录 Topaz Video AI for Mac 视频无损放大软件 安装完成,软件打开效果一、Topaz Video AI 视频无损放大软件 Mac电脑版——v5.3.5⚠️注意事项:1️⃣:下载软件2️⃣:安装软件,将安装包从左侧拖入右侧文…...
虚函数和纯虚函数是 C++ 中实现多态性的关键概念
虚函数(Virtual Function) 定义:虚函数是在基类中使用 virtual 关键字声明的函数,目的是允许派生类重写该函数。用途:通过虚函数,基类指针或引用可以调用派生类中重写的函数,从而实现动态多态性…...
计算机网络IP地址分类,子网掩码,子网划分复习资料
IP 地址的概念 IP 地址是独立于硬件地址的逻辑地址,它是由软件提供的地址。 IP 地址是网络层地址。 IP 编址方案和分类 IP 地址由 32 位二进制数构成,分为前缀(网络地址)和后缀(主机地址) 同一网段中每台计算机的 IP 地址是唯一的网络地址的分配全球…...
LINUX下使用SQLite查看.db数据库文件
目录 1. 安装 SQLite 对于 Debian/Ubuntu 系统: 2.安装完成后操作 打开 SQLite 命令行工具并连接到数据库文件 查看表结构 查询表中的数据 执行其他 SQL 操作 3. 退出 SQLite 命令行工具 4. 使用图形化工具(可选) 总结 在 Linux 环…...
基于uniapp微信小程序的校园二手书交易系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
性能测试中的操作系统参数优化
目录 以下是一些针对性能测试的操作系统优化建议 关闭不必要的服务和程序: 更新系统和驱动程序: 优化电源管理设置: 调整内存配置: 网络配置优化: 磁盘I/O优化: 内核参数调整: 安全软件…...
rabbitmq高级特性(2)TTL、死信/延迟队列、事务与消息分发
目录 1.TTL 1.1.设置消息过期时间 1.2.设置队列过期时间 2.死信队列 2.1.介绍 2.2.演示 3.延迟队列 3.1.模拟实现延迟队列 3.2.延迟队列插件 4.事务与消息分发 4.1.事务 4.2.消息分发 1.TTL 所谓的ttl,就是过期时间。对于rabbitmq,可以设置…...
了解一下,RN中怎么加载 threejs的
在React Native(RN)中加载和使用Three.js,一个流行的3D图形库,通常需要一些额外的步骤,因为Three.js主要是为Web浏览器设计的,而React Native则使用原生的渲染引擎。不过,有一些方法可以在React…...
笔记整理—linux驱动开发部分(1)驱动梗概
驱动可以分为广义上的和狭义上的驱动。广义上的驱动是用于操作硬件的代码,而狭义上的驱动为基于内核系统之上让硬件去被操作的逻辑方法。 linux体系架构: 1.分层思想 :在OS中间还会有许多层。 : 2.驱动的上面是系统调用(API&…...
重庆网站建设/天津百度推广公司
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。 解析:这道题和矩形覆盖有什么区别吗?反正我是用矩形覆盖的思路做的 https://blog.csdn.net/q…...
wordpress评论推广/太原优化排名推广
模板介绍 医疗保健服务宣传和医疗咨询服务PPT模板-优页文档。一套,疫情防护,幻灯片模板,内含青色,蓝色多种配色,风格设计,动态播放效果,精美实用。 希望下面这份精美的PPT模板能给你带来帮助,温馨提示:本…...
宜春网站建设哪家专业/义乌最好的电商培训学校
原文地址:http://www.tuicool.com/articles/MzeM7r 本博文少许理论资料来至DBA技术大牛 http://blog.csdn.net/tianlesoftware/article/details/4717318 ,本着实践式学习,书写以下博文: 一、什么是分区表 Oracle提供了分区技…...
上虞中国建设银行官网站/百度公司
作为一名工程师,一名做技术的工程师,NUMA也是我的近期工作重点之一。在工作时间,在茶余饭后,也看了些NUMA的资料,学习了英特尔下一代Xeon处理器。这里就是我的一点小结,一点心得,和感兴趣的朋友…...
投资理财网站建设规划书/重庆网站优化排名推广
P2341 [HAOI2006]受欢迎的牛 题目描述 每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶 牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜 欢B,B喜欢C,那么A也喜欢C。牛栏里…...
asp网站如何虚拟发布/广告公司收费价格表
转载:http://meigesir.iteye.com/blog/1856503 当我们原来系统中有ubuntu的时候,如果我们重装或安装新的windows系统时,会发现ubuntu系统启动菜单不见啦,我们重现安装ubuntu系统也可以解决这个问题,但是我们以前在ubun…...