news 2026/6/17 0:56:22

webrtc peerconnection_server 模块介绍

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
webrtc peerconnection_server 模块介绍

peerconnection_server是webrtc一个简单的信令服务器示例,它位于 src/examples/peerconnection/server/ 目录下。它的主要目的是配合 peerconnection_client(另一个示例客户端)使用,演示两个 WebRTC 对等端(Peer)如何通过一个中间服务器交换建立连接所需的元数据(SDP 和 ICE Candidates),从而完成 P2P 握手。

• 它仅用于学习和测试 WebRTC 的信令流程。
• 它不支持大规模并发、没有身份验证、没有加密(HTTPS/WSS)、功能非常有限。
• 在实际产品中,你需要使用专业的信令服务器(如基于 WebSocket 的 Node.js/Go/Python 服务,或使用 SIP 协议的服务)。

一、核心功能

peerconnection_server 的主要职责是消息中转。它不处理任何媒体数据(音频/视频),只处理文本信令。

1.1. 用户注册与发现:

• 客户端启动时连接到服务器并注册一个名字(例如 "Alice")。
• 服务器维护一个在线用户列表。
• 当新用户加入或旧用户离开时,服务器会通知所有其他在线用户更新他们的成员列表。

1.2. 消息路由:

• 当 Alice 想呼叫 Bob 时,她通过服务器发送一条消息给 Bob。
• 服务器根据 Bob 的名字或 ID,将消息转发给 Bob 的连接。

1.3. 支持的信令类型:

• SDP Offer/Answer: 用于协商媒体能力(编解码器、分辨率等)。
• ICE Candidates: 用于网络穿透,交换双方的网络地址信息。

二、实现架构

该服务器是一个单线程、非阻塞 I/O 的 HTTP 服务器。

2.1. 网络层:

• 使用原生 BSD Socket API(跨平台封装在 SocketBase, DataSocket, ListeningSocket 中)。
• 使用 select() (Linux/Mac) 或 WSAPoll (Windows)进行 I/O 多路复用,在一个线程中同时处理多个客户端连接。

2.2. 应用层协议:

• 基于 HTTP/1.1。
• 使用 长轮询(Long-Polling) 机制来实现服务器向客户端的“推送”。
• 客户端发送一个 /wait 请求。
• 服务器挂起该请求,直到有发给该客户端的消息到达,或者超时。
• 一旦有消息,服务器立即响应 HTTP 200 OK 并带上消息体。
• 客户端收到后立即发起下一个 /wait 请求。

2.3. 核心类:

• ChannelMember:

这个类代表一个已连接到信令服务器的特定用户/客户端。每个浏览器标签页或应用程序实例在服务器上都有一个对应的 ChannelMember 对象。


• PeerChannel :

这个类代表整个聊天室或信令房间。它管理所有当前连接的 ChannelMember。在示例代码中,通常只有一个全局的 PeerChannel 实例,所有用户都在同一个“房间”里。

// Represents a single peer connected to the server. class ChannelMember { public: explicit ChannelMember(DataSocket* socket); ~ChannelMember(); bool connected() const { return connected_; } int id() const { return id_; } void set_disconnected() { connected_ = false; } bool is_wait_request(DataSocket* ds) const; const std::string& name() const { return name_; } bool TimedOut(); std::string GetPeerIdHeader() const; bool NotifyOfOtherMember(const ChannelMember& other); // Returns a string in the form "name,id\n". std::string GetEntry() const; void ForwardRequestToPeer(DataSocket* ds, ChannelMember* peer); void OnClosing(DataSocket* ds); void QueueResponse(const std::string& status, const std::string& content_type, const std::string& extra_headers, const std::string& data); void SetWaitingSocket(DataSocket* ds); protected: struct QueuedResponse { std::string status, content_type, extra_headers, data; }; DataSocket* waiting_socket_; int id_; bool connected_; time_t timestamp_; std::string name_; std::queue<QueuedResponse> queue_; static int s_member_id_; }; // Manages all currently connected peers. class PeerChannel { public: typedef std::vector<ChannelMember*> Members; PeerChannel() {} ~PeerChannel() { DeleteAll(); } const Members& members() const { return members_; } // Returns true if the request should be treated as a new ChannelMember // request. Otherwise the request is not peerconnection related. static bool IsPeerConnection(const DataSocket* ds); // Finds a connected peer that's associated with the |ds| socket. ChannelMember* Lookup(DataSocket* ds) const; // Checks if the request has a "peer_id" parameter and if so, looks up the // peer for which the request is targeted at. ChannelMember* IsTargetedRequest(const DataSocket* ds) const; // Adds a new ChannelMember instance to the list of connected peers and // associates it with the socket. bool AddMember(DataSocket* ds); // Closes all connections and sends a "shutting down" message to all // connected peers. void CloseAll(); // Called when a socket was determined to be closing by the peer (or if the // connection went dead). void OnClosing(DataSocket* ds); void CheckForTimeout(); protected: void DeleteAll(); void BroadcastChangedState(const ChannelMember& member, Members* delivery_failures); void HandleDeliveryFailures(Members* failures); // Builds a simple list of "name,id\n" entries for each member. std::string BuildResponseForNewMember(const ChannelMember& member, std::string* content_type); protected: Members members_; };


• DataSocket:

这个类代表一个已建立的客户端连接。当 ListeningSocket 接受一个新连接后,会创建一个 DataSocket 实例来处理该连接上的 HTTP 请求和响应。

• ListeningSocket :

这个类代表服务器的监听端点。它负责等待新的传入连接.

三、通信流程示例

3.1 信令工作流程

1. 启动: PeerConnectionServer 创建一个 ListeningSocket 并调用 Listen(8888)。
2. 事件循环: 服务器进入主循环,使用 select() 监视 ListeningSocket 和所有活跃的 DataSocket。
3. 新连接:
• 如果 ListeningSocket 可读,调用 Accept()。
• 获取新的 DataSocket*,将其添加到活跃连接列表中。
4. 接收数据:
• 如果某个 DataSocket 可读,调用其 OnDataAvailable()。
• DataSocket 内部解析 HTTP。
• 如果 request_received() 变为 true,主循环检测到该 socket 就绪且请求完整。
5. 业务处理:
• 服务器读取 DataSocket->request_path() 和 DataSocket->data()。
• 根据路径(如 /message)将数据转发给目标用户的 DataSocket。
• 调用 DataSocket->Send("200 OK", ...) 回复当前客户端。
6. 关闭:
• 如果 OnDataAvailable 返回错误,或业务逻辑决定关闭,从列表中移除该 DataSocket,其析构函数会关闭底层 socket。

3.2 用户A发生SDP给用户B

1. 用户 B 等待:
• 用户 B 的客户端发送 HTTP GET /wait。
• 服务器找到 B 的 ChannelMember,调用 SetWaitingSocket(socket_B)。
• Socket B 保持打开,不返回数据,进入挂起状态。
2. 用户 A 发送消息:
• 用户 A 的客户端发送 HTTP POST /message?peer_id=B_ID,Body 包含 SDP Offer。
• 服务器主循环收到请求,通过 PeerChannel::Lookup(socket_A) 找到用户 A。
• 通过 PeerChannel::IsTargetedRequest 解析出目标 ID 是 B。
• 找到用户 B 的 ChannelMember 对象。
3. 转发:
• 服务器调用 member_B->ForwardRequestToPeer(socket_A, member_B) (实际逻辑在 PeerChannel 中协调)。
• 在 ChannelMember::ForwardRequestToPeer 内部,它会将 SDP 数据包装。
• 检查 member_B->waiting_socket_。发现不为空(即步骤 1 中的 Socket B)。
• 立即通过 Socket B 发送 HTTP 200 OK + SDP 数据。
• 清空 member_B->waiting_socket_。
4. 用户 B 接收:
• 用户 B 的客户端收到 HTTP 响应,解析出 SDP。
• 用户 B 的 WebRTC 引擎处理 SDP,生成 Answer。
• 用户 B 再次发起 /wait 或发送 Answer,循环继续。

3.3 视频会话流程

1. 连接与注册:
• Alice 连接服务器: POST /sign_in?name=Alice
• Bob 连接服务器: POST /sign_in?name=Bob
• 服务器回复 Alice: 200 OK, body 包含当前在线用户列表(包含 Bob)。
• 服务器回复 Bob: 200 OK, body 包含当前在线用户列表(包含 Alice)。
• 此时,Alice 和 Bob 都知道对方在线了。
2. 发起呼叫 (Alice -> Bob):
• Alice 的 WebRTC 引擎生成 SDP Offer。
• Alice 客户端发送: POST /message?peer_id=Bob_ID, Body 为 SDP Offer JSON。
• 服务器收到请求,查找 Bob 的连接。
• 如果 Bob 正处于 /wait 挂起状态,服务器立即通过该连接返回 SDP Offer。
• 如果 Bob 没在等待,服务器将消息存入 Bob 的消息队列,待 Bob 下次 /wait 时发送。
3. 接收与回应 (Bob -> Alice):
• Bob 收到 SDP Offer,WebRTC 引擎生成 SDP Answer。
• Bob 客户端发送: POST /message?peer_id=Alice_ID, Body 为 SDP Answer。
• 服务器转发给 Alice。
4. 交换 ICE Candidates:
• 双方不断收集本地网络候选者(Host, Srflx, Relay)。
• 每收集到一个,就通过 POST /message 发送给对方。
• 服务器持续中转这些 JSON 消息。
5. P2P 建立:
• 一旦 SDP 和足够的 ICE Candidates 交换完毕,且连通性检查成功,Alice 和 Bob 之间建立起直接的 UDP 连接。
• 此后,所有的音频、视频和数据通道流量都直接在对等端之间传输,不再经过 peerconnection_server。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 0:56:12

CTF竞赛全流程解析:从平台搭建到题目设计的系统工程实践

1. 项目概述&#xff1a;从“2026dasctf夏季赛”看一场CTF竞赛的完整生命周期最近和圈子里的朋友聊起CTF&#xff08;Capture The Flag&#xff0c;夺旗赛&#xff09;&#xff0c;不少人觉得它神秘又高深&#xff0c;仿佛只是顶尖安全高手们的游戏。恰好&#xff0c;一个名为“…

作者头像 李华
网站建设 2026/6/17 0:55:33

强力解锁macOS下载通道:gibMacOS跨平台解决方案深度解析

强力解锁macOS下载通道&#xff1a;gibMacOS跨平台解决方案深度解析 【免费下载链接】gibMacOS Py2/py3 script that can download macOS components direct from Apple 项目地址: https://gitcode.com/gh_mirrors/gi/gibMacOS 在macOS系统维护和部署过程中&#xff0c;…

作者头像 李华
网站建设 2026/6/17 0:47:43

QorIQ处理器硬件配置与内存映射实战指南:从DIP开关到系统启动

1. 项目概述与核心价值在嵌入式系统开发领域&#xff0c;尤其是基于飞思卡尔&#xff08;现恩智浦&#xff09;QorIQ系列处理器的项目中&#xff0c;硬件配置是决定项目成败的第一步。很多工程师拿到一块全新的开发板&#xff0c;烧录了官方SDK后&#xff0c;发现系统无法启动&…

作者头像 李华
网站建设 2026/6/17 0:43:38

PCIe DMA性能测试与Linux大页内存优化实战指南

1. 项目概述与核心价值在嵌入式系统和数据中心服务器开发中&#xff0c;我们经常需要评估和优化外设与主机之间的数据传输性能。PCIe DMA&#xff08;直接内存访问&#xff09;技术是实现这一目标的核心手段&#xff0c;它允许PCIe设备绕过CPU&#xff0c;直接与系统内存进行高…

作者头像 李华
网站建设 2026/6/17 0:35:51

单科英语很差,会影响大学大数据专业学习吗

英语差不阻碍大数据基础学习 很多大数据专业新生会陷入误区&#xff0c;认为英语弱就学不会大数据&#xff0c;其实二者基础适配性很低。大学大数据大一、大二核心课程以数学基础、数据库原理、Excel数据分析、基础爬虫为主&#xff0c;这些课程的教材、课件和实操案例大多有完…

作者头像 李华
网站建设 2026/6/17 0:31:54

决战申论100题2026|最新|范文

决战申论100题2026|最新|范文资料全科都有决战申论100题2026 PDFhttps://tool.nineya.com/s/1jr3ck8t3 【数学真题】1. 某市2023年常住人口为500万人&#xff0c;2024年常住人口为520万人&#xff0c;则人口增长率为&#xff08; &#xff09; A. 4% B. 2% C. 5% D. 20% 答案&a…

作者头像 李华