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::queueQueuedResponse queue_; static int s_member_id_; }; // Manages all currently connected peers. class PeerChannel { public: typedef std::vectorChannelMember* 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 thats 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给用户B1. 用户 B 等待:• 用户 B 的客户端发送 HTTP GET /wait。• 服务器找到 B 的 ChannelMember调用 SetWaitingSocket(socket_B)。• Socket B 保持打开不返回数据进入挂起状态。2. 用户 A 发送消息:• 用户 A 的客户端发送 HTTP POST /message?peer_idB_IDBody 包含 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?nameAlice• Bob 连接服务器: POST /sign_in?nameBob• 服务器回复 Alice: 200 OK body 包含当前在线用户列表包含 Bob。• 服务器回复 Bob: 200 OK body 包含当前在线用户列表包含 Alice。• 此时Alice 和 Bob 都知道对方在线了。2. 发起呼叫 (Alice - Bob):• Alice 的 WebRTC 引擎生成 SDP Offer。• Alice 客户端发送: POST /message?peer_idBob_ID Body 为 SDP Offer JSON。• 服务器收到请求查找 Bob 的连接。• 如果 Bob 正处于 /wait 挂起状态服务器立即通过该连接返回 SDP Offer。• 如果 Bob 没在等待服务器将消息存入 Bob 的消息队列待 Bob 下次 /wait 时发送。3. 接收与回应 (Bob - Alice):• Bob 收到 SDP OfferWebRTC 引擎生成 SDP Answer。• Bob 客户端发送: POST /message?peer_idAlice_ID Body 为 SDP Answer。• 服务器转发给 Alice。4. 交换 ICE Candidates:• 双方不断收集本地网络候选者Host, Srflx, Relay。• 每收集到一个就通过 POST /message 发送给对方。• 服务器持续中转这些 JSON 消息。5. P2P 建立:• 一旦 SDP 和足够的 ICE Candidates 交换完毕且连通性检查成功Alice 和 Bob 之间建立起直接的 UDP 连接。• 此后所有的音频、视频和数据通道流量都直接在对等端之间传输不再经过 peerconnection_server。