前端开发者独立构建呼叫中心系统时,软电话库的选择往往是第一个需要迈过的坎。本文一次性讲清 SIP.js、JsSIP、Verto 三种主流方案,并给出两种常见架构下的最终选型建议。

引言

如果你是一名前端开发人员,正在规划一套基于 FreeSWITCH 的呼叫中心系统,那么“如何在浏览器里实现软电话”这个问题一定会摆在面前。目前业界主流的解决方案有三条路线:SIP.jsJsSIP 以及 FreeSWITCH 自带的 Verto

这三种方案各有特点,而且随着系统架构的不同(单机 FreeSWITCH 还是引入 Kamailio 负载均衡),最佳的选型结论也会发生逆转。本文将从零开始,详细拆解三者的技术背景、核心能力与优缺点,并通过对比表格和实际架构场景,帮你一次性搞清楚到底该选哪个。


第一部分:三种技术详细介绍

一、SIP.js:现代化浏览器 SIP 客户端

1. 背景与定位

SIP.js 由 OnSIP 团队于 2014 年从 JsSIP fork 而来。OnSIP 是一家商业 VoIP 服务商,他们在使用 JsSIP 开发 WebRTC 产品时发现其无法满足复杂业务场景的需求,因此决定独立维护并持续投入开发。如今 SIP.js 已成为社区最活跃的浏览器 SIP 库之一。

2. 技术特点

1)完整的 SIP over WebSocket 实现:在浏览器中通过 WebSocket 传输 SIP 协议,与 SIP 服务器完成信令交互。

2)TypeScript 原生支持:整个库使用 TypeScript 编写,类型定义完善,对现代化前端开发极为友好。

3)分层 API 设计

① SimpleUser:高层简化 API,仅需几行代码即可实现软电话。

② 完整底层 API:支持呼叫保持、转移(Transfer)、早期媒体(Early Media)、会话恢复等高级 SIP 特性。

4)活跃维护:持续更新,GitHub 活跃度高,文档详尽。

3. 核心代码示例
import { Web } from 'sip.js';

const simpleUser = new Web.SimpleUser('wss://your-server:8083/ws', {
    aor: 'sip:1001@your-domain.com',
    media: {
        constraints: { audio: true, video: false },
        remote: { audio: document.getElementById('remoteAudio') }
    }
});

// 呼叫流程
await simpleUser.connect();    // WebSocket 连接
await simpleUser.register();   // SIP 注册
await simpleUser.call('sip:customer@your-domain.com');  // 发起呼叫
await simpleUser.hangup();     // 挂断
4. 优缺点总结
优点 缺点
✅ TypeScript 原生支持,开发体验优秀 ⚠️ SIP 协议较重,复杂网络下需配合 TURN/STUN 服务器
✅ API 设计现代化,SimpleUser 降低开发门槛 ⚠️ 对 SIP 协议有一定理解要求
✅ 完整支持高级 SIP 特性(转移、保持等)
✅ 与 FreeSWITCH 兼容性良好
✅ 社区活跃,文档完善

二、JsSIP:最早的浏览器 SIP 客户端

1. 背景与定位

JsSIP 由 José Luis Millán 和 Iñaki Baz Castillo(同时也是 RFC 7118 的作者)开发,是第一个完整的浏览器端 SIP 协议栈。它开创了在浏览器中通过 WebSocket 实现 SIP 通信的先河,是整个领域的奠基者。

2. 技术特点

1)RFC 7118 标准实现:由标准文档的作者亲自编写,协议规范性极高。

2)跨平台支持:既可在浏览器中运行,也支持 Node.js 环境。

3)轻量级设计:核心库体积较小,专注于基础 SIP 功能。

4)广泛的 SIP 服务器兼容性:与 OverSIP、Kamailio、Asterisk 等主流 SIP 服务器均可配合。

3. 核心代码示例
var socket = new JsSIP.WebSocketInterface('wss://sip.myhost.com');
var configuration = {
    sockets: [socket],
    uri: 'sip:alice@example.com',
    password: 'superpassword'
};

var ua = new JsSIP.UA(configuration);
ua.start();

var session = ua.call('sip:bob@example.com', {
    eventHandlers: {
        'confirmed': function(e) { console.log('call answered'); },
        'ended': function(e) { console.log('call ended'); }
    }
});
4. 优缺点总结
优点 缺点
✅ 历史最悠久,协议规范性高 ❌ 与 FreeSWITCH 直连存在已知兼容性问题(后文详述)
✅ 轻量级,核心库体积小 ❌ FreeSWITCH 核心开发者曾明确表示“JSSIP IS KNOWN TO BE BROKEN AND DOES NOT RELIABLY WORK”
✅ 由 RFC 作者开发,SIP 标准理解深刻 ❌ 项目活跃度已被 SIP.js 超越
✅ 官方文档明确列出与 Kamailio 兼容 ⚠️ TypeScript 支持较弱
⚠️ 高级 SIP 特性支持不如 SIP.js 完整

⚠️ 关于 JsSIP 与 FreeSWITCH 兼容性的重要说明
FreeSWITCH 核心开发者 Mike Jerris 在官方邮件列表中多次指出:“JSSIP IS KNOWN TO BE BROKEN AND DOES NOT RELIABLY WORK FOR ANYONE I HAVE SEEN POST ABOUT IT”。这个警告针对的是 JsSIP 直接连接 FreeSWITCH 的 mod_sofia 的场景。如果架构中引入 Kamailio 做中间层,这一问题可以被规避。我们会在后文详细分析。


三、Verto:FreeSWITCH 原生的 WebRTC 通信协议

1. 背景与定位

Verto(全称 Verto RTC)是由 FreeSWITCH 核心团队 专门为 WebRTC 场景设计的原生通信协议。它不是基于 SIP 的,而是 FreeSWITCH 内置的一个 endpoint 模块(mod_verto),使用 JSON-RPC over WebSocket 进行信令交互。名字“Verto”源自拉丁语“通信”或“信息传播”,寓意简化现代设备间的通信。

2. 技术特点

1)FreeSWITCH 原生协议:与 FreeSWITCH 同根生,由核心团队开发维护,兼容性无可比拟。

2)JSON-RPC over WebSocket:使用简洁的 JSON 格式进行信令交互,而非复杂的 SIP 协议。

3)原生支持高级功能:会议(Conference)、实时状态更新(LiveArray)、消息推送等开箱即用。

4)简化 Web 开发:设计目标就是让 Web 开发者能用最简单的代码实现复杂的实时通信功能。

5)社区 TypeScript 支持:社区项目 vertojs 提供了 TypeScript 类型定义。

3. 核心代码示例
import { Verto } from 'vertojs';

const verto = new Verto({
    transportConfig: {
        socketUrl: 'wss://your-freeswitch:8082',
        login: '1001',
        passwd: 'password'
    },
    rtcConfig: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }
});

await verto.login();

// 获取本地媒体流
const localStream = await navigator.mediaDevices.getUserMedia({ audio: true });

// 发起呼叫
const call = verto.call(localStream.getTracks(), '9664');

// 监听远端音频
call.subscribeEvent('track', (track) => {
    if (track.kind === 'audio') {
        const stream = new MediaStream();
        stream.addTrack(track);
        document.getElementById('remoteAudio').srcObject = stream;
    }
});

// 接听来电
verto.subscribeEvent('invite', (call) => {
    call.answer(localStream.getTracks());
});
4. FreeSWITCH 服务端配置示例
<!-- conf/autoload_configs/verto.conf.xml -->
<configuration name="verto.conf">
  <profiles>
    <profile name="mine">
      <param name="bind-local" value="0.0.0.0:8082" secure="true"/>
      <param name="secure-combined" value="/path/to/wss.pem"/>
      <param name="userauth" value="true"/>
    </profile>
  </profiles>
</configuration>

<!-- 用户配置中启用 Verto -->
<user id="1001">
  <params>
    <param name="verto-context" value="public"/>
    <param name="jsonrpc-allowed-methods" value="verto"/>
  </params>
</user>
5. 优缺点总结
优点 缺点
✅ 与 FreeSWITCH 完美兼容,由核心团队开发维护 ⚠️ 生态相对较小,社区资源不如 SIP.js 丰富
✅ 协议简洁(JSON over WebSocket),开发门槛低 ⚠️ 仅适用于 FreeSWITCH 平台,无法与其他 SIP 服务器互通
✅ 原生支持会议、实时状态更新等高级功能 ⚠️ 社区维护的 TypeScript 版本(vertojs)版本号较低(0.0.5),功能尚在完善中
✅ 避免 SIP 协议的复杂性和 NAT 穿透问题
✅ FreeSWITCH 官方强烈推荐

第二部分:三方对比总览

对比维度 SIP.js JsSIP Verto
本质 SIP over WebSocket 客户端库 SIP over WebSocket 客户端库 FreeSWITCH 原生 JSON-RPC 协议
开发者 OnSIP 团队(商业 VoIP 服务商) RFC 7118 作者团队 FreeSWITCH 核心团队
与 FreeSWITCH 直连兼容性 ✅ 良好 ❌ 存在已知严重问题 ✅✅ 完美(官方原生)
官方推荐度(单机) 可接受,但更推荐 Verto ❌ 官方明确不推荐 ✅✅ 官方强烈推荐
协议复杂度 高(完整 SIP 协议栈) 高(完整 SIP 协议栈) 低(JSON-RPC 简洁协议)
TypeScript 支持 ✅ 一流(原生 TS 编写) ⚠️ 较弱 ✅ 有社区版本(vertojs)
高级功能 保持/转移/早期媒体等完整 SIP 特性 基础 SIP 功能 会议、LiveArray、消息推送等原生支持
生态与社区 ✅ 活跃,文档完善 ⚠️ 维护模式,活跃度降低 ⚠️ 生态较小,但官方文档完善
跨平台 浏览器 浏览器 + Node.js 浏览器(FreeSWITCH 服务端)
学习曲线 中等(需理解 SIP 概念) 中等(需理解 SIP 概念) 低(JSON API,无 SIP 负担)
NAT 穿透 需配合 TURN/STUN 需配合 TURN/STUN 同样需 TURN/STUN,但协议层面更简洁

第三部分:两种架构下的选型建议

场景一:WebRTC 与 FreeSWITCH 直接交互(单机或简单集群)

在这个场景下,浏览器直接通过 WebSocket 与 FreeSWITCH 通信,FreeSWITCH 的 mod_verto 或 mod_sofia 直接处理信令。

1. 推荐:Verto

1)官方原生支持:由 FreeSWITCH 核心团队设计,是官方为 WebRTC 场景量身定制的解决方案。

2)开发效率最高:JSON 比 SIP 信令简单得多,前端开发者无需深入理解 SIP 协议即可上手。

3)功能完备:呼叫中心所需的会议、状态同步等功能开箱即用。

4)规避兼容性问题:不存在 JsSIP 直连 FreeSWITCH 的稳定性风险。

5)如果因某些原因无法使用 Verto(例如需要对接非 FreeSWITCH 的 SIP 服务器,或团队对 SIP 协议有成熟经验),则 SIP.js 是稳妥的第二选择。

2. 不推荐:JsSIP(直连场景)

理由如上一节所述,官方明确指出其与 FreeSWITCH 直连存在根本性问题,不应在直连架构中使用。


场景二:WebRTC 与 FreeSWITCH 之间引入 Kamailio 做负载均衡

1. 架构示意图
┌──────────┐  WSS (SIP over WS)  ┌──────────────┐   SIP (UDP/TCP)  ┌─────────────┐
│  Browser │ ──────────────────▶│   Kamailio   │ ───────────────▶│ FreeSWITCH 1 │
│  (SIP.js │                     │  (负载均衡)   │                  ├─────────────┤
│   或     │ ◀──────────────────│              │ ◀───────────────│ FreeSWITCH 2 │
│  JsSIP)  │                     └──────────────┘                  └─────────────┘
└──────────┘                                                                                
2. 为什么 Verto 不再适用?

Kamailio 是一个 SIP 服务器,其负载均衡、路由、Dispatcher 等核心模块都是为 SIP 协议设计的。Verto 使用 JSON-RPC over WebSocket,不是 SIP 协议,因此无法被 Kamailio 识别和处理。虽然 Kamailio 可以转发 WebSocket 连接,但它无法解析 Verto 的信令内容来做智能路由。在此架构下,Verto 被迫出局。

3. 推荐:JsSIP

1)官方明确支持 Kamailio:JsSIP 的官方文档在“生态项目”章节中明确列出 Kamailio 作为兼容的 SIP 服务器,这是非常强的背书。

2)与 Kamailio 生态高度契合:JsSIP 的作者 Iñaki Baz Castillo 同时也是 RFC 7118 和 OverSIP(专为 WebSocket 设计的 SIP 代理)的作者,他对 Kamailio 生态的理解非常深入。

3)规避直连问题:通过 Kamailio 中转,JsSIP 与 FreeSWITCH 直连的已知问题被彻底规避。Kamailio 负责 WebSocket 连接管理和 SIP 协议转换,与 FreeSWITCH 之间使用标准的 UDP/TCP SIP 通信,稳定性有保障。

4)轻量级设计:代码量较小,浏览器加载性能更优。

4. 备选:SIP.js

SIP.js 同样完全兼容 Kamailio,如果你或团队:

1)对 TypeScript 有强需求;

2)需要 SIP.js 的 SimpleUser API 来快速开发;

3)未来可能脱离 FreeSWITCH,对接其他 SIP 服务器;

那么 SIP.js 可以作为备选方案,它与 JsSIP 在这个架构下的差距很小,更多是个人偏好问题。


结语

架构 首选方案 备选方案
单机 FreeSWITCH(或简单集群,无 Kamailio) Verto SIP.js
Kamailio + FreeSWITCH 集群(运营商级负载均衡) JsSIP SIP.js

选择软电话库时,建议先确定你的系统最终架构:是简单部署一个 FreeSWITCH 就上线,还是需要支持高并发、多地容灾的集群方案。架构决定技术路线,技术路线决定最终选型。

如果你正在构建呼叫中心系统,并且上述方案仍无法覆盖你的特定需求(例如需要与特定的 PBX 对接、需要使用特定协议特性等),欢迎在评论区留言交流。


本文基于 FreeSWITCH 官方社区讨论、各项目 GitHub 仓库及作者实际项目经验整理,力求客观准确。如有疑问或指正,请不吝赐教。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐