Ubuntu 下搭建 AppRTC 服务
研究 WebRTC 协议,最好先自己基于官方 Demo 动手搭一套服务环境,其中包括 房间服务器 AppRTC 、信令服务器(AppRTC 目录下的 src/Collider)、coTurn NAT 穿透服务器 ,分别通过下载、编译并配置相关参数,便可以成功的搭建起一套 WebRTC 实时音视频通信系统;
而客户端可以使用 Firefox brower 和 Android 客户端 AppRTCMobile Demo。
以下为浏览器与Android AppRTCMobile Demo 建立视频通话效果图(Android 截图)!
![](https://img.haomeiwen.com/i6162333/d340f444798afa9a.png)
在搭建的过程中,走了一些弯路,现记录下正确的流程,以供学习交流!
注:搭建过程中需要从 Google 服务器下载工具包,所以请事先准备好相应的网络环境!
一、环境准备
本人一直在 Ubuntu 下进行编译、配置、调试 webrtc 相关服务,故以下内容均是指在 Ubuntu 下的操作流程!
1、安装 Ubuntu 系统
目前 AppRTC 目前仅支持 Linux/MAC 下运行(Linux kernel 均可),安装 Ubuntu 系统。
安装好 Git ,以下载 AppRtc 源码 :
sudo apt-get install git
2、 VPN网络
搭建过程中需要从 Google 服务器下载源代码和工具包,所以请事先准备好相应的翻墙网络环境!
二、编译 & 运行 AppRTC
1、下载 AppRTC 源码
如果本机已经配置好了 ssh public key,则可以通过以下命令进行下载:
git clone git@github.com:webrtc/apprtc.git
或者通过 Https 进行下载,不需要配置 ssh key:
git clone https://github.com/webrtc/apprtc.git
编译并运行 AppRTC,可参照其 README,需首先下载安装并配置相关依赖包;
可依次执行以下安装命令,如果全部执行成功,则直接跳到第 6 步:
sudo apt-get install nodejs
sudo npm install -g npm
sudo apt-get install nodejs-legacy
sudo npm -g install grunt-cli
sudo apt-get install python-requests
2、下载编译 Google App Engine SDK for Python
Google App Engine 是一种让您可以在 Google 的基础架构上运行您的网络应用程序。Google App Engine 应用程序易于构建和维护,并可根据您的访问量和数据存储需要的增长轻松扩展。使用 Google App Engine,将不再需要维护服务器:您只需上传您的应用程序,它便可立即为您的用户提供服务。
当前,Google App Engine支持的编程语言包括 Python、Java、PHP 和 GO。Google说它准备在未来支持更多的语言,Google App Engine也将会独立于某种语言。任何支持 WSGI 的使用 CGI 的 Python 框架可以使用。框架可以与开发出的应用程序一同上传,也可以上传使用Python编写的第三方库。这里我们使用 基于 Python 的 App Engine。
参考文档安装 Google app engine sdk ,严格按照文档进行安装即可;
3、下载Node.js
Node.js 是一个能够在服务器端运行 JavaScript 的开放源代码、跨平台的 JavaScript 运行环境;Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 Node.js 的包管理器 npm,是全球最大的开源管理系统。
Node.js大部分基本模块都用JavaScript语言编写。在Node.js出现之前,JavaScript通常作为客户端程序设计语言使用,以JavaScript写出的程序常在用户的浏览器上运行。Node.js的出现使JavaScript也能用于服务器端编程。Node.js含有一系列内置模块,使得程序可以脱离 Apache Http Server 或 IIS,作为独立服务器运行。
下载 并配置 bin 目录到 环境变量 PATH 中,或者直接使用命令安装:
sudo apt-get install nodejs
sudo apt-get install nodejs-legacy
4、下载 Grunt
在 JavaScript 的开发过程中,经常会遇到一些重复性的任务,比如合并文件、压缩代码、检查语法错误、将Sass代码转成CSS代码等等。通常,我们需要使用不同的工具,来完成不同的任务,既重复劳动又非常耗时。Grunt就是为了解决这个问题而发明的工具,可以帮助我们自动管理和运行各种任务。
简单说,Grunt是一个自动任务运行器,会按照预先设定的顺序自动运行一系列的任务。这可以简化工作流程,减轻重复性工作带来的负担。
Grunt基于Node.js,安装之前要先安装Node.js。
安装 Grunt 可以通过官网介绍进行下载安装,或者通过 npm 进行安装:
sudo apt-get install npm
sudo npm -g install grunt-cli //-g 表示全局安装
5、安装 Python-requests 依赖
sudo apt-get install python-requests
6、编译 AppRTC
进入 AppRTC 目录,执行编译命令:
npm install
sudo grunt build
编译成功后,会在 out/app_engine 目录下生成 server 可执行脚本。
7、配置 apprtc.py
AppRTC 配置文件存放在 AppRTC 源码目录下的 out/app_engine 下:
gobert@gobert-ThinkPad-X230:~/OpenSource/apprtc/out/app_engine$ ls
analytics_enums.py apprtc.py compute_page.py full_template.html js
analytics_page.py apprtc.py_bak constants.py html probers.py
analytics.py app.yaml cron.yaml images third_party
apiauth.py bigquery css index_template.html version_info.json
gobert@gobert-ThinkPad-X230:~/OpenSource/apprtc/out/app_engine$
打开 apprtc.py 配置文件,修改 get_wss_parameters(request) 方法定义,目的为不要让客户端或浏览器使用 ssl 进行连接(因为我们刚开始还没有任何根证书),但是如果有第三 方根证书的签名机构颁发的证书就不需要这样了。
将 wss 改为 ws, https 改为 http 即可:
if wss_tls and wss_tls == 'false':
wss_url = 'ws://' + wss_host_port_pair + '/ws'
wss_post_url = 'http://' + wss_host_port_pair
else:
wss_url = 'ws://' + wss_host_port_pair + '/ws'
wss_post_url = 'http://' + wss_host_port_pair
8、配置 constants.py
打开并修改 constants.py 文件,因为我们计划后续支持 AppRTC Demo 与 浏览器之间的视频通话,而 AppRTC Demo 默认只支持通过 HTTP REST API 的方式获取 TURN Server 地址信息,所以我们必须配置一个 ICE HTTP URL,当客户端请求此 URL 时,返回一个 json 字符串,其中包含 turnserver 地址及认证信息,url 地址根据自己配置的具体的 http server ip & port 进行设定:
# TODO(jansson): Remove once AppRTCDemo on iOS supports ICE_SERVER.
#TURN_BASE_URL = 'https://computeengineondemand.appspot.com'
TURN_BASE_URL = 'http://100.100.32.64:3033'
TURN_URL_TEMPLATE = '%s/turn?username=%s&key=%s'
CEOD_KEY = '4080218913'
ICE_SERVER_BASE_URL = 'http://100.100.32.64:3033'
#ICE_SERVER_BASE_URL = 'https://networktraversal.googleapis.com'
#ICE_SERVER_URL_TEMPLATE = '%s/v1alpha/iceconfig?key=%s'
ICE_SERVER_URL_TEMPLATE = '%s/iceconfig?key=%s'
ICE_SERVER_API_KEY = os.environ.get('ICE_SERVER_API_KEY')
配置 信令服务器 collider 信息,指定 collider 运行端口号为 8089,后面在启动 collider 时,要与其指定绑定的端口号相对应;
WSS_INSTANCES 只保留一个即可:
# Dictionary keys in the collider instance info constant.
WSS_INSTANCE_HOST_KEY = 'host_port_pair'
WSS_INSTANCE_NAME_KEY = 'vm_name'
WSS_INSTANCE_ZONE_KEY = 'zone'
WSS_INSTANCES = [{
WSS_INSTANCE_HOST_KEY: '100.100.32.64:8089',
WSS_INSTANCE_NAME_KEY: 'wsserver-std',
WSS_INSTANCE_ZONE_KEY: 'us-central1-a'
}]
9、启动 AppRTC Server
在 AppRTC 目录下执行启动命令:
dev_appserver.py -–host=0.0.0.0 ./out/app_engine
注:--host 指定绑定 IP 地址,如 100.100.32.64,如果不指定,则默认使用 localhost,但其它终端不能访问。
dev_appserver.py 拥有众多配置参数,详细如下:
usage: dev_appserver.py [-h] [-A APP_ID] [--host HOST] [--port PORT]
[--admin_host ADMIN_HOST] [--admin_port ADMIN_PORT]
[--auth_domain AUTH_DOMAIN] [--storage_path PATH]
[--log_level {debug,info,warning,critical,error}]
[--max_module_instances MAX_MODULE_INSTANCES]
[--use_mtime_file_watcher [USE_MTIME_FILE_WATCHER]]
[--threadsafe_override THREADSAFE_OVERRIDE]
[--php_executable_path PATH]
[--php_remote_debugging [PHP_REMOTE_DEBUGGING]]
[--php_gae_extension_path PATH]
[--php_xdebug_extension_path PATH]
[--appidentity_email_address APPIDENTITY_EMAIL_ADDRESS]
[--appidentity_private_key_path APPIDENTITY_PRIVATE_KEY_PATH]
[--python_startup_script PYTHON_STARTUP_SCRIPT]
[--python_startup_args PYTHON_STARTUP_ARGS]
[--jvm_flag JVM_FLAG] [--go_work_dir GO_WORK_DIR]
[--enable_watching_go_path [ENABLE_WATCHING_GO_PATH]]
[--custom_entrypoint CUSTOM_ENTRYPOINT]
[--runtime RUNTIME] [--blobstore_path BLOBSTORE_PATH]
[--mysql_host MYSQL_HOST] [--mysql_port MYSQL_PORT]
[--mysql_user MYSQL_USER]
[--mysql_password MYSQL_PASSWORD]
[--mysql_socket MYSQL_SOCKET]
[--datastore_path DATASTORE_PATH]
[--clear_datastore [CLEAR_DATASTORE]]
[--datastore_consistency_policy {consistent,random,time}]
[--require_indexes [REQUIRE_INDEXES]]
[--auto_id_policy {sequential,scattered}]
[--logs_path LOGS_PATH]
[--show_mail_body [SHOW_MAIL_BODY]]
[--enable_sendmail [ENABLE_SENDMAIL]]
[--smtp_host SMTP_HOST] [--smtp_port SMTP_PORT]
[--smtp_user SMTP_USER]
[--smtp_password SMTP_PASSWORD]
[--smtp_allow_tls [SMTP_ALLOW_TLS]]
[--search_indexes_path SEARCH_INDEXES_PATH]
[--clear_search_indexes [CLEAR_SEARCH_INDEXES]]
[--enable_task_running [ENABLE_TASK_RUNNING]]
[--allow_skipped_files [ALLOW_SKIPPED_FILES]]
[--watcher_ignore_re WATCHER_IGNORE_RE]
[--api_port API_PORT] [--grpc_api GRPC_APIS]
[--grpc_api_port GRPC_API_PORT]
[--automatic_restart [AUTOMATIC_RESTART]]
[--dev_appserver_log_level {debug,info,warning,critical,error}]
[--skip_sdk_update_check [SKIP_SDK_UPDATE_CHECK]]
[--default_gcs_bucket_name DEFAULT_GCS_BUCKET_NAME]
[--env_var ENV_VARIABLES]
[--google_analytics_client_id GOOGLE_ANALYTICS_CLIENT_ID]
[--google_analytics_user_agent GOOGLE_ANALYTICS_USER_AGENT]
yaml_path [yaml_path ...]
至此房间服务器 AppRTC Server 已经运行起来了。
三、编译 & 运行 Collider
WebRTC 并未规定信令服务器的具体协议标准,其只需要帮助客户端来回传递信息即可,这个过程在 webrtc 中并未实现,需要开发者自己实现,不过在 AppRTC 中提供了一个 Collider (集成在 AppRTC 内部的)信令服务器可供参考、调试。
1、安装 Go 开发包
Collider 基于 Go 语言进行开发,编译 collider 则必须依赖 go 语言工具包,执行以下命令首先安装 Go 语言开发工具:
sudo apt-get install golang-go
2、配置 Go 环境变量
首先创建 Go 工作目录 $HOME/goWorkspace,并添加环境变量 GOPATH ,并在 GOPATH 中手动创建 src 目录;
编辑 /etc/profile 文件,在最下方添加如下内容:
#set go path
export GOPATH=$HOME/goWorkspace
添加完成后,执行
source /etc/profile
使环境变量生效;
最后在 $GOPATH 中手动添加 src 目录:
mkdir $GOPATH/src
注:如需 room 权限,则自行添加 sudo 前缀。
3、拷贝 collider 源码
在 $GOPATH/src 目录下创建指向 collider 源码目录的软链接,或者直接拷贝 collider 目录源码至 GOPATH 目录;
创建软链接时不要使用官方推荐的 'pwd',否则更换了目录,软链接就会指向了无效目录,建议使用绝对路径,如:
sudo ln -s $HOME/OpenSource/apprtc/src/collider/collider $GOPATH/src
sudo ln -s $HOME/OpenSource/apprtc/src/collider/collidermain $GOPATH/src
sudo ln -s $HOME/OpenSource/apprtc/src/collider/collidertest $GOPATH/src
4、编译、安装 collider
将上一步中指向的 collider 源码进行编译并安装到系统中,首先要将 collider server 中指定的 Room Server 地址改为本地的 AppRTC Server 地址,修改 collider/collidermain/main.go 文件中的 roomSrv 地址:
var roomSrv = flag.String("room-server", "https://appr.tc", "The origin of the room server")
改为:
var roomSrv = flag.String("room-server", "http://192.168.1.107:8080/", "The origin of the room server")
如果没有翻墙的话,go get 将无法安装 golang 的官方包,导致编译报错,如:
package golang.org/x/net/websocket: unrecognized import path "golang.org/x/net/websocket" (https fetch: Get https://golang.org/x/net/websocket?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
其实 golang 在 github 上建立了一个镜像库,如 https://github.com/golang/net 即是 https://golang.org/x/net 的镜像库,获取 golang.org/x/net 包,其实只需要以下步骤:
mkdir -p $GOPATH/src/golang.org/x
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/net.git
其它 golang.org/x 下的包获取皆可使用该方法
编译并安装 collider:
go get collidermain
go install collidermain
如果提示 $GOPATH not set,请排查 GOPATH 设置是否正常,倘若还是搞不定的话,就使用如下命令进行编译安装:
sudo env GOPATH=$HOME/goWorkspace go get collidermain
sudo env GOPATH=$HOME/goWorkspace go install collidermain
5、启动 collider
任意目录下,通过如下命令启动 collider 服务,其中 port 需配置为 apprtc server 的 constants.py 中信令服务器 WSS_INSTANCE_HOST_KEY 的端口地址:
collidermain -port=8089 -tls=false
其中 --port 指定端口,-tls 是否开启 ssl 安全验证;collidermain 还有一个启动配置参数:-room-server 用于制定房间服务器地址,如果未指定,则默认使用源码 collider/collidermain/main.go 文件中的 roomSrv 地址。如:
collidermain -port=8089 -tls=false -room-server="http://100.100.32.64:8080"
三、配置 & 启动 coTurn
coturn 是一个 turn server,其支持浏览器通过 stun 协议与其进行通讯,以获取浏览器自己的公网地址及 NAT 信息,如果建立 ICE Server 则需要自己建立一个 http Server,客户端通过 REST API 进行访问以获取 turn Server 地址及认证信息等;我们将首先通过配置,保证浏览器使用我们自己的 TURN Server。
1、下载
下载 Ubuntu 下对应的最新版本,下载地址
coturn 底层网络库依赖 libevent,所以需首先安装 libevent,这个我们就直接通过 apt-get 进行下载安装,不单独下载源码进行编译安装了:
sudo apt-get install libevent-dev
2、编译
coturn 下载的是源码,需自己编译,进入 coturn 源码目录,依次执行以下命令:
./configure
make
编译成功后,会在源码 bin 目录中生成配置及可执行文件(手动将 example 目录中的 turnserver.conf 配置文件拷贝到 bin 目录):
gobert@gobert-ThinkPad-X230:~/OpenSource/coturn/bin$ ls
turnadmin turnutils_natdiscovery turnutils_stunclient
turnserver turnutils_oauth turnutils_uclient
turnserver.conf turnutils_peer turnutils_rfc5769check
3、配置
修改 turnserver.conf 配置为如下内容,并根据自己的实际情况做适当的修改:
#如果多网卡,记得此处设置为和你所用监听的IP相对应的eth
listening-device=wlxe840f236118e
listening-port=3478
relay-device=wlxe840f236118e
#转发端口号范围
min-port=59000
max-port=65000
#Verbose
fingerprint
#webrtc需要使用此选项
lt-cred-mech
use-auth-secret
static-auth-secret=4080218913
#之前turnadmin中-r参数的值,此处要对应
realm=qiniu
#stale-nonce=600
#max-allocate-lifetime=3000
#可以添加用户名和密码
user=gobert:4080218913
#测试期间可以使用example/etc中的pem,自己计算的话需要用到openssl,方法为:
#sudo openssl req -x509 -newkey rsa:2048 -keyout /etc/turn_server_pkey.pem -out /etc/turn_server_cert.pem -days 99999 -nodes
#填写pem目录即可,如
#cert=~/OpenSource/coturn/example/etc/turn_server_cert.pem
#pkey==$HOME/OpenSource/coturn/example/etc/turn_server_pkey.pem
# Certificate file.
# Use an absolute path or path relative to the
# configuration file.
#
cert=/usr/local/etc/turn_server_cert.pem
# Private key file.
# Use an absolute path or path relative to the
# configuration file.
# Use PEM file format.
#
pkey=/usr/local/etc/turn_server_pkey.pem
no-loopback-peers
no-multicast-peers
mobility
no-cli
#各项参数含义,可以看turnserver.conf中的说明。
或者根据自己一项一项的在 turnserver.conf 文件中查找并修改;
4、启动 coTurn
进入 bin 目录
sudo ./turnserver
四、配置 ICE REST API
客户端连接 Room Server apprtc 后,通过 Room Server 得到用于获取 ICE Server 信息的 iceServerRequestUrl,然后浏览器和 AppRTC Demo 通过请求此 URL,得到 ICE Server 相关信息(返回 json),具体标准请参考:TURN REST API
下面一一介绍,如何配置 REST API 接口,以供浏览器及 AppRTC Demo 获取 coTurn Server 连接信息。
1、配置 REST API
如果前面已经成功编译 apprtc server,则一定已经安装过了 Node.js 工具包,这里就介绍通过 Node.js 配置 REST API 的方法;另:通过 apache2 或者 nginx 解析 php 去实现也可以,本人均已配置通过(前期花了较大的精力去一步步解决配置 nginx 解析 php 的问题),但走到最后感觉最方便的还是 Node.js:
-
首先安装 Express,Express 是一种保持最低程度规模的灵活 Node.js Web 应用程序框架,为 Web 和移动应用程序提供一组强大的功能。
npm install express --save
-
生成 index.js 代码文件,配置 REST API
在一个自定义目录中新建 index.js 文件,内容如下:
var express = require('express') var crypto = require('crypto') var app = express() var hmac = function (key, content) { var method = crypto.createHmac('sha1', key) method.setEncoding('base64') method.write(content) method.end() return method.read() } app.get('/iceconfig', function (req, resp) { var query = req.query var key = '4080218913' var time_to_live = 600 var timestamp = Math.floor(Date.now() / 1000) + time_to_live var turn_username = timestamp + ':gobert' var password = hmac(key, turn_username) resp.setHeader("Access-Control-Allow-Origin", "*"); //设置跨域访问 return resp.send({ iceServers: [ { urls: [ 'turn:100.100.32.64:3478?transport=udp', 'turn:100.100.32.64:3478?transport=tcp', 'turn:100.100.32.64:3479?transport=udp', 'turn:100.100.32.64:3479?transport=tcp' ], username: turn_username, credential: password } ] }) }) app.listen('3033', function () { console.log('server started') })
注意:
resp.setHeader("Access-Control-Allow-Origin", "*"); //设置跨域访问
*添加此行代码,可以解决同源策略禁止跨域请求的问题,为了方便设置了 ,即所有域名,布置公网的话比较危险;
其中 turn server ip 地址请根据上面步骤中的配置自行修改!
最后通过 node 启动此脚本,外部即可访问:
node index.js
访问方法如:
http://100.100.32.64:3303/iceconfig?key=none
注:其实 URL 中 iceconfig 后面跟其它参数也是可以的,客户端只需要最后返回的 json 字符串;
2、 修改 apprtc 配置
修改 apprtc 源码目录下 /out/app_engine/js/apprtc.debug.js 文件,将 requestIceServers 方法中调用 sendAsyncUrlRequest 时使用的 POST 方法改为 GET 方法,因为我们的这个 nodejs 代码不支持 POST 方法:
function requestIceServers(iceServerRequestUrl, iceTransports) {
return new Promise(function(resolve, reject) {
//sendAsyncUrlRequest("POST", iceServerRequestUrl).then(function(response) {
sendAsyncUrlRequest("GET", iceServerRequestUrl).then(function(response) {
var iceServerRequestResponse = parseJSON(response);
if (!iceServerRequestResponse) {
reject(Error("Error parsing response JSON: " + response));
return;
}
if (iceTransports !== "") {
filterIceServersUrls(iceServerRequestResponse, iceTransports);
}
trace("Retrieved ICE server information.");
resolve(iceServerRequestResponse.iceServers);
}).catch(function(error) {
reject(Error("ICE server request error: " + error.message));
return;
});
});
注:此项修改主要解决客户端通过 POST 不能访问我们的 REST API 获取 turn server 的问题!
3、修改 Android Demo 源码
参照以上方法配置后,浏览器已经可以正常的进入房间,开启音视频通话,而通过 Android demo(AppRTCMobile)请求时会报 404 错误(Connection error Room IO Error, Non-200 response when requesting TURN Server from ×××HTTP/1.1 404 not found)
解决方法:通过修改 RoomParametersFetcher.java 文件,找到并注释掉此行代码即可:
//connection.setDoOutput(true);
具体作用请自行查找资料。
修改后,需重新编译生成 AppRTCMobile.apk 并安装:
ninja -C out/Debug
4、重启服务
修改了上述文件配置,建议重新启动下相关服务进程:
- 重启 room server(apprtc)
- 重启 REST API 服务(nodejs)
五、测试
1、访问测试
通过浏览器与Android AppRTCMobile Demo 进行联调测试!
- 可以通过浏览器访问 AppRTC server 进行访问:http://ip:8080 ,输入一个房间号,如 123456,并加入;
- 打开 AppRTC Demo Settings 界面,在 Room server URL 配置栏中,输入相同的 地址:http://ip:8080,回到首界面输入房间号:123456,加入即可与浏览器进行音视频通话。
至此,我们已经可以通过我们自己搭建的 Room Server、Collider Server、ICE Server 进行音视频聊天了!
注:官方 Demo 默认仅支持两人进入房间。
当前仅支持 Firefox 浏览器,因为 Chrome 浏览器(version 47 以后)对音视频采集有权限控制,其目前仅支持在 localhost 打开音视频采集,如果是其它网络地址,则需要通过 HTTPS 进行加密访问。
关于如何让 Chrome 浏览器支持访问 AppRTC Server,将在后续更新中介绍。
以下为浏览器与Android Demo 联调效果图!
![](https://img.haomeiwen.com/i6162333/d340f444798afa9a.png)
2、抓包验证
在建立音视频通话过程中,通过 wireshark 抓包,并过滤 stun 协议,通过查看数据包往来判断会话建立过程全部通过内网服务器:
![](https://img.haomeiwen.com/i6162333/cd0e24c27ec1d192.png)
3、浏览器调试
在调试 REST API 时,借助浏览器的控制台可以很好的进行错误的定位和排查,下图为配置好后,浏览器控制台的正确输出:
![](https://img.haomeiwen.com/i6162333/0cf34a7238df3257.png)
通过查看来往消息,可以清晰的看到 webrtc 在建立音视频通话的过程中,与 room server、collider server、coturn server 之间的通讯流程。