探索基于pion开发的WebRTC应用的建连过程

0f8266994b25c5f26d105071786ca5ad.jpeg请点击上方蓝字TonyBai订阅公众号!

099912a561000a91fefcef5f99a1b7bd.png

在《WebRTC第一课:从信令、ICE到NAT穿透的连接建立全流程》一文中,我们从理论层面全面细致地了解了WebRTC连接建立的完整流程。这个流程大致可以分为以下几个阶段:

  • 与信令服务器的交互

  • ICE候选项的采集、交换与排序

  • 形成ICE候选检查表、进行连通性检查,并最终确定最优候选路径

这个过程的复杂性不言而喻。即便多次阅读全文,读者可能仍难以形成深入的理解。因此,如果能够配上一个真实的示例,相信会更有助于读者全面把握这一过程的细节和原理。

在这篇文章中,我就为大家呈现一个真实的示例,我将使用Go语言开源WebRTC项目pion/webrtc[1]来实现一个基于datachannel的WebRTC演示版程序,通过将pion/webrtc的日志级别设置为TRACE级,输出更多pion/webrtc实现层面的日志,以帮助大家理解WebRTC建连过程。同时,我还会实现一个简易版的基于“Room抽象模型”的信令服务器,供WebRTC通信两端交换信息使用。希望该示例能帮助大家更好的理解WebRTC端到端的建连流程。

按照WebRTC建连的流程,我们先来实现一个简易版的信令服务器。

注:提醒各位读者,本文中所有例子均以演示和帮助大家理解为目的,不建议在生产中使用示例中的代码。

1. 信令服务器(signaling-server)

下面是一个基于WebSocket的WebRTC信令服务器的简化实现,使用WebSocket进行WebRTC信令交换可以提供更快速、更高效和更灵活的通信体验,同时WebSocket生态丰富,可复用的代码库有很多,实现起来也比较简单。

这个信令服务器是基于Room抽象模型的,因此其主要结构是一个Room结构体,代表一个聊天室。我们具体看一下该信令服务器的实现代码:

// webrtc-first-lesson/part2/signaling-server/main.go

package main

import (
 "encoding/json"
 "fmt"
 "log"
 "net/http"
 "sync"

 "github.com/gorilla/websocket"
)

type Room struct {
 Clients map[*websocket.Conn]bool
 mu      sync.Mutex
}

var (
 upgrader = websocket.Upgrader{
  CheckOrigin: func(r *http.Request) bool {
   return true
  },
 }
 rooms  = make(map[string]*Room)
 roomMu sync.Mutex
)

func main() {
 http.HandleFunc("/ws", handleWebSocket)
 log.Println("Signaling server starting on :28080")
 log.Fatal(http.ListenAndServe(":28080", nil))
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
 conn, err := upgrader.Upgrade(w, r, nil)
 if err != nil {
  log.Println("Error upgrading to WebSocket:", err)
  return
 }
 defer conn.Close()

 remoteAddr := conn.RemoteAddr().String()
 log.Println("New WebSocket connection from:", remoteAddr)

 roomID := r.URL.Query().Get("room")
 if roomID == "" {
  roomID = fmt.Sprintf("room_%d", len(rooms)+1)
  log.Printf("Created new room: %s\n", roomID)
 }

 roomMu.Lock()
 room, exists := rooms[roomID]
 if !exists {
  room = &Room{Clients: make(map[*websocket.Conn]bool)}
  rooms[roomID] = room
 }
 roomMu.Unlock()

 room.mu.Lock()
 room.Clients[conn] = true
 room.mu.Unlock()

 log.Printf("Client[%v] joined room %s\n", remoteAddr, roomID)

 for {
  messageType, message, err := conn.ReadMessage()
  if err != nil {
   log.Println("Error reading message:", err)
   break
  }

  var msg map[string]interface{}
  if err := json.Unmarshal(message, &msg); err != nil {
   log.Println("Error unmarshaling message:", err)
   continue
  }

  msg["roomId"] = roomID
  updatedMessage, _ := json.Marshal(msg)

  room.mu.Lock()
  for client := range room.Clients {
   if client != conn {
    clientAddr := client.RemoteAddr().String()
    if err := client.WriteMessage(messageType, updatedMessage); err != nil {
     log.Println("Error writing message:", err)
    } else {
     log.Printf("writing message to client[%v] ok\n", clientAddr)
    }
   }
  }
  room.mu.Unlock()
 }

 room.mu.Lock()
 delete(room.Clients, conn)
 room.mu.Unlock()
 log.Printf("Client[%v] left room %s\n", remoteAddr, roomID)
}

我们看到:Room结构体包含一个WebSocket连接的map和一个互斥锁。演示程序使用全局变量rooms(房间map)和相应的互斥锁管理房间和加入房间的连接,并在房间内进行消息广播,以保证消息能转发到参与通信的所有端(Peer)。当然,如果仅有两端在一个房间中,那么这就变成了一对一的实时通信。

这个信令服务器程序启动后,默认监听28080端口,当客户端连接时,会根据URL参数来将客户端连接加入到某个房间,如果房间号参数为空,则代表该客户端期望创建一个房间。先创建房间并加入的客户端作为answer端,等待offer端的连接。当从某个客户端连接收到消息后,会广播给房间内的其他客户端。当客户端断开连接时,便将其从房间中移除。

当然这仅是一个演示版程序,并未对历史建立的房间进行回收,同时也没有进行身份认证等安全方面的控制。

接下来,我们再来看看借助信令服务器进行端到端实时通信的端侧WebRTC应用的实现。

2. 端侧WebRTC应用(webrtc-peer)

WebRTC应用的代码通常都很“样板化”。在开发WebRTC应用程序时,信令连接、设置本地和远程描述、收集ICE候选以及转发信令消息等步骤都是一些常见且重复性较高的任务。这些步骤在不同的WebRTC应用程序中通常都大同小异。以下是这些重复性任务的一些具体步骤示例:

  1. 信令连接处理

  • 创建信令通道(如WebSocket连接)

  • 监听连接建立、断开等事件

  • 通过信令通道交换offer/answer等信令消息

本地和远程描述设置

  • 创建RTCPeerConnection实例

  • 设置本地描述(createOffer/createAnswer)

  • <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值