webrtc服務器搭建

兩年前寫的筆記,可能有些鏈接和方式已經不對了,自己評估!

名詞解釋

  • realm: 用於描述服務器或服務器中的上下文的字符串。realm告訴客戶端使用哪個用戶名和密碼組合來認證請求用於描述服務器或服務器內的上下文的字符串。
  • allocation: 客戶端向服務端申請分配授予中繼傳輸地址,以及相關狀態,例如權限和到期定時器。它要求不能重複allocate申請,沒有釋放請求協議,而在在一段時間內自動失效(在turnserver中配置)。而如果希望一直使用,則需要定時使用refresh請求保持allocate的資源。在成功的allocate之後,發送數據之前,需要有一個createPermission的請求,這個請求目的是對通信的兩端進行授權,否則中繼不會轉發收到的包到對端,而是丟棄。這個授權與allocate類似,也沒有釋放請求協議,並且會在一段時間內自動失效。如果希望一直使用,則需要定時再次使用相同的請求參數發送createPermission請求。
  • peer: 被轉發的對象,turn客戶端發送數據給turn服務,服務再把數據轉發到peers,peers回覆服務,服務把轉發給turn客戶端。peer就是被轉發的對象。
  • Transport Address: 一個ip地址和一個port的組合
  • Server-Reflexive Transport Address: NAT的“公共端”上的傳輸地址。該地址由NAT分配以對應於特定主機傳輸地址。
  • Long-Term Credential: 簡單來說就是,使用hmac對realm、key、username的組合進行加密,並取base64的值;詳情請看rfc5389#section-15.4
  • candidate:candidate是一個ip端口對(ip地址和端口號),在ICE中它有3類組合{Local Address:子網傳輸地址,Server Reflexive Address:NAT上傳輸地址,Relayed Address:TURN上轉發地址(存在TURN的情況纔有)}

概要

用一張圖片來說明客戶端A向客戶端B發起通話這個過程中,信令服務和STUN服務承擔怎樣的角色
WebRTC單向呼叫的流程圖

  • 房間服務
    房間服務用於創建和管理通話會話的狀態。Google搭建了一個可用的房間服務appr.tc,而且Google也給出這個房間服務的源碼,我們可以依據它的代碼去搭建我們自己的房間服務用於本地測試以及項目使用。

  • 信令服務
    信令服務主要工作是會話控制,網絡和媒體信息交換。具體閱讀WebRTC的一篇官方文章Getting Started with WebRTC,找到Signaling: session control, network and media information

  • 會話控制消息:初始化或者關閉通信並報告錯誤

  • 網絡配置:獲取NAT外的ip和port

  • 媒體能力:獲取當前終端和其他終端{瀏覽器、Android、IOS}支持的編碼解碼器{格式、能力}、採集分辨率、採樣率、位寬等信息。

  • ICE服務
    ICE服務是對STUN和TURN服務的整合,用於處理peers間的打洞和打洞不通的情況下的轉發。

房間服務、信令服務、ICE(STUN、TURN)服務、Web服務的安裝與配置

Google推薦的服務安裝平臺是ubuntu,所以我們需要準備一個ubuntu版本爲14.04及以上的系統。

房間服務 安裝與配置

房間服務是負責房間的創建於管理,也負責整合其他的服務。

這裏採用webrtc提供的apprtc項目去架設我們自己的房間服務,此項目依賴Google_App_Engine_SDK_for_PythonGrunt

安裝

依據官方的說明能很快就能裝好房間服務

  • 安裝依賴
    依賴nodejs、npm、pip這三個執行程序
~/webrtc_server$ sudo apt install nodejs nodejs-legacy npm pip
  • 下載代碼
~/webrtc_server$ git clone https://github.com/webrtc/apprtc.git
  • 編譯
    此服務是基於js編寫的,所以需要配置一下js環境;更新js包管理工具npm,安裝js構建工具grunt-g安裝到全局目錄下,默認全局目錄爲/usr/local/lib/node_modules
    安裝依賴工具
~/webrtc_server/apprtc$ sudo npm install -g npm grunt-cli

安裝依賴包;安裝在當前目錄的node_modules目錄

~/webrtc_server/apprtc$ npm install

編譯apprtc;生成的文件在再當前目錄下的out目錄

~/webrtc_server/apprtc$ grunt build

下載運行apprtc工具-GAE
此房間服務需要依賴Google的應用引擎(Google APP Engine)來執行;用瀏覽器打開GAE倉庫,把網頁拉到最後,找到最新的版本號,然後把剛剛的參考地址+版本號去下載;例如google_appengine_1.9.49.zip

~/webrtc_server/apprtc$ wget https://storage.googleapis.com/appengine-sdks/featured/google_appengine_1.9.49.zip
~/webrtc_server/apprtc$ unzip google_appengine_1.9.49.zip
  • 配置
    每一個peer都會連接房間服務,房間服務需要提供信令服務、打洞/轉發服務的訪問地址,所以此配置需要依據其他兩個服務的配置而定。

constants.py修改如下:

-TURN_BASE_URL = 'https://computeengineondemand.appspot.com'
-TURN_URL_TEMPLATE = '%s/turn?username=%s&key=%s'
-CEOD_KEY = '4080218913'
+TURN_BASE_URL = 'http://192.168.201.64:2016' #Web服務地址
+TURN_URL_TEMPLATE = '%s/turn/%s/%s' # web服務的地址,rest樣式
+CEOD_KEY = 'boyaa_media' # key,這個需要與coturn的配置相同

 WSS_INSTANCES = [{
-    WSS_INSTANCE_HOST_KEY: 'apprtc-ws.webrtc.org:443',
+    WSS_INSTANCE_HOST_KEY: '192.168.201.64:8089', #信令服務器
     WSS_INSTANCE_NAME_KEY: 'wsserver-std',
     WSS_INSTANCE_ZONE_KEY: 'us-central1-a'
 }, {
-    WSS_INSTANCE_HOST_KEY: 'apprtc-ws-2.webrtc.org:443',
+    WSS_INSTANCE_HOST_KEY: '192.168.201.64:8089', #信令服務器
     WSS_INSTANCE_NAME_KEY: 'wsserver-std-2',
     WSS_INSTANCE_ZONE_KEY: 'us-central1-f'
 }]

apprtc.py修改如下:

-    wss_url = 'wss://' + wss_host_port_pair + '/ws'
-    wss_post_url = 'https://' + wss_host_port_pair
+    wss_url = 'ws://' + wss_host_port_pair + '/ws'
+    wss_post_url = 'http://' + wss_host_port_pair
  • 啓動房間服務
    配置GAE環境
~/webrtc_server/apprtc$ export PAHT=$PATH:${PWD}/google_appengine

啓動服務
選項--host房間服務監聽地址,選項--port房間服務監聽端口;選項--admin_host後臺管理監聽地址,選項--admin_port後臺管理監聽端口;選項--log_level打印級別{debug,info,warning,critical,error};選項--skip_sdk_update_check運行dev_appserver.py命令時不檢查更新;

~/webrtc_server/apprtc$ dev_appserver.py --host=0.0.0.0 --admin_host=0.0.0.0 --storage_path=/home/stone/mywork/webrtc_server/log --logs_path=apprtc.log out/app_engine/ --log_level debug

信令服務

房間服務apprtc裏自帶了一個信令服務[Collider](https://github.com/webrtc/apprtc/blob/master/src/collider),是一個Go語言編寫的基於WebSocket實現的一個信令服務器。

  • 安裝依賴
~/webrtc_server$ sudo apt install go
  • 創建collider安裝目錄
    我會把信令服務當作一個單獨的服務處理,所以會爲它創建一個單獨的目,並命名爲collider;由於go的語言的安裝會從環境變量GOPATH尋找可安裝的目錄,所以需要把創建的目錄collider加入到環境變量GOPATH中。
~/webrtc_server$ mkdir -p collider/src && export GOPATH=${PWD}/collider
  • “下載”代碼
    代碼已經存放在房間服務裏面,只需要建立軟連接或者進行一次拷貝,建議是通過軟連接的方式(源目錄需要絕對路徑),這樣下次升級房間服務的時候可以直接升級信令服務,只需要重新編譯就可以了。
~/webrtc_server/apprtc$ ln -s ${PWD}/src/collider/collider $GOPATH/src
~/webrtc_server/apprtc$ ln -s ${PWD}/src/collider/collidermain $GOPATH/src
~/webrtc_server/apprtc$ ln -s ${PWD}/src/collider/collidertest $GOPATH/src
  • 下載相關依賴與編譯
    選項get下載指定包以及相關依賴資源;選項install編譯指定包,編譯後的文件存放在$GOPATH/bin目錄下;如果get失敗,請檢查下環境變量GOPATH
~/webrtc_server/apprtc$ go get collidermain
~/webrtc_server/apprtc$ go install collidermain
  • 修改配置
    需要與房間服務關聯起來,這樣信令服務才知道要服務那個房間服務;需要修改的地方是:監聽端口、房間服務地址、tls模式;從安全角度考慮,後期我們上線以後應該還是需要支持tls模式的,目前測試階段暫時不做支持,因爲需要一個認證的證書。每次修改配置文件都需要重新執行一次安裝的動作go install collidermain
    修改如下:
-var tls = flag.Bool("tls", true, "whether TLS is used")
-var port = flag.Int("port", 443, "The TCP port that the server listens on")
-var roomSrv = flag.String("room-server", "https://appr.tc", "The origin of the room server")
+var tls = flag.Bool("tls", false, "whether TLS is used")
+var port = flag.Int("port", 8089, "The TCP port that the server listens on")
+var roomSrv = flag.String("room-server", "http://192.168.201.64:8080", "The origin of the room server")
  • 啓動信令服務
    collider信令服務支持命令行配置和文件形式配置,命令行配置會覆蓋文件配置;配置文件是GOPATH/src/collidermain/main.go;命令行參數:-port指定監聽端口,-tls設置是否採用tls模式,-room-server設定房間服務地址。
$ $GOPATH/bin/collidermain

turn服務

Google實現的

  • 安裝依賴
    coturn的“瞬時認證”需要依賴數據庫,目前我採用最簡單的sqlite3,如果我們的用戶量較大可以考慮採用其他數據庫;它還依賴openssl做認證、加解密,依賴libevent2做監聽;
$ sudo apt install openssl libevent-core-2.0-5 libevent-dev sqlite sqlite3
  • 下載
    它提供了兩種安裝方式,源碼安裝和deb包安裝,由於我需要簡單閱讀源碼,所以選擇源碼安裝方式。
~/webrtc_server$ git clone https://github.com/coturn/coturn.git
  • 編譯
~/webrtc_server/coturn$ ./configure && make -j && sudo make install
  • 默認配置
    配置文件/usr/local/etc/turnserver.conf
    認證證書: 私鑰/usr/local/etc/turn_server_pkey.pem,公鑰/usr/local/etc/turn_server_cert.pem
    Log文件/var/tmp/turn.log
    數據庫文件/usr/local/var/db/turndb

  • 配置
    初始化數據庫,我們採用sqlite3,利用coturn的配置創建數據庫文件並創建相應的表

~/webrtc_server/coturn$ sudo rm -f /usr/local/var/db/turndb
~/webrtc_server/coturn$ cat turndb/schema.sql | sudo sqlite3 /usr/local/var/db/turndb

創建證書,並拷貝到默認目錄,此證書用到的密碼應該與配置文件turnserver.confstatic-auth-secret的一樣

~/webrtc_server/coturn$ openssl req -new -x509 -newkey rsa:4096 -days 3650 -keyout privkey.pem -out server.pem
~/webrtc_server/coturn$ openssl rsa -in privkey.pem -out privkey.pem
~/webrtc_server/coturn$ sudo mv privkey.pem /usr/local/etc/turn_server_pkey.pem
~/webrtc_server/coturn$ sudo mv server.pem /usr/local/etc/turn_server_cert.pem

配置文件,由於項目需求,我們暫時不考慮stun,所以會去掉stun的監聽以減輕服務器監聽負擔(佔用的資源感覺可以忽略不計),採用REST API方式認證;創建一個空配置文件\/usr\/local\/etc\/turnserver.conf,配置文件內容如下:

listening-device=ens32 #監聽設備名字,通過ifconfig查看
listening-port=3478 #stun/turn共用的監聽端口,根據看到的文檔沒有發現分離方式
listening-ip=192.168.201.64 #監聽的ip,如果不設置會默認監聽ipv4/v6的本地地址以及host地址,我只需要監聽ipv4的host地址,所以需要指定它;可以存在多個監聽地址,寫多個listening-ip即可
relay-device=ens32 #轉發設備名字
relay-ip=192.168.201.64 #轉發地址,我也僅僅需要ipv4的host地址,所以手動指定,也可以多個
verbose #開啓普通打印
fingerprint #取設備server的fingerprint給到client
use-auth-secret #使用瞬時認證
static-auth-secret=boyaa_media #瞬時認證的key
realm=boyaa.com #域名,一定需要設置的
no-dtls #不支持dtls模式
no-stdout-log #不在終端輸出日誌
log-file=/var/log/turnserver.log #指定log存放位置及名字類型,最後的文件名字會是turnserver_日期.log
no-stun #不支持stun,我們只需要轉發
no-loopback-peers #不支持perr使用loopback地址(127.0.0.1 和 ::1)
no-multicast-peers #不支持使用廣播地址(224.0.0.0及以上地址)
mobility #移動ICE規範支持,我也不懂啥意思
cli-ip=192.168.201.64 #command-line-interface地址,也就是telnet登陸地址,一般調試用,我需要用它看turnserver的狀態
cli-password=coturn #telnet登陸密碼
  • 啓動coturn服務
    服務的配置有兩種形式{配置文件、命令行配置},可以同時使用兩種形式的配置,命令行配置會覆蓋文件配置;-o選項以守護進程方式啓動;-c指定配置文件;-n僅僅採用命令行方式配置;-v普通模式的Log輸出;-V很囉嗦的打印,官方都不建議打開;-S僅僅使用stun服務。
$ sudo turnserver
  • 查看監聽端口
    netstat選項說明:-l僅顯示監聽的socket;-n顯示數字地址和端口(192.168.1.102:3487這類地址);-p顯示socket所屬的pid和進程名;-t顯示tcp協議的socket;-u顯示udp協議的socket。
    這些ip和端口REST-API使用的。
$ sudo netstat -lnptu | grep turnserver | uniq | grep tcp
  • 配置REST API
    我們需要一個Web服務用於給webrtc提供“瞬時密碼”,依據coturn REST API規範去編寫這個Web服務,提供一個GET方法;這個Web服務還需要向turn服務寫入這個“瞬時密碼”。
    客戶端和服務端的驗證過程如下圖:
    在這裏插入圖片描述

python實現如下:

import web
import json
import hmac
import sqlite3
from hashlib import sha1
import time, datetime

urls = ('/turn/([^/]+)/[^/]+','turn')

class turn:
    def GET(self,username):
        ttl = 86400
        end_time = int(time.time()) + ttl
        turn_username = str(end_time)+":"+username
        key = 'boyaa_media'
        hashed = hmac.new(key, turn_username, sha1)
        credential = hashed.digest().encode("base64").rstrip('\n')
        data = {
          'ttl': ttl,
          'iceServers': [
              {
                  'urls': [
                      "turn:192.168.201.64:3478?transport=udp",
                      "turn:192.168.201.64:3478?transport=tcp",
                      "turn:192.168.201.64:5349?transport=tcp",
                      "turn:192.168.201.64:5766?transport=tcp",
                      ],
                  'username': turn_username,
                  'credential': credential
              },
           ],
        }

        conn = sqlite3.connect('/usr/local/var/db/turndb')
        conn.execute("insert into turn_secret (realm,value) values (?,?)",(realm,credential));
        conn.commit()
        conn.close()
        return json.dumps(data)

if __name__=="__main__":
    app = web.application(urls, globals())
    app.run()
  • 啓動Web服務
    在後面指定監聽端口號,我使用2016,由於需要訪問到需要root權限的數據庫,所以使用sudo執行
$ sudo python webrtc_restapi_server.py 2016
  • 配置文件其他選項說明
選項 說明
listening-device 監聽的網絡設備名字,通過命令ifconfig查看
listening-port TURN的監聽端口
tls-listening-port TURN TLS監聽端口
listening-ip 轉發服務器監聽ip,可以有多個(設置多個listening-ip即可),同時支持IPv4 IPv6,默認使用本ip
aux-server 輔助服務,用於stun/turn
relay-device 轉發服務器的設備名字
relay-ip 轉發ip,默認採用本地ip地址作爲轉發地址,端口號爲服務器自動分配
external-ip turn server共有地址和私有地址的隱射,書寫形式: external-ip=60.70.80.91 或者 external-ip=60.70.80.91/172.17.19.101
relay-threads 單個連接的最大併發數,如果不設置這個字段默認爲當前設備的CPU個數
min-port 最小分配端口號,轉發服務器申請的最小端口號
max-port 最大分配端口號,轉發服務器申請的最大端口號
verbose 詳細日誌,默認關閉,調試的時候或許可以用到
Verbose 這個是verbose的擴展,會輸出更加詳細的日誌,官方都推薦打開
fingerprint 指紋,默認關閉,TODO
lt-cred-mech long-term認證,可以通過turnadmin -k -u user -p pass -r realm獲取key
no-auth 和lt-cred-mech的作用是相反的,允許匿名訪問turn server,如果沒有定義任何用戶並且沒有定義定義此選擇,那麼默認是開啓的,如果存在一個用戶或者開啓lt-cred-mech,那麼默認是關閉的
use-auth-secret 靜態認證密碼,這是一種較安全的做法,客戶端通過http向服務器請求一組加密後的密碼,客戶端返回一個lt認證過的密碼,用戶可以使用這組密碼與turn server通信。具體查看TURN REST API
static-auth-secret TURN REST API使用的靜態密碼,否則動態生成一個祕密
server-name 認證服務器
oauth 開啓認證
user 設置用戶以及密碼,這個和use-auth-secret是對立存在的。客戶端可以直接通過這個用戶名和密碼來訪問turn server,使用形式:user=username1:key1 user=username1:password1,存在多個用戶就使用使用多次;key通過turnadmin生成
userdb SQLite數據庫文件的位置,使用形式:userdb=/var/db/turndb
psql-userdb PostgreSQL數據庫的連接配置,使用形式:psql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> connect_timeout=30"
mysql-userdb MySQL數據庫的連接配置,使用形式:mysql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> port=<port> connect_timeout=<seconds> read_timeout=<seconds>"
mongo-userdb MongoDB數據庫的連接配置,使用形式:mongo-userdb="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
redis-statsdb Redis數據庫的連接配置,使用形式:redis-statsdb="ip=<ip-address> dbname=<database-number> password=<database-user-password> port=<port> connect_timeout=<seconds>"
realm 域名,任意字符串,例如boyaa.com
user-quota 每一個用戶可以申請的名額個數
total-quota 總共可以申請的名額個數
max-bps 輸入和輸出的最大碼率
bps-capacity 總吞吐量
no-udp 不監聽upd客戶端,默認監聽
no-tcp 不監聽tcp客戶端,默認監聽
no-tls 不監聽tls客戶端,默認監聽
no-dtls 不監聽dtls客戶端,默認監聽
no-udp-relay 不轉發到upd peer,默認轉發
no-tcp-relay 不轉發到tcp peer,默認轉發
max-allocate-lifetime allocate失效時間,默認是3600秒
channel-lifetime channel通道失效時間,默認是600秒
permission-lifetime 權限失效時間,默認死300秒
cert 證書位置
pkey 私鑰位置
no-stdout-log 日誌不輸出到終端,默認輸出
log-file 日誌輸出位置,如果設定,默認情況會同時輸出到終端和log文件內
simple-log 日誌的輸出格式(without PID and date appendage)
alternate-server 交替server,用戶輪流接收客戶端的ALLOCATE requests請求,存在多個server就定義多次
stun-only 只提供stun功能
no-stun 不提供stun功能
rest-api-separator 設置TURN REST API的分隔符號
no-loopback-peers 不允許peer使用loopback地址(127.x.x.x and ::1)
no-multicast-peers 不允許peer使用廣播地址(224.0.0.0 and above, and FFXX:*)
max-allocate-timeout 設置allocate超時時間,單位是秒,使用形式:max-allocate-timeout=60
denied-peer-ip 不允許訪問的ip範圍
allowed-peer-ip 允許訪問的ip範圍
no-cli 關閉telnet支持
cli-ip 設置telnet登陸ip
cli-port 設置telnet端口號

參考

擴展閱讀

  • NAT,瞭解NAT是什麼,以及NAT類型{完全圓錐型NAT、受限圓錐型NAT、端口受限圓錐型NAT、對稱NAT}
  • ICE,瞭解ICE是啥
  • STUN,知道啥是STUN(是一種網絡協議,允許客戶端找出自己的公網地址,查出自己位於哪種類型的NAT之後以及NAT爲某一個本地端口所綁定的Internet端端口);瞭解STUN 流程是如何通過stun協議判斷client的NAT類型的
  • TURN,瞭解TURN的是幹啥的
  • SDP for the WebRTC,在創建本地媒體之後需要把這些信息按照SDP規範封裝起來(音視頻編碼格式、採樣率、位寬、分辨率等),peer端接收到這些信息以後創建指定類型的解碼器,與代碼結合起來看規範會容易理解一些;sessiondescription.h包含了對SDP的拆分的封裝;此文檔主要以例子爲主
  • SDP-RFC4145,如果不想閱讀RFC文檔,想快速瞭解SDP可以看看wiki-SDP對SDP字段的解釋
  • 路由形式,瞭解路由形式,特別是單播和多播{任播廣播多播單播geocase}
  • webrtcstats,瞭解webrtc的一些統計

other

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章