深入浅出mediasoup—WebRtcTransport

mediasoup 提供了多种 transport,包括 WebRtcTransport、PipeTransport、DirectTransport、PlainTransport 等,用来实现不同目的和场景的媒体通信。WebRtcTransport 是 mediasoup 实现与 WebRTC 客户端进行媒体通信的对象,是 mediasoup 最重要也是最复杂的 transport,理解了 WebRtcTransport 的设计与实现,再去分析其他类型 transport 会简单很多。

1. 静态结构

WebRtcTransport 可以创建独立通信端口,也可以共享 WebRtcServer 上的通信端口,从安全和运维复杂度来讲,大部分场景都应该选择第二种方式,本文主要分析第二种实现方式。

1.1. WebRtcTransport

1.1.1. 接口继承

1)Transport

这是所有 transport 的基类,代表与具体协议无关的通信端口,它封装了通信端口参与上层数据交换的公共能力,比如如何与 router、producer、consumer 的关联与交互等。

2)UdpSocket::Listener

WebRtcTransport 建立独立通信端口时,接收 UDP 数据。

class Listener
{
public:virtual void OnUdpSocketPacketReceived(RTC::UdpSocket* socket, const uint8_t* data, size_t len, const struct sockaddr* remoteAddr) = 0;
};

3)TcpServer::Listener

WebRtcTransport 建立独立通信端口时,处理 TCP 连接关闭。

class Listener
{
public:virtual void OnRtcTcpConnectionClosed(RTC::TcpServer* tcpServer, RTC::TcpConnection* connection) = 0;
};

4)TcpConnection::Listener

WebRtcTransport 建立独立通信端口时,接收 TCP 数据。

class Listener
{
public:virtual void OnTcpConnectionPacketReceived(RTC::TcpConnection* connection, const uint8_t* data, size_t len) = 0;
};

5)IceServer::Listener

ICE 交互相关回调,具体见注释。

class Listener
{
public:// 通知发送 stun 报文virtual void OnIceServerSendStunPacket(const RTC::IceServer* iceServer, const RTC::StunPacket* packet, RTC::TransportTuple* tuple) = 0;// 通知添加/删除 ufrag,主要用于 WebRtcServer stun 报文路由virtual void OnIceServerLocalUsernameFragmentAdded(const RTC::IceServer* iceServer, const std::string& usernameFragment) = 0;virtual void OnIceServerLocalUsernameFragmentRemoved(const RTC::IceServer* iceServer, const std::string& usernameFragment) = 0;// 通知添加/删除 tuple,用于 WebRtcServer 非 stun 报文路由virtual void OnIceServerTupleAdded(const RTC::IceServer* iceServer, RTC::TransportTuple* tuple) = 0;virtual void OnIceServerTupleRemoved(const RTC::IceServer* iceServer, RTC::TransportTuple* tuple) = 0;virtual void OnIceServerSelectedTuple(const RTC::IceServer* iceServer, RTC::TransportTuple* tuple)        = 0;// WebRTC 客户端与服务器通信端口连接状态变化通知virtual void OnIceServerConnected(const RTC::IceServer* iceServer)    = 0;virtual void OnIceServerCompleted(const RTC::IceServer* iceServer)    = 0;virtual void OnIceServerDisconnected(const RTC::IceServer* iceServer) = 0;
};

6)DtlsTransport::Listener

DTLS 相关回调,详见注释。

class Listener
{
public:// 接收方向协商成功virtual void OnDtlsTransportConnecting(const RTC::DtlsTransport* dtlsTransport) = 0;// 双向协商成功virtual void OnDtlsTransportConnected(const RTC::DtlsTransport* dtlsTransport,RTC::SrtpSession::CryptoSuite srtpCryptoSuite,uint8_t* srtpLocalKey,size_t srtpLocalKeyLen,uint8_t* srtpRemoteKey,size_t srtpRemoteKeyLen,std::string& remoteCert) = 0;// 连接失败或异常中断virtual void OnDtlsTransportFailed(const RTC::DtlsTransport* dtlsTransport) = 0;// 对方关闭 DTLS 连接(close_notify alert)virtual void OnDtlsTransportClosed(const RTC::DtlsTransport* dtlsTransport) = 0;// 向对端发送 DTLS 数据(DTLS协议交互)virtual void OnDtlsTransportSendData(const RTC::DtlsTransport* dtlsTransport, const uint8_t* data, size_t len) = 0;// 收到 DTLS 应用层数据virtual void OnDtlsTransportApplicationDataReceived(const RTC::DtlsTransport* dtlsTransport, const uint8_t* data, size_t len) = 0;
};

1.1.2. 重要属性

1)webRtcTransportListener

主要用于 WebRtcTransport 通知 WebRtcServer 相关信息用来建立数据路由。

class WebRtcTransportListener
{
public:virtual void OnWebRtcTransportCreated(RTC::WebRtcTransport* webRtcTransport) = 0;virtual void OnWebRtcTransportClosed(RTC::WebRtcTransport* webRtcTransport)  = 0;// ICE 协议交互阶段需要使用 ufrag 来建立路由virtual void OnWebRtcTransportLocalIceUsernameFragmentAdded(RTC::WebRtcTransport* webRtcTransport, const std::string& usernameFragment) = 0;virtual void OnWebRtcTransportLocalIceUsernameFragmentRemoved(RTC::WebRtcTransport* webRtcTransport, const std::string& usernameFragment) = 0;// ICE 协议交互完成后需要使用 TransportTuple 来建立路由virtual void OnWebRtcTransportTransportTupleAdded(RTC::WebRtcTransport* webRtcTransport, RTC::TransportTuple* tuple) = 0;virtual void OnWebRtcTransportTransportTupleRemoved(RTC::WebRtcTransport* webRtcTransport, RTC::TransportTuple* tuple) = 0;
};

2)iceServer

处理 ICE 协议交互,确定用于通信的 TransportTuple。

3)udpSockets

WebRtcTransport 建立独立通信端口的 UDP socket。

4)tcpServers

WebRtcTransport 建立独立通信端口的 TCP 监听器。

5)dtlsTransport

处理 DTLS 协商,协商得到 SRTP 所需的加解密参数。

6)srtpRecvSession

用于接收方向的 SRTP 解密。

7)srtpSendSession

用于发送方向的 SRTP 加密。

1.2. IceServer

1.2.1. 重要属性

1)usernameFragment 和 password

本端生成的随机值,对应 SDP 中的 ice-ufrag 和 ice-pwd

2)oldUsernameFragment 和 oldPassword

用来协助处理 ICE 重协商。

2)consentTimeoutMs

客户端需要定时发送 Bind 请求进行保活,这是相关超时设置。

4)state

ICE 协商状态,具体见下面的状态机分析。

enum class IceState
{NEW = 1,CONNECTED,COMPLETED,DISCONNECTED,
};

5)remoteNomination

正常情况是使用 USE-CANDIDATE 属性来选择用来通信的 candidate,当没有 USE-CANDIDATE 属性时,但携带了 NOMINATION 属性,则可以使用 nomination 来选择通信 candidate,值越大地址优先级越高。

6)tuples

保存接收到客户端所有 BIND 请求的地址。

7)selectedTuple

最终选择使用的通信地址对。

1.2.2. 重要方法

1)ProcessStunPacket

WebRtcTransport 调用,用来处理 BIND 请求。

2)RestartIce

当客户端发起 ICE 重协商时调用此接口,此时会重新生成 ufrag 和 pwd。

1.3. DtlsTransport

DtlsTransport 用来处理 DTLS 协议交互,完成身份验证和密钥协商。一旦 DTLS 完成握手并协商好密钥,后续RTP报文就不再通过 DTLS 处理,而是通过 SRTP 进行加密和解密。

1.3.1. 重要属性

1)listener

用来通知 DTLS 连接状态,DTLS 协议报文需要经过 WebRtcTransport 发送,另外, SCTP报文会被封装在DTLS报文中进行传输,通过 OnDtlsTransportApplicationDataReceived 回调收到的 SCTP 数据。

2)ssl

处于 SSL 协议交互的 OpenSSL 对象。

3)state

DTLS 连接状态,具体见 DTLS 状态机分析。

enum class DtlsState
{NEW = 1,CONNECTING,CONNECTED,FAILED,CLOSED
};

4)localRole

DTLS 角色主要用于确定DTLS握手过程中的通信方向和权限,通常是 CLIENT 发起握手。

enum class Role
{AUTO = 1,CLIENT,SERVER
};

mediasoup 根据客户端 DTLS 角色来设置本端 DTLS 角色(调用 connectWebRtcTransport 传递),并返回协商结果给客户端。

// Set local DTLS role.
switch (dtlsRemoteRole)
{case RTC::DtlsTransport::Role::CLIENT:{this->dtlsRole = RTC::DtlsTransport::Role::SERVER;break;}// If the peer has role "auto" we become "client" since we are ICE controlled.case RTC::DtlsTransport::Role::SERVER:case RTC::DtlsTransport::Role::AUTO:{this->dtlsRole = RTC::DtlsTransport::Role::CLIENT;break;}
}

5)remoteFingerprint

保存客户端的证书指纹,用来验证 DTLS 协商时对端证书的合法性。

6)remoteCert

保存客户端的证书。

1.3.2. 重要方法

1)ProcessDtlsData

WebRtcTransport 调用此接口传入 DTLS 报文。

2)SetRemoteFingerprint

在建立 DTLS 连接之前,服务器需要调用此接口设置客户端的证书指纹,否则无法对客户端证书进行验证。

3)SendDtlsData

OpenSSL 回调需要发送协商报文,内部会调用回调 WebRtcTransport 进行发送。

4)SendApplicationData

发送 SCTP 数据。

2. 数据流

WebRtcTransport 在进行媒体通信前要经历两个阶段,第一个阶段是 ICE 协议交互,用来确定进行通信的地址对,第二个阶段是 DTLS 协议交互,用来实现身份认证和密钥协商。这两个阶段完成后才开始进行 RTP/RTCP 传输阶段,并使用 DTLS 阶段协商的密钥对淑军进行加解密。

2.1. STUN

WebRtcServer 收到 UDP 数据时,会解析报文特征,识别报文类型,然后调用 ProcessStunPacketFromWebRtcServer 处理 STUN 报文,WebRtcTransport 将 STUN 报文丢给 IceServer 处理。IceServer 处理完后,如果需要发送响应的则会回调 WebRtcTransport,WebRtcTransport 调用 TransportTuple 将报文发送出去。

但这里有个问题,WebRtcServer 如何知道 STUN 报文属于哪个 WebRtcTransport?方法是,WebRtcTransport 在创建 IceServer时,会将 ice-ufrag 与 WebRtcTransport 关联起来。WebRtcServer 通过解析 STUN 报文的 ufrag 字段找到所属 WebRtcTransport。

同时,WebRtcTransport 还会将 ICE 交互过程中所有地址对设置到 WebRtcServer,这样后续从这些地址发送过来的报文都送到关联的 WebRtcTransport 处理,不用再依赖 ufrag 字段。当然,非 STUN 报文也没有这个字段可用。

2.2. DTLS

DTLS 的数据流与 STUN 数据流类似,只是在 WebRtcTransport 会对非 STUN 数据进行解析,如果是 DTLS 协议报文会调用 DtlsTransport::ProcessDtlsData 进行处理。如果需要发送 DTLS 协议报文,也是回调 WebRtcTransport 发送。

2.3. RTP

RTP 数据流相对复杂一些,涉及到转发层的路由,如下图所示。

1)WebRtcTransport 将收到的 RTP 报文交给 Transport 处理。

2)Transport 通过报文的 SSRC 找到对应的 Producer,然后将报文交给 Producer 处理。

【注】在 Worker 上创建 Producer 时,需要传入 RtpParameters,里面包含了 SSRC、MID、RID 等信息。Worker 会将这些信息与 Producer 关联起来,当收到报文时可以基于这些信息找到对应的 Producer。

3)Producer 需要做拥塞控制、NACK、关键帧请求等相关处理。处理完后,会回调 Transport 进行后续的报文转发。

4)Transport 直接将报文转发给 Router 处理。

【注】Transport 是在 Router 上创建的,Transport 只能属于某个 Router。

5)Router 将报文转发给所有连接到 Producer 的 Consumer。

6)Consumer 也需要做拥塞控制,NACK、关键帧请求等相关处理。处理完后,会回调 Consumer 所在 Transport,将报文发送出去。

3. 补充分析

3.1. 证书指纹

证书指纹(fingerprint)用来验证对等端的合法性,有效防止中间人攻击。证书中不携带证书指纹,需要通过其他方式来传递和交换。WebRTC 在 SDP 中携带证书指纹,如下所示。fingergpinrt 分为两段,第一段为摘要算法类型,第二段为使用此摘要算法计算的证书摘要值。

a=fingerprint:sha-512 28:8D:69:62:88:27:68:0B:41:FB:BE:28:DE:63:F0:2D:7C:AA:38:72:57:58:37:D4:BD:B9:BE:01:9D:A1:AF:86:1D:BB:9F:36:76:04:A8:0D:24:80:5C:08:D7:70:0D:BA:54:06:CC:48:27:52:DE:00:CD:72:B3:1A:E6:15:F1:7D

mediasoup 的证书指纹通过 connectWebRtcTransport 流程传递到 Worker,如下图所示:

证书指纹的计算方式可以简单的描述为:基于 X509 规范对证书内容使用指定的哈希算法计算摘要。

ret = X509_digest(certificate, hashFunction, binaryFingerprint, &size);

在 DTLS 握手过程中,Worker 会验证对等端的证书与设置的指纹是否匹配, 如果匹配,则验证通过,握手继续进行;如果不匹配,则握手失败,连接终止。

3.2. 密钥协商

mediasoup 预置支持的加密套件如下所示:

std::vector<DtlsTransport::SrtpCryptoSuiteMapEntry> DtlsTransport::srtpCryptoSuites =
{{ RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM,        "SRTP_AEAD_AES_256_GCM"  },{ RTC::SrtpSession::CryptoSuite::AEAD_AES_128_GCM,        "SRTP_AEAD_AES_128_GCM"  },{ RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_80, "SRTP_AES128_CM_SHA1_80" },{ RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_32, "SRTP_AES128_CM_SHA1_32" }
};

通过下面代码设置将支持的 SRTP 的加密套件设置到 SSL。

// 设置SRTP加密套件
ret = SSL_CTX_set_tlsext_use_srtp(DtlsTransport::sslCtx, dtlsSrtpCryptoSuites.c_str());

最终协商结果是两套密钥(盐),客户端和服务器使用独立的密钥进行加密。

// Create the SRTP local master key.
std::memcpy(srtpLocalMasterKey, srtpLocalKey, srtpKeyLength);
std::memcpy(srtpLocalMasterKey + srtpKeyLength, srtpLocalSalt, srtpSaltLength);// Create the SRTP remote master key.
std::memcpy(srtpRemoteMasterKey, srtpRemoteKey, srtpKeyLength);
std::memcpy(srtpRemoteMasterKey + srtpKeyLength, srtpRemoteSalt, srtpSaltLength);

3.3. 重启 ICE

如果网络条件发生变化(例如,网络断开或切换),则可能需要重新启动ICE进程,以便重新发现和建立有效的网络连接。如下图所示,如果在 DEMO 上点击所示图标,会触发 ICE 重启。

客户端先调用服务器接口,获取服务器更新后的 ICE 参数,使用新的 ICE 参数更新 remote SDP,再进行 PeerConnection 的 SDP 重协商。

async restartIce(iceParameters: IceParameters): Promise<void> {this.assertNotClosed();// 更新 ICE 参数(ufrag/pwd)this._remoteSdp!.updateIceParameters(iceParameters);if (!this._transportReady) {return;}// SDP 重协商if (this._direction === 'send') {const offer = await this._pc.createOffer({ iceRestart: true });await this._pc.setLocalDescription(offer);const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() };await this._pc.setRemoteDescription(answer);} else {const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() };await this._pc.setRemoteDescription(offer);const answer = await this._pc.createAnswer();await this._pc.setLocalDescription(answer);}
}

3.4. ICE 状态机

USE_CANDIDATE 属性用于指示特定的候选对(candidate pair)被选为用于传输。mediasoup 收到 Binding 请求如果携带 USE_CANDIDATE 属性则进入 COMPLETED 状态,否则进入 CONNECTED 状态。客户端要定时向服务器发送 binding 请求来保活,如果超时,则进入 DISCONNECTED 状态。

3.5. DTLS 状态机

DTLS 状态比较简单,在 CONNECTING 状态,如果证书验证失败、加密套件协商失败或者协商超时,都会进入 FAILED 状态。在 CONNECTED 状态如果收到关闭请求,则进入到 CLOSED 状态。DTLS 连接成功后,好像没有保活处理,看起来像是依赖于 ICE 的保活。

4. 总结

本文重点分析了 WebRtcTransport 的静态结构及重要数据流,对于理解 mediasoup 媒体转发框架非常重要。建立 WebRtcTransport 需要经历 ICE 协商和 DTLS 协商两个阶段,本文只对其中几个比较重要的逻辑进行了分析,不涉及 ICE 协商和 DTLS 协商的协议细节,如有需要请参考其他文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/48694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何学习Airflow:糙快猛的大数据之路(附思维导图)

什么是Airflow? 在开始之前,让我们先简单了解一下Airflow是什么。Apache Airflow是一个开源的工作流管理平台。它允许你以代码的方式定义、调度和监控复杂的数据处理管道。 想象一下,你有一系列需要按特定顺序执行的任务,而且这些任务之间还有依赖关系,Airflow就是为解决这…

SpringBoot自动配置(面试重点)

自动配置是指&#xff1a; 自动配置是指在应用程序启动时&#xff0c;SpringBoot根据classpath路径下的jar包自动配置应用程序所需的一系列bean和组件&#xff0c;从而减少开发者的配置工作&#xff0c;提高开发效率。 一&#xff1a;Condition Condition是spring4.0之后添加…

linux离线安装mysql8(单机版)

文章目录 一、检查服务器是否有残留mysql资源&#xff0c;有的话就全删除1.1、查询mysql已安装的相关依赖&#xff1a;1.2、查找含有MySQL的目录 二、安装2.1、上传mysql安装包到文件夹下并解压2.2、移动及重命名2.3、mysql用户2.4、配置mysql所需的my.cnf文件2.5、给my.cnf配置…

JVM之经典垃圾回收器

1.垃圾回收器的分类 处理范围划分&#xff1a; 新生代垃圾回收器&#xff1a;serial、parNew、parallel scavenge&#xff1b; 老年代垃圾回收器&#xff1a;serial Old、parallel Old、CMS&#xff1b; 整堆收集器&#xff1a;G1、ZGC&#xff1b; 2.Serial GC Serial是单…

java单元测试:Mockito常用技巧

Mockito是Java中最流行的Mock框架之一&#xff0c;主要用于创建和配置模拟对象&#xff08;Mock&#xff09;&#xff0c;以测试代码的行为。Mockito使得单元测试更加简单和可控&#xff0c;特别是在需要隔离外部依赖的情况下。 1. Mockito简介 1.1 什么是Mockito Mockito是一个…

QGC二次开发入门教程(一):课程大纲

文章目录 前言一、课程大纲二、修改软件名称三、修改软件图标四、官方QGC中文版BUG修复五、汉化六、修改商标七、添加信号-槽八、添加QML和C交互九、MAVLINK的解析与发送十、换地图十一、添加自定义mavlink消息十二、在主工具栏添加一个自定义图标十三、解析自定义mavlink数据并…

SVN文件夹没有图标(绿钩子和红感叹号)

3分钟教会你解决SVN文件夹没有绿勾和红色感叹号的问题_svn文件被改动过不显示红色-CSDN博客https://blog.csdn.net/weixin_43382915/article/details/124251563 关于SVN状态图标不显示的解决办法(史上最全) - 简书 (jianshu.com)https://www.jianshu.com/p/92e8e1f345c0

人工智能与机器学习原理精解【6】

文章目录 数值优化基础理论凹凸性定义在国外与国内存在不同国内定义国外定义总结示例与说明注意事项 国内凹凸性二阶定义的例子凹函数例子凸函数例子 凸函数&#xff08;convex function&#xff09;的开口方向凸函数的二阶导数凸函数的二阶定义单变量函数的二阶定义多变量函数…

基于springboot+vue+uniapp的网上花店小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

FreeSWITCH 1.10.10 简单图形化界面26-在网页上播放SIP设备视频

​ FreeSWITCH 1.10.10 简单图形化界面26-在网页上播放SIP设备视频 1、前言2、大概流程3、测试环境4、安装流媒体服务器5、设置流媒体服务器接口6、简单写个web接口7、测试一下1、web播放在线播放器1在线播放器2本地video控件 2、vlc播放vlc播放rtmpvlc播放rtsp 8、总结 1、前…

https改造-python https 改造

文章目录 前言https改造-python https 改造1.1. https 配置信任库2. 客户端带证书https发送,、服务端关闭主机、ip验证 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每…

【项目日记(三)】梦幻笔耕-博客模块

❣博主主页: 33的博客❣ ▶️文章专栏分类:项目日记◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多项目内容 目录 1.前言2.获取博客列表3.获取博客详情4.新增博客5.更新博客6.删除博客7.拦截器9.统一处…

日常开发记录分享-SQL中的partition分区功能使用

文章目录 需求来源实现思路实施SQL 语句结构内部查询&#xff08;子查询&#xff09;外部查询 结果 partition的升级使用解释 验证一下看看分区 分区的一些操作1. 普通查询2. 分区表上的查询优化3. 插入数据4. 删除分区中的数据5. 分区维护操作添加新的分区删除分区重组分区 6.…

银河麒麟服务器V10 SP3 安装人大金仓V009R001C001B0030

原文链接&#xff1a;银河麒麟服务器V10 SP3 安装人大金仓V009R001C001B0030 Hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇关于在银河麒麟服务器V10 SP3上安装人大金仓V009R001C001B0030的文章。人大金仓是国内知名的数据库管理系统&#xff0c;它在高性能、高可靠…

【C++】标准库:介绍string类

string 一.string类介绍二.string类的静态成员变量三.string类的常用接口1.构造函数&#xff08;constructor&#xff09;2.析构函数&#xff08;destructor&#xff09;3.运算符重载&#xff08;operator&#xff09;1.operator2.operator[]3.operator4.operator 4.string的四…

算法从零到精通 (一) ~ 快慢双指针

1. 前言 快慢双指针是一种常用的算法技巧&#xff0c;通常用于解决涉及链表或数组的问题。它的基本思想是使用两个指针&#xff0c;一个移动速度快&#xff08;快指针&#xff09;&#xff0c;一个移动速度慢&#xff08;慢指针&#xff09;&#xff0c;来解决特定的问题。这两…

Docker搭建群晖

Docker搭建群晖 本博客介绍在docker下搭建群晖 1.编辑docker-compose.yml文件 version: "3" services:dsm:container_name: dsmimage: vdsm/virtual-dsm:latestenvironment:DISK_SIZE: "16G"cap_add:- NET_ADMIN ports:- 8080:50…

c# 端口监控 Helper 以及写一个端口监控工具

c# 端口监控 Helper 以及写一个端口监控工具 介绍核心代码&#xff1a;工具完整编码&#xff1a;1、编写界面2、打开定时控件的属性设置。3、编写定时控件的 Tick 事件结果&#xff08;运行效果&#xff09; 介绍 由于最近做上架比较多&#xff0c;会经常来确保服务器的服务&a…

Flink时间和窗口

目录 时间语义 水位线&#xff08;Watermarks&#xff09; 并行流中的水位线 窗口 滚动窗口—Tumbling Windows 滑动窗口—Sliding Windows 会话窗口—Session Windows 全局窗口—Global Windows 例子 时间语义 如图所示&#xff0c;由事件生成器&#xff08;Event Pr…

萤石举办2024夏季新品发布会,全力推进“2+5+N”智能家居新生态

7月24日&#xff0c;“智动新生&#xff0c;尽在掌控”2024萤石夏季新品发布会在杭州成功举办。本次发布会上&#xff0c;“智慧生活守护者”萤石深入挖掘应用场景&#xff0c;重磅发布了包括智能健康手表、智能家居AI主机、生态控制器、智家APP等多款创新性的产品及应用&#…