☰
Chrome浏览器网页版SIP客户端:从注册到通话的完整实现指南
2026/10/5 9:52:12 网站建设 项目流程

简介:这份资源面向希望基于浏览器构建呼叫中心或网页软电话的开发者,提供了一套可直接运行的Chrome网页版SIP客户端示例。它解决的是传统呼叫中心依赖桌面应用或专用电话设备的问题,让语音通话、视频通话与消息传递直接在网页内完成,适合具备一定前端与WebRTC基础的开发者快速验证原型。压缩包共25个文件,约426KB,以14个JavaScript脚本为核心,配合2个CSS样式、5个PNG图标、3个WAV提示音和1个HTML入口页,覆盖界面渲染、SIP信令交互与通话反馈音效等环节。目前已有593人学习下载。资源内含SIPml-api.js通信库、Bootstrap响应式界面资源以及拨号音、回铃音、按键音等音频素材,读者可据此理解注册、呼叫、挂断等流程,并在此基础上扩展出安全、可定制的Web通信系统。

1. 拆开这个压缩包:Chrome 里的 SIP 网页客户端到底解决什么问题

呼叫中心坐席的电脑上,装软电话这件事一直很尴尬。装原生客户端吧,IT 要逐台部署、升级、处理驱动冲突;不装吧,坐席又得在浏览器和话务系统之间来回切。标题里这个「chrome浏览器网页版SIP」压缩包,本质就是把 SIP 协议栈塞进 Chrome,让坐席打开一个网页就能注册分机、接打电话,不用装任何本地程序。它面向的是呼叫中心、客服团队、外呼系统集成方,以及想把话务能力嵌进现有 Web 后台的开发者。

核心逻辑不复杂:浏览器通过 WebRTC 拿到麦克风和音频通道,用 JavaScript 实现的 SIP 协议栈(常见的是 SIP.js 或 JsSIP)跟 SIP 服务器做注册和信令交互,媒体流走 WebRTC 的 SRTP。Chrome 在这里扮演的是运行容器,不是通信协议本身。压缩包里通常包含前端页面、SIP 协议栈库、信令配置和呼叫控制逻辑。你拿到它之后,真正要搞清楚的只有三件事:SIP 服务器怎么配、Chrome 的 WebRTC 权限怎么放、呼叫流程怎么跟你的业务系统对接。下面按这个顺序拆。

2. 网页 SIP 客户端的信令链路:从 Chrome 到 SIP 服务器要过几道关

2.1 WebRTC 负责媒体,SIP over WebSocket 负责信令

很多人第一次接触网页版 SIP,会以为浏览器直接说 SIP 协议就行了。不是的。Chrome 不认识 UDP 上的 SIP 信令,它只认 WebSocket。所以网页 SIP 客户端跟服务器之间的信令通道是 SIP over WebSocket(RFC 7118),默认走 80 或 443 端口,用ws://或wss://连接。媒体通道则是 WebRTC 自己协商出来的,走 SRTP,跟信令通道完全分开。

这意味着你的 SIP 服务器必须支持 WebSocket 传输。常见的 Asterisk、FreeSWITCH、Kamailio 都支持,但默认配置里 WebSocket 往往是关着的。以 FreeSWITCH 为例,需要在internalprofile 里确认ws-binding和wss-binding参数已经打开。Asterisk 则要在http.conf里启用enabled=yes,并在 SIP 配置里加上transport=ws或transport=wss。

提示:如果服务器只开了 UDP 5060 而没开 WebSocket,网页客户端会一直卡在「连接中」,Chrome 控制台里能看到 WebSocket 握手失败的报错。

2.2 注册流程:Authorization 头是怎么算出来的

网页 SIP 客户端的注册跟普通软电话一样,走 REGISTER 方法。但浏览器环境下有一个容易翻车的点:SIP 摘要认证的Authorization头需要计算 MD5,而浏览器没有原生的 MD5 接口。SIP.js 和 JsSIP 内部用 JavaScript 实现了 MD5,所以你在配置里填用户名和密码就行,不用自己算。

注册的完整链路是这样的:客户端先发一个不带认证信息的 REGISTER,服务器返回 401 并带上WWW-Authenticate头,里面包含 realm 和 nonce。客户端用 username、password、realm、nonce、uri 这几个值算出 response,再发一次带Authorization头的 REGISTER。服务器验证通过后返回 200 OK,注册生效。

这里有个参数必须注意:expires。它决定注册有效期,单位是秒。设太短,客户端要频繁重注册,增加服务器压力;设太长,分机状态更新不及时。呼叫中心场景一般设 300 到 600 秒比较稳。SIP.js 里对应的配置项是registerExpires,JsSIP 里是register_expires。

2.3 呼叫建立:INVITE 到 200 OK 的完整交互

注册成功只是第一步,真正打电话走的是 INVITE 流程。主叫方发 INVITE,被叫方返回 100 Trying 表示收到了,然后 180 Ringing 表示正在振铃,接听后返回 200 OK。主叫方收到 200 OK 后发 ACK 确认,媒体流开始传输。挂断时任意一方发 BYE,对方回 200 OK,会话结束。

在 Chrome 里,这个流程的每一步都对应 SIP.js 的事件回调。比如onInvite触发时有来电,onAccepted触发时对方接听了,onTerminated触发时会话结束。你需要在回调里更新页面 UI,比如显示来电弹窗、切换通话状态图标、播放振铃音。这些 UI 逻辑不在 SIP 协议栈里,得自己写。

一个常见的坑是:Chrome 要求页面必须处于「安全上下文」才能调用getUserMedia。也就是说,你的网页必须走 HTTPS,或者跑在localhost上。如果部署在内网用 HTTP 访问,麦克风权限会被 Chrome 直接拒绝,连弹窗都不弹。解决办法是给内网服务器配一个自签名证书,或者用 Chrome 的--unsafely-treat-insecure-origin-as-secure启动参数临时绕过(仅限调试)。

3. 在 Chrome 里跑通最小可用的网页 SIP 客户端

3.1 环境准备与依赖确认

拿到压缩包后,先别急着打开 HTML 文件。你需要确认三样东西:一个支持 WebSocket 的 SIP 服务器、一个能跑静态页面的 Web 服务器、以及 Chrome 的版本。Chrome 从 80 版本开始对 WebRTC 和 WebSocket 的安全策略收紧了不少,建议用 100 以上的版本。如果坐席电脑还在跑 Win7 上的 Chrome 109,基本功能没问题,但要注意 109 是 Win7 能装的最后一个大版本,后续不再更新。

Web 服务器用 Nginx 或 Python 自带的http.server都行。如果只是本地测试,在压缩包解压目录下执行:

python3 -m http.server 8080

然后浏览器访问http://localhost:8080。注意必须是localhost,换成127.0.0.1也行,但换成局域网 IP 就会触发安全上下文限制。

3.2 SIP.js 初始化与注册配置

压缩包里的前端代码通常已经引入了 SIP.js 或 JsSIP。以 SIP.js 为例,核心初始化代码如下:

// 创建 UserAgent,配置 SIP 服务器地址和用户信息 const ua = new SIP.UserAgent({ uri: SIP.UserAgent.makeURI('sip:1001@your-sip-server.com'), transportOptions: { server: 'wss://your-sip-server.com:7443' // WebSocket 信令地址 }, authorizationUsername: '1001', // SIP 认证用户名 authorizationPassword: 'your-password', // SIP 认证密码 register: true, // 启动时自动注册 registerExpires: 300, // 注册有效期 300 秒 sessionDescriptionHandlerFactoryOptions: { constraints: { audio: true, // 只取音频,呼叫中心场景不需要视频 video: false } } }); // 注册状态回调 ua.delegate = { onConnect: () => console.log('WebSocket 已连接'), onDisconnect: (error) => console.error('连接断开', error), onRegistered: () => console.log('SIP 注册成功'), onUnregistered: () => console.log('SIP 注销'), onInvite: (invitation) => { // 来电处理:弹出接听界面 console.log('来电来自', invitation.remoteIdentity.uri.toString()); } }; // 启动 UserAgent ua.start().then(() => ua.register());

这段代码做了四件事:建立 WebSocket 连接、发送 REGISTER 注册、监听注册状态、处理来电事件。server参数填的是 SIP 服务器的 WebSocket 地址,不是 UDP 地址。registerExpires设 300 秒,意味着客户端每 300 秒会重新注册一次,服务器端也要把对应的超时时间设成差不多的值,否则会出现客户端以为注册着、服务器已经踢掉的情况。

3.3 发起呼叫与接听的处理逻辑

注册成功后,发起呼叫的代码大致如下:

// 发起呼叫 async function makeCall(targetNumber) { const target = SIP.UserAgent.makeURI(`sip:${targetNumber}@your-sip-server.com`); const inviter = new SIP.Inviter(ua, target); // 监听呼叫状态 inviter.stateChange.addListener((state) => { switch (state) { case SIP.SessionState.Establishing: console.log('正在呼叫...'); break; case SIP.SessionState.Established: console.log('通话已建立'); // 此时媒体流已经通了,可以挂载音频 break; case SIP.SessionState.Terminated: console.log('通话已结束'); break; } }); await inviter.invite(); // 发送 INVITE }

接听来电的逻辑类似,在onInvite回调里拿到invitation对象后,调用invitation.accept()接听,或者invitation.reject()拒接。接听后同样要监听stateChange来更新 UI。

这里有一个实操中很容易忽略的点:音频元素的挂载。WebRTC 的媒体流不会自动播放,你需要把sessionDescriptionHandler里的remoteMediaStream挂到一个<audio>标签上,并且设置autoplay属性。Chrome 的自动播放策略要求用户至少有一次交互(点击、触摸)之后才允许播放音频,所以通常会在「接听」按钮的点击事件里做这个操作。

3.4 关键参数速查表

参数作用推荐值注意事项
registerExpires注册有效期300 秒服务器端超时时间要匹配
serverWebSocket 信令地址wss://开头必须用 wss 或 localhost 下的 ws
constraints.audio音频采集开关true设为 false 则无法通话
sessionTimers会话超时定时器默认开启防止僵尸会话
iceServersNAT 穿透配置按需配置跨网段呼叫必须配 STUN/TURN

4. 避坑与排查:网页 SIP 客户端最容易翻车的五个地方

4.1 现象:注册一直失败,控制台报 401 后没有后续

原因通常是密码错了,或者authorizationUsername跟 SIP URI 里的用户名不一致。有些 SIP 服务器要求认证用户名是完整的分机号,有些则要求带域名后缀。解决方法是先在服务器端用sip show peers或类似命令确认分机配置,再对照前端代码里的authorizationUsername和uri是否匹配。另外注意密码里如果有特殊字符,在 JavaScript 字符串里要转义。

4.2 现象:能注册成功,但一打电话就没声音

这是最典型的媒体问题。先检查iceServers有没有配。如果客户端和服务器在不同网段,没有 STUN/TURN 的话,WebRTC 协商出来的候选地址可能不可达。其次检查 Chrome 的麦克风权限有没有给,在chrome://settings/content/microphone里确认当前页面没有被屏蔽。还有一个隐蔽的坑:某些服务器的external_media_address没配对外网 IP,导致 SDP 里的媒体地址是内网地址,客户端根本连不上。

4.3 现象:Chrome 提示「无法访问麦克风」,连权限弹窗都不出

这就是前面说的安全上下文问题。Chrome 要求getUserMedia必须在 HTTPS 或localhost下调用。如果你用http://192.168.x.x访问,Chrome 会直接拒绝,不给弹窗机会。解决办法有两个:给 Web 服务器配 HTTPS 证书,或者用 Chrome 启动参数--unsafely-treat-insecure-origin-as-secure=http://192.168.x.x临时放行。生产环境必须用 HTTPS。

4.4 现象:通话几十秒后自动断线

大概率是会话定时器(Session Timer)在起作用。SIP 协议里有一个Session-Expires头,默认可能是 90 秒或 180 秒。如果一方没有在超时前发 UPDATE 或 re-INVITE 刷新会话,另一方就会主动发 BYE 断线。解决方法是确认 SIP.js 的sessionTimers配置跟服务器端一致,或者直接在服务器端把会话超时设长一点。呼叫中心场景一般设 1800 秒以上。

4.5 现象:Win7 上的 Chrome 109 打开页面白屏

Chrome 109 是 Win7 支持的最后一个版本,它不支持一些较新的 JavaScript 语法和 Web API。如果压缩包里的 SIP.js 用了 ES2020 以上的语法(比如可选链?.或空值合并??),在 Chrome 109 上会直接报语法错误导致白屏。解决办法是换用编译后的 ES5 版本,或者在构建时加 Babel 转译。如果坐席电脑还在跑 Win7,这一点必须提前确认。

5. 把网页 SIP 客户端嵌进呼叫中心业务系统的三个进阶技巧

5.1 用事件总线解耦 SIP 层和 UI 层

很多初版实现会把 SIP.js 的回调直接写在页面逻辑里,导致话务状态和业务状态搅在一起。更稳的做法是加一层事件总线:SIP 层只负责发事件(call:incoming、call:answered、call:ended),UI 层和业务层各自订阅。这样以后换 SIP 库或者加新的业务逻辑,不用动核心通话代码。我一般用一个简单的发布订阅对象就够了,不需要引入重型框架。

// 极简事件总线 const bus = { handlers: {}, on(event, fn) { (this.handlers[event] = this.handlers[event] || []).push(fn); }, emit(event, data) { (this.handlers[event] || []).forEach(fn => fn(data)); } }; // SIP 层只发事件 ua.delegate.onInvite = (invitation) => { bus.emit('call:incoming', { from: invitation.remoteIdentity.uri.toString() }); }; // UI 层订阅 bus.on('call:incoming', ({ from }) => showIncomingPopup(from));

5.2 用 localStorage 缓存注册状态,减少重复注册

坐席页面刷新是常事,每次刷新都重新注册一遍 SIP 会拖慢体验。可以在注册成功后把分机号和注册时间存到localStorage,页面加载时先读缓存,如果距离上次注册不到registerExpires的一半,就跳过重新注册直接复用。注意这只能优化体验,不能替代服务器端的注册状态管理。

5.3 用 Chrome 的chrome://webrtc-internals做通话质量排查

这个页面是排查 WebRTC 问题的黑匣子。通话过程中打开chrome://webrtc-internals,能看到当前会话的完整统计:丢包率、抖动、往返延迟、码率、编解码器。如果坐席反馈「声音断断续续」,先看丢包率和抖动,丢包超过 5% 或者抖动超过 50ms,基本就是网络问题,不是代码问题。这个工具比在代码里打日志高效得多,建议每个做网页 SIP 的人都把它加到书签栏。

最后说一个我自己的习惯:每次改完 SIP 相关配置,先不急着在业务系统里测,而是用一个最简 HTML 页面单独验证注册和通话。业务系统的代码路径太长,出了问题不好定位。把最简页面跑通之后,再把配置搬过去,能省掉大量排查时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询