服务端部署
以下我所列出的Docker镜像部署方式是在 Debian 12 下进行操作的,部署过程中由于镜像下载较缓慢,我直接使用了代理进行进行拉取,没有使用Docker镜像源来进行部署。
快速部署指导
将如下配置中的 对镜像内的相关配置进行设置 命令部分的 test.212490197.xyz 改为 生产环境公网域名或IP,且开放 10000-10009 的 UDP 公网端口,15061 的 TCP+UDP 端口,并将相应公网端口指向该服务,即可通过配置中的默认配置使用 [1001], [1002], [1003], [10086], [10011] 五个号码进行互相打电话的操作。
BASH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release
sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/debian \ $(. /etc/os-release; echo "$VERSION_CODENAME") stable" \ | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER newgrp docker
sudo systemctl set-environment HTTP_PROXY=socks5://192.168.1.12:23333 HTTPS_PROXY=socks5://192.168.1.12:23333 sudo systemctl restart docker
docker pull andrius/asterisk:18.26.4_debian-trixie
docker run -d --name asterisk --network host --restart always andrius/asterisk:18.26.4_debian-trixie asterisk -fvvv
docker exec -it asterisk bash
cat > /etc/asterisk/rtp.conf <<EOF [general] rtpstart=10000 rtpend=10009 rtpchecksums=no icesupport=yes EOF cat > /etc/asterisk/http.conf <<EOF [general] enabled=yes enablestatic=yes bindaddr=0.0.0.0 bindport=8088 tlsenable=yes tlsbindaddr=0.0.0.0:8089 tlscertfile=/etc/asterisk/keys/asterisk.pem tlsprivatekey=/etc/asterisk/keys/asterisk.key EOF cat > /etc/asterisk/pjsip.conf <<EOF [transport-udp] type=transport protocol=udp bind=0.0.0.0:15061 external_media_address=test.212490197.xyz ; 宿主机局域网 IP external_signaling_address=test.212490197.xyz local_net=192.168.0.0/16
[transport-tcp] type=transport protocol=tcp bind=0.0.0.0:15061 ; 监听 TCP SIP 端口 external_media_address=test.212490197.xyz external_signaling_address=test.212490197.xyz local_net=192.168.0.0/16
[transport-wss] type=transport protocol=wss bind=0.0.0.0:8089 external_media_address=192.168.1.2 external_signaling_address=192.168.1.2
[1001] type=endpoint context=default disallow=all allow=ulaw auth=1001 aors=1001 transport=transport-tcp rtcp_mux=yes rtp_symmetric=yes force_rport=yes direct_media=no rewrite_contact=yes
[1001] type=auth auth_type=userpass password=123456 username=1001
[1001] type=aor max_contacts=1 remove_existing=yes
[1002] type=endpoint context=default disallow=all allow=ulaw auth=1002 aors=1002 transport=transport-tcp rtcp_mux=no use_avpf=no rtp_symmetric=yes force_rport=yes direct_media=no rewrite_contact=yes
[1002] type=auth auth_type=userpass password=123456 username=1002
[1002] type=aor max_contacts=1 remove_existing=yes
[1003] type=endpoint context=default disallow=all allow=ulaw auth=1003 aors=1003 transport=transport-tcp rtcp_mux=yes rtp_symmetric=yes force_rport=yes direct_media=no rewrite_contact=yes
[1003] type=auth auth_type=userpass password=123456 username=1003
[1003] type=aor max_contacts=1 remove_existing=yes
[10086] type=endpoint context=default disallow=all allow=ulaw auth=10086 aors=10086 transport=transport-tcp rtcp_mux=yes rtp_symmetric=yes force_rport=yes direct_media=no rewrite_contact=yes
[10086] type=auth auth_type=userpass password=123456 username=10086
[10086] type=aor max_contacts=1 remove_existing=yes
[10010] type=endpoint context=default disallow=all allow=ulaw auth=10010 aors=10010 transport=transport-wss media_encryption=dtls dtls_verify=no dtls_setup=actpass dtls_cert_file=/etc/asterisk/keys/all.pem ;dtls_ca_file=/etc/asterisk/keys/ca.crt use_avpf=yes rtcp_mux=yes rtp_symmetric=yes force_rport=yes rewrite_contact=yes ice_support=yes direct_media=no
[10010] type=auth auth_type=userpass password=123456 username=10010
[10010] type=aor max_contacts=1 remove_existing=yes
[10011] type=endpoint context=default disallow=all allow=ulaw auth=10011 aors=10011 transport=transport-tcp rtcp_mux=yes rtp_symmetric=yes force_rport=yes direct_media=no rewrite_contact=yes
[10011] type=auth auth_type=userpass password=123456 username=10011
[10011] type=aor max_contacts=1 remove_existing=yes
[10000] type=endpoint context=default disallow=all allow=ulaw auth=10000 aors=10000 transport=transport-wss media_encryption=dtls dtls_verify=no dtls_setup=actpass dtls_cert_file=/etc/asterisk/keys/all.pem ;dtls_ca_file=/etc/asterisk/keys/ca.crt use_avpf=yes rtcp_mux=yes rtp_symmetric=yes force_rport=yes rewrite_contact=yes ice_support=yes direct_media=no asymmetric_rtp_codec=no
[10000] type=auth auth_type=userpass password=123456 username=10000
[10000] type=aor max_contacts=1 remove_existing=yes EOF cat > /etc/asterisk/extensions.conf <<EOF [default] exten => 1001,1,Dial(PJSIP/1001) exten => 1002,1,Dial(PJSIP/1002) exten => 1003,1,Dial(PJSIP/1003) exten => 10086,1,Dial(PJSIP/10086) exten => 10010,1,Dial(PJSIP/10010) exten => 10011,1,Dial(PJSIP/10011) exten => 10000,1,Dial(PJSIP/10000) EOF
asterisk -rx "pjsip reload"
asterisk -rx "dialplan reload"
docker restart asterisk
|
配置中的关键点解释
- 在
rtp.conf 中的 rtpstart 和 rtpend 指定了进行通话时可使用的UDP端口范围(端口需在公网下进行UDP通信开放,否则无法进行通话操作),此处指定了10个,而目前的配置下,观测到一次双向通话会占用4个UDP端口,这意味着在这个配置下,最多保持2个并发的双向通话。
- 在
http.conf 中,配置为了供前端WebRTC进行通话的端口。由于目前浏览器使用WebRTC无法听到声音(截至2026年1月27日),故该配置目前暂时无用。
- 在
pjsip.conf 中
[transport-udp],[transport-tcp],[transport-wss] 分别为 UDP、TCP、浏览器与服务端通信时所使用的的信令协议。
[transport-udp]和[transport-tcp]中的
bind 为UDP和TCP信令需要暴露到外界的端口,同样需要进行外网端口的开放,TCP和UDP的都开放。
external_media_address 和 external_signaling_address 为公网IP或域名,我这里使用了我自己的测试域名,实际情况下需替换为公网IP或域名。
local_net 为所在服务器的局域网的 CIDR 的IP格式
[transport-wss]中的
bind 为WebRTC连接的端口,由于暂不使用,且为内网访问,故这里不需要暴露端口,且改为任意其他未占用端口即可。
external_media_address 和 external_signaling_address 为公网IP或域名,这里由于暂不使用,所以我只填写了内网IP,后边改为实际的内网IP即可。
- 进行一个有效的用户添加
- 这里
[1001] 相当于一个用户,一个用户分了三个块,按照固定格式填写即可。每个块的[xxxx]需要保持一致
- 第一个块需要修改的部分为
auth 和 aors 均和用户号码一致
transport是信令传输协议,这里使用了tcp
- 第二个块需要修改的部分为
password 是用户的认证密码
username与用户号码保持一致
- 添加之后,这个用户就相当于是可以使用该服务进行通信了
- 这里的
[1001], [1002], [1003], [10086], [10011] 为普通客户端的通信配置案例,[10010] 和 [10000] 为WebRTC通信配置示例,可根据实际情况进行修改或删减
BASH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| [1001] type=endpoint context=default disallow=all allow=ulaw auth=1001 aors=1001 transport=transport-tcp rtcp_mux=yes rtp_symmetric=yes force_rport=yes direct_media=no rewrite_contact=yes
[1001] type=auth auth_type=userpass password=123456 username=1001
[1001] type=aor max_contacts=1 remove_existing=yes
|
相关客户端下载及使用
这里的客户端下载后,先配置上边部署的服务,然后就可以打电话了。
常用客户端
包括 安卓,linux桌面版,MacOS,Windows 等版本。
从 官方release链接 下载即可。
下载后,配置 服务端地址,用户名,密码 即可使用
linux命令行
个人尝试,使用 baresip 效果更好。
linphonec
BASH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
apt update apt install -y linphone
export ALSA_CARD=1 export ALSA_PCM=plughw:1,0 export ALSA_PCM_CAPTURE=plughw:1,0 export ALSA_PCM_PLAYBACK=plughw:1,0
linphonecsh init
linphonecsh generic "unregister"
linphonecsh generic "register sip:1002@test.212490197.xyz:15061;transport=tcp sip:test.212490197.xyz:15061;transport=tcp 123456"
linphonecsh generic "status register"
linphonecsh generic "soundcard playback 2" linphonecsh generic "soundcard capture 2"
linphonecsh generic "call sip:10000@test.212490197.xyz:15061"
linphonecsh generic "terminate"
linphonecsh exit
linphonec
|
baresip
BASH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| sudo apt update sudo apt install -y baresip
sudo yum install epel-release -y sudo yum install baresip -y
export ALSA_CARD=1 export ALSA_PCM=plughw:1,0 export ALSA_PCM_CAPTURE=plughw:1,0 export ALSA_PCM_PLAYBACK=plughw:1,0
baresip
echo "<sip:1002@test.212490197.xyz:15061;transport=tcp>;auth_pass=123456;mediaenc=dtls_srtp" >> ~/.baresip/accounts
baresip
|
网页
可使用如下测试代码来处理
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"/> <title>Asterisk WebRTC Test</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.1.2/jssip.min.js"></script> <style> body { font-family: sans-serif; padding: 20px; } #status { color: gray; font-weight: bold; } .connected { color: green !important; } .disconnected { color: red !important; } </style> </head> <body> <h2>Asterisk WebRTC 控制台</h2> <div>状态: <span id="status">初始化...</span></div> <br> <button onclick="makeCall(8888)">拨打 8888 回声测试</button> <button onclick="makeCall(1001)">拨打 1001的电脑</button>
<audio id="remoteAudio" autoplay></audio> <div>远端音量: <span id="audioLevel">0</span></div>
<script> JsSIP.debug.enable('JsSIP:*');
const socket = new JsSIP.WebSocketInterface('wss://192.168.1.2:8089/ws'); const configuration = { sockets: [socket], uri: 'sip:10000@192.168.1.2', password: '123456', pcConfig: { rtcpMuxPolicy: 'require', iceServers: [] } };
const ua = new JsSIP.UA(configuration); ua.start();
ua.on('registered', () => { const status = document.getElementById('status'); status.innerText = '注册成功 (10000)'; status.className = 'connected'; });
ua.on('newRTCSession', (data) => { const session = data.session; console.log("新会话:", session.direction);
if (session.direction === 'incoming') { session.answer({ mediaConstraints: { audio: true, video: false } }); }
session.on('peerconnection', (e) => { const pc = e.peerconnection;
pc.ontrack = (event) => { console.log("检测到远端 track:", event.streams[0].getAudioTracks()); const remoteAudio = document.getElementById('remoteAudio'); if (!remoteAudio.srcObject || remoteAudio.srcObject !== event.streams[0]) { remoteAudio.srcObject = event.streams[0]; remoteAudio.volume = 1.0; remoteAudio.muted = false; remoteAudio.play().catch(err => console.warn('播放音频错误:', err));
try { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const source = audioCtx.createMediaStreamSource(event.streams[0]); const analyser = audioCtx.createAnalyser(); analyser.fftSize = 256; source.connect(analyser);
const dataArray = new Uint8Array(analyser.frequencyBinCount); function updateAudioLevel() { analyser.getByteFrequencyData(dataArray); let sum = 0; for (let i = 0; i < dataArray.length; i++) sum += dataArray[i]; const avg = sum / dataArray.length / 128; document.getElementById('audioLevel').innerText = avg.toFixed(3); requestAnimationFrame(updateAudioLevel); } updateAudioLevel(); } catch (err) { console.warn('无法创建音量监听:', err); } } }; });
session.on('ended', () => { console.log("呼叫结束"); const remoteAudio = document.getElementById('remoteAudio'); remoteAudio.srcObject = null; document.getElementById('audioLevel').innerText = '0'; });
session.on('failed', (e) => console.log("呼叫失败:", e.cause)); });
function makeCall(number) { ua.call(`sip:${number}@192.168.1.2`, { mediaConstraints: { audio: true, video: false }, pcConfig: { rtcpMuxPolicy: 'require', iceServers: [] } }); } </script>
</body> </html>
|
常识
- 打电话的过程中,传输速度为 64kb/s或32kb/s 就可以保证不卡。
问题记录
websocket的配置
公网没声音