由于https://appr.tc已经不维护了,导致研究webrtc源代码变得不太方便。同时webrtc编译出来的AppRTCMobile也无法正常和浏览器进行一对一的实时通信,本人花了好长一些时间来解决这些问题,通过解决这些问题特写下这个文章,一方面起到记录使用,另一方面也可以作为他人参考使用,本文在Ubuntu 20.04.6 LTS上部署apprtc,使得两个chrome浏览器可以进行通信,也可以在webrtc for android的AppRTCMobile.apk和chrome浏览器进行通信。
本文只考虑在一台机器中本地部署AppRTC服务器及turn服务的情况,如果需要在公网或者正式商用场景部署,需要考虑https和wss安全链接及其证书的申请和配置等。
图1: 本文搭建后的整体架构
一、前置条件
本文假定同学已经具备了以下条件,如果不具备科学上网的条件,可以下载本人上传到网盘的所有资料,完成本文目标(两个chrome浏览器可以进行通信,也可以在webrtc for android的AppRTCMobile.apk和chrome浏览器进行通信)
- Ubuntu 20.04.6 LTS 系统,配置好了vim,git,python2(Apprtc不支持3.x,后面有时间再来改apprtc支持python3)等环境
- 具备科学上网
- 已经安装好的JDK(1.8),openssh-server、make、libtool等
二、AppRTC安装
1.安装node.js
mkdir ~/Desktop/webrtc
# 也可以采用其他版本的nodejs,本文使用v10.16.0
wget https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.xz
# 解压
tar -xvf node-v10.16.0-linux-x64.tar.xz
# 进入目录
cd node-v10.16.0-linux-x64/
# 查看当前的目录
pwd
# 确认一下nodejs下bin目录是否有node 和npm文件,如果有就可以执行软连接,比如
sudo ln -sf /home/louis/Desktop/webrtc/node-v10.16.0-linux-x64/bin/npm /usr/local/bin/
sudo ln -sf /home/louis/Desktop/webrtc/node-v10.16.0-linux-x64/bin/node /usr/local/bin/
# /home/louis/Desktop/webrtc这个路径你自己创建的路径
# 查看是否安装,安装正常则打印版本号
node -v
npm -v
# 有版本信息后安装 grunt-cli,先进到nodejs的bin目录, 要和自己的目录匹配
cd /home/louis/Desktop/webrtc/node-v10.16.0-linux-x64/bin
sudo npm install grunt-cli
sudo ln -sf /home/louis/Desktop/webrtc/node-v10.16.0-linux-x64/bin/grunt /usr/local/bin/
grunt --version
# 显示grunt-cli v1.4.3
2.安装google_appengine
cd ~/Desktop/webrtc/ # 进到webrtc目录
wget https://storage.googleapis.com/appengine-sdks/featured/google_appengine_1.9.40.zip # 下载google_appengine
unzip google_appengine_1.9.40.zip #解压
export PATH=$PATH:/home/Desktop/webrtc/google_appengine #配置环境变量:在/etc/profile文件最后增加一行,和自己路径保持一致
source /etc/profile
3.安装go
cd ~/Desktop/webrtc/ # 进到webrtc目录
wget http://www.golangtc.com/static/go/1.7/go1.7.linux-amd64.tar.gz
#创建go工作目录
mkdir -p ~/Desktop/webrtc/goworkspace/src
#配置环境变量:在/etc/profile文件最后增加一行:
sudo vim /etc/profile
export GOPATH=/home/louis/Desktop/webrtc/goworkspace
source /etc/profile
4.AppRTC下载和安装
cd ~/Desktop/webrtc/
git clone https://github.com/webrtc/apprtc.git
# 如果github的连接下不了就用gitee的链接
# git clone https://gitee.com/sabergithub/apprtc.git
#将collider的源码软连接到go的工作目录下
ln -sf /home/louis/Desktop/webrtc/apprtc/src/collider/collider $GOPATH/src
ln -sf /home/louis/Desktop/webrtc/apprtc/src/collider/collidermain $GOPATH/src
ln -sf /home/louis/Desktop/webrtc/apprtc/src/collider/collidertest $GOPATH/src
#下一步在编译 go get collidermain
#报错: package golang.org/x/net/websocket: unrecognized import path "golang.org/x/net/websocket"
#所以先执行:
mkdir -p $GOPATH/src/golang.org/x/
cd $GOPATH/src/golang.org/x/
git clone https://github.com/golang/net.git net
cd $GOPATH/src/golang.org/x/net
git checkout v0.16.0 # 切换到老一点的tag v0.16.0,不然编译不过
go install net
#编译collidermain
cd ~/Desktop/webrtc/goworkspace/src
go get collidermain
go install collidermain
5.turn服务器的配置
cd ~/Desktop/webrtc/
sudo apt-get install libssl-dev
sudo apt-get install libevent-dev
wget http://coturn.net/turnserver/v4.5.0.7/turnserver-4.5.0.7.tar.gz #可以使用其他版本
tar xfz turnserver-4.5.0.7.tar.gz
cd turnserver-4.5.0.7
./configure
make
sudo make install
三、Nginx代理服务器的安装
sudo apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev openssl
cd ~/Desktop/webrtc/
#下载nginx 本文以1.15.8版本为例
wget http://nginx.org/download/nginx-1.15.8.tar.gz
tar xvzf nginx-1.15.8.tar.gz
cd nginx-1.15.8/
# 要支持https请添加--with-http_ssl_module参数
./configure --with-http_ssl_module
# 编译
make
#安装
sudo make install
四、配置
1. 开启所有需要的端口
sudo ufw allow 3478/tcp
sudo ufw allow 3478/udp
sudo ufw allow 8080/tcp
sudo ufw allow 8080/udp
sudo ufw allow 8089/tcp
sudo ufw allow 8089/udp
sudo ufw allow 3033/tcp
sudo ufw allow 3033/udp
2. 房间服务器配置
以下通过git diff的方式说明本人修改了那些部分以便支持本文目标。
diff --git a/src/app_engine/apprtc.py b/src/app_engine/apprtc.py
index 933bd7c..54126e3 100755
--- a/src/app_engine/apprtc.py
+++ b/src/app_engine/apprtc.py
@@ -149,8 +149,8 @@ def get_wss_parameters(request):
wss_url = 'ws://' + wss_host_port_pair + '/ws'
wss_post_url = 'http://' + wss_host_port_pair
else:
- 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
return (wss_url, wss_post_url)
def get_version_info():
diff --git a/src/app_engine/constants.py b/src/app_engine/constants.py
index 682331b..9e44c4b 100644
--- a/src/app_engine/constants.py
+++ b/src/app_engine/constants.py
@@ -37,23 +37,42 @@ ICE_SERVER_OVERRIDE = None
# ]
# }
# ]
-ICE_SERVER_BASE_URL = 'https://appr.tc'
+#ICE_SERVER_BASE_URL = 'https://appr.tc'
+ICE_SERVER_BASE_URL = 'http://192.168.0.104:3033'
ICE_SERVER_URL_TEMPLATE = '%s/v1alpha/iceconfig?key=%s'
ICE_SERVER_API_KEY = os.environ.get('ICE_SERVER_API_KEY')
HEADER_MESSAGE = os.environ.get('HEADER_MESSAGE')
ICE_SERVER_URLS = [url for url in os.environ.get('ICE_SERVER_URLS', '').split(',') if url]
# Dictionary keys in the collider instance info constant.
-WSS_INSTANCE_HOST_KEY = 'host_port_pair'
+#WSS_INSTANCE_HOST_KEY = 'host_port_pair'
+WSS_INSTANCE_HOST_KEY = '192.168.0.104:8089'#根据自身的ip和端口来决定
WSS_INSTANCE_NAME_KEY = 'vm_name'
WSS_INSTANCE_ZONE_KEY = 'zone'
WSS_INSTANCES = [{
- WSS_INSTANCE_HOST_KEY: 'apprtc-ws.webrtc.org:443',
+ WSS_INSTANCE_HOST_KEY: '192.168.0.104: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.0.104:8089',
WSS_INSTANCE_NAME_KEY: 'wsserver-std-2',
WSS_INSTANCE_ZONE_KEY: 'us-central1-f'
}]
diff --git a/src/web_app/html/index_template.html b/src/web_app/html/index_template.html
index ade4c22..d032f20 100644
--- a/src/web_app/html/index_template.html
+++ b/src/web_app/html/index_template.html
@@ -137,6 +137,15 @@
<script src="/js/storage.js"></script>
<script type="text/javascript">
+ var servers=[{
+ credential:"louis",
+ username:"louis",
+ urls:[
+ "turn:192.168.0.104:3478?transport=udp",
+ "turn:192.168.0.104:3478?transport=tcp"
+ ]
+ }];
+
var loadingParams = {
errorMessages: {{ error_messages }},
isLoopback: {{ is_loopback }},
@@ -147,7 +156,8 @@
{% endif %}
mediaConstraints: {{ media_constraints | safe }},
offerOptions: {{ offer_options | safe }},
- peerConnectionConfig: {{ pc_config | safe }},
+ //peerConnectionConfig: {{ pc_config | safe }},
+ peerConnectionConfig: { "rtcpMuxPolicy":"require","iceServers":servers,"bundlePolicy":"max-bundle"},
peerConnectionConstraints: {{ pc_constraints | safe }},
iceServerRequestUrl: '{{ ice_server_url }}',
iceServerTransports: '{{ ice_server_transports }}',
diff --git a/src/web_app/js/appcontroller.js b/src/web_app/js/appcontroller.js
index 16a75d5..b01321e 100644
--- a/src/web_app/js/appcontroller.js
+++ b/src/web_app/js/appcontroller.js
@@ -429,11 +429,12 @@ AppController.prototype.onKeyPress_ = function(event) {
};
AppController.prototype.pushCallNavigation_ = function(roomId, roomLink) {
- window.history.pushState({'roomId': roomId, 'roomLink': roomLink}, roomId,
- roomLink);
+ // window.history.pushState({'roomId': roomId, 'roomLink': roomLink}, roomId,
+ // roomLink);//编译报错
};
AppController.prototype.displaySharingInfo_ = function(roomId, roomLink) {
+ roomLink=roomLink.replace("http","https"); //浏览器通话跨域问题 :pushState Messages:Failed to start signaling: Failed to execute ‘pushState’ on ‘History’
this.roomLinkHref_.href = roomLink;
this.roomLinkHref_.text = roomLink;
this.roomLink_ = roomLink;
以上修改需要重新在apprtc目录下编译
cd ~/Desktop/webrtc/apprtc
sudo grunt build
3.nginx代理配置
(1)产生证书
sudo mkdir -p /cert
cd /cert
# CA私钥
openssl genrsa -out key.pem 2048
# 自签名证书
openssl req -new -x509 -key key.pem -out cert.pem -days 9999
(2)配置web服务器
配置文件/usr/local/nginx/conf/conf.d/apprtc-websocket-proxy.conf
upstream roomserver {
server 192.168.0.104:8080;
}
server {
listen 443 ssl;
ssl_certificate /cert/cert.pem;
ssl_certificate_key /cert/key.pem;
charset utf-8;
# ip地址或者域名
server_name 192.168.0.104;
location / {
# 转向代理的地址
proxy_pass http://roomserver$request_uri;
proxy_set_header Host $host;
}
}
编辑nginx.conf文件,在末尾}之前添加包含文件
include /usr/local/nginx/conf/conf.d/*.conf;
}
(3)配置websocket代理
完整配置文件:/usr/local/nginx/conf/conf.d/apprtc-websocket-proxy.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server 192.168.0.104:8089;
}
server {
listen 8088 ssl;
ssl_certificate /cert/cert.pem;
ssl_certificate_key /cert/key.pem;
server_name 192.168.0.104;
location /ws {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_connect_timeout 4s; #配置点1
proxy_read_timeout 6000s; #配置点2,可以考虑这个时间配置长一点
proxy_send_timeout 6000s; #配置点3
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
4.turn web服务器
为能支持webrtc for android的AppRTCMobile.apk能正常和浏览器连接,需要增加一个http的turn访问服务器,通过nodejs启动它,本人将其命名为ice.js,在此之前,需要nodejs安装一下
sudo npm install express
完整的turn web服务代码如下,监听3033端口,将客户端的turn url请求返回正常的turn服务器信息
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()
}
function handleIceRequest(req, resp) {
console.log('handleIceRequest louis louis')
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 + ':ninefingers'
var password = hmac(key, turn_username)
console.log('handleIceRequest req' + req)
return resp.send({
iceServers: [
{
urls: [
'stun:192.168.0.104:3478',//根据自身的ip修改
'turn:192.168.0.104:3478'//根据自身的ip修改
],
username: turn_username,
credential: password
}
]
})
}
app.get('/v1alpha/iceconfig', handleIceRequest)
app.post('/v1alpha/iceconfig', handleIceRequest)
app.listen('3033', function () {
console.log('server started')
})
5.turn服务配置
直接给出配置文件
#本地监听的网卡设备,这里根据自己的实际情况填写
listening-device=ens37 #网卡名
listening-port=3478 #端口
#本地用于转发的网卡设备,这里根据自己的实际情况填写
relay-device=ens37
#指定的转发端口的分配范围,测试时,可以将防火墙全部关闭,防止 UDP 端口被屏蔽
min-port=3480
max-port=3500
#日志输出级别,turnserver 启动时加上 -v,可以得到更清晰的日志输出
Verbose
#消息验证,WebRTC 的消息里会用到
fingerprint
#webrtc 通过 turn 中继,必须使用长验证方式
lt-cred-mech
# ICE REST API 认证需要(如果打开了这行,turn就不工作了)
use-auth-secret
# REST API 加密所需的 KEY
# 这里我们使用“静态”的 KEY,Google 自己也用的这个
static-auth-secret=louis
#用户登录域,下面的写法可以不改变它,因为再启动 turnserver 时,可以通过指定参数覆盖它
realm=<填写你自己的服务器的IP>#这个特别关键,如果这里不是你的服务器的IP,数据就不通
#可为 TURN 服务提供更安全的访问(这个我没用,不知道干啥的)
#stale-nonce
#在Coturn代码中的/etc/examples/目录下有秘钥文件,可以直接用
#cert=/home/louis/Desktop/webrtc/turnserver-4.5.0.7/examples/etc/turn_server_cert.pem
#pkey=/home/louis/Desktop/webrtc/turnserver-4.5.0.7/examples/etc/turn_server_pkey.pem
cert=/cert/cert.pem
pkey=/cert/key.pem
#屏蔽 loopback, multicast IP地址的 relay
no-loopback-peers
no-multicast-peers
#启用 Mobility ICE 支持(不懂)
mobility
#禁用本地 telnet cli 管理接口
no-cli
五、启动服务
为了方便启动,本人做了一个shell脚本 start.sh,可以用来启动所有的服务,脚本内容如下:
#/usr/bin/bash
sudo kill -9 `ps -elf| grep turnserver | awk '{print$4}'`
sudo kill -9 `ps -elf| grep collidermain | awk '{print$4}'`
sudo kill -9 `ps -elf| grep dev_appserver.py | awk '{print$4}'`
sudo kill -9 `ps -elf| grep ice.js | awk '{print$4}'`
cd /home/louis/Desktop/webrtc/apprtc
sudo grunt build
cd -
sleep 1
sudo turnserver -L 0.0.0.0 -z -S -u louis:louis -v -f -r 192.168.0.104 -c /home/louis/Desktop/webrtc/turnserver.conf 2>&1 > turnserver.log &
sleep 1
sudo node /home/louis/Desktop/webrtc/apprtc/ice.js &
sleep 1
sudo /home/louis/Desktop/webrtc/goworkspace/bin/collidermain -port=8089 -tls=false -room-server="http://192.168.0.104:8080" 2>&1 > signal.log &
#sudo /home/louis/Desktop/webrtc/goworkspace/bin/collidermain -port=8089 -tls=false -room-server="http://192.168.0.104:8080" &
sleep 1
#sudo /home/louis/Desktop/webrtc/google_appengine/dev_appserver.py --host=0.0.0.0 /home/louis/Desktop/webrtc/apprtc/out/app_engine --skip_sdk_update_check &
sudo /home/louis/Desktop/webrtc/google_appengine/dev_appserver.py --host=0.0.0.0 /home/louis/Desktop/webrtc/apprtc/out/app_engine --skip_sdk_update_check 2>&1 > roomserver.log &
sleep 1
sudo /usr/local/nginx/sbin/nginx -s stop
sudo /usr/local/nginx/sbin/nginx
sleep 1
ps -elf | grep -E "turn|dev_app|nginx|http|collidermain|ice.js"
此次所有的安装和配置工作都已经完成了,接下来将在“Webrtc系列四——Ubuntu 20.04上Apprtc服务搭建之二”中测试所有的链路和给出本人的所有代码。