ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于oSIP协议栈从零搭建轻量级SIP服务器实战指南

基于oSIP协议栈从零搭建轻量级SIP服务器实战指南 做SIP这块也有些年头了从最初拿别人的开源服务器改配置到后来被逼着自己去翻协议栈源码一路踩坑无数。如果你问我“想自己搭一个SIP服务器最踏实的入门路径是什么”我的答案一直是别一上来就套用现成的大型软交换先拿 oSIP 这个协议栈从最底层把一个能跑通的轻量服务器写出来。这篇就用 oSIP 2.3.1.0 这个版本完整走一遍流程包括注册、呼叫、RTP 媒体流配合以及我这些年实测下来最常踩的坑。这篇东西适合三类人一是刚接触 VoIP 想搞懂 SIP 服务器内部原理的开发者二是需要在局域网内做一个轻量注册/呼叫转发服务的运维或嵌入式工程师三是被各种开源服务器配置折腾得头疼、想自己掌控协议细节的人。读完你能获得一个可以真实注册软电话并完成通话的极简 SIP 服务器以及一套能解决 90% 常见问题的排查思路。1. 动手前的思路拆解为什么选 oSIPSIP 服务器到底在干什么1.1 用生活化类比理解 SIP 信令与 RTP 媒体流很多人一开始就被 SIP 和 RTP 两个词绕晕我习惯用一个打电话的比喻来解释SIP 协议相当于“打电话过程中”的一系列动作指令——拨号、响铃、接听、挂断这些都是信令而 RTP 协议相当于“通话过程中”真正从嘴里说出去的声音——这是媒体流。信令负责把通话建立起来媒体流负责把声音传过去两者互相配合但又彼此独立。具体到一次普通通话你的软电话比如 MicroSIP、Linphone先发一个 INVITE 请求给服务器服务器根据被叫号码找到被叫方并转发这个请求被叫方振铃后接听回复 200 OK这个响应里带着一个 SDP 报文里面写清楚了“我用什么编解码、我的 RTP 端口开在哪个数字上”主叫方收到后回复 ACK此时信令面的工作完成双方开始直接往对方的 IP 和端口发送 RTP 音频包。通话结束任一方发 BYE对方回 200 OK整个流程结束。明白了这个协作关系你就知道搭建 SIP 服务器时信令处理和媒体流转发是两个完全不同的关注点。oSIP 2.3.1.0 这个协议栈解决的是前者——帮你解析、构造、事务管理 SIP 消息而 RTP 这层一般我们用 ORTP、JRTPLib 或者直接裸 socket 发送 RFC3550 定义的 RTP 包即可。用 oSIP 搭服务器本质上就是写一段“SIP 消息的智能转发器”。1.2 为什么要用 oSIP 2.3.1.0而不是直接自己写协议解析我自己早期也试过直接用 socket 监听 5060 端口然后自己截取字符串来解析 SIP 头字段。刚开始还挺爽但很快就会发现两个问题一是 SIP 协议头字段的各种变体太复杂Via 可能有多个Contact 可能是引号格式分号后面一堆参数解析漏一个就会出诡异 Bug二是事务层的状态机非常容易错比如重传、超时、事务匹配这类逻辑自己写难免顾此失彼。oSIPGNU oSIP是一个老牌开源 SIP 协议栈它把 SIP 协议的解析、事务状态机、对话管理等底层细节都封装好了。2.3.1.0 是很多嵌入式平台和老项目中广泛使用的稳定版本接口虽老但足够清晰。用它开发的好处在于你只需要关注业务逻辑——比如“收到 REGISTER 后要不要存用户地址”“收到 INVITE 后往哪里转”而不用关心底层字节流怎么拼、重传怎么处理。这就像一个做嵌入式开发的朋友说的oSIP 把 SIP 世界的脏活累活都干了你要做的只是填业务空白。1.3 整体架构一个极简 SIP 服务器要包含哪些模块在我们动手之前先在脑子里把服务器的模块拆一下。别看市面上那些软交换功能花里胡哨剥掉外壳一个能注册、能呼叫的最小 SIP 服务器只需要四块UDP 监听模块监听 5060 端口接收来自软电话的 SIP 请求/响应消息。这个可以用系统 socket 直接实现oSIP 会负责解析这些收到的字节流。用户注册管理模块软电话发 REGISTER 过来时从中解析出它的地址AOR比如 sip:1001192.168.1.10、Contact 地址实际监听地址、注册有效期存进一张内存表里。呼叫路由模块收到 INVITE 请求时从 Request-URI 里提取被叫号码去注册表里找对应联系地址找到了就把 INVITE 转发过去找不到就回 404 Not Found。事务与对话管理层这部分主要利用 oSIP 的回调机制处理事务状态变化比如收到 100 Trying、180 Ringing 要转发给主叫方收到 200 OK 也要原路转发。这个架构看起来简单但它能让整个 VoIP 通信的链路变得非常透明。你可以随时打印消息、观察路由逻辑这是直接用 Asterisk 这种巨无霸服务器难以做到的。2. 搭建环境准备与核心依赖编译2.1 系统环境与工具链我这次用的环境是 Ubuntu 22.04CPU 无所谓虚拟机也可以跑。为什么选 Linux 而不是 Windows因为 oSIP 的编译、调试在 Linux 下最顺gdb 跟代码也方便。不过你在 macOS 或 Windows 上用 WSL 也可以思路完全一样。需要准备的东西如下gcc / make基本的编译工具链Ubuntu 下直接sudo apt install build-essential。pkg-config用于后续程序链接库时自动匹配头文件和库路径。Wireshark必装抓包分析 SIP 消息全靠它。这里多说一句调试 SIP 服务器时 Wireshark 的用途比日志大得多很多日志没印出来的协议细节抓包一看就明白了。软电话Windows 上用 MicroSIPAndroid 上用 ZoiperiOS 上用 Linphone任选两个即可用于模拟两个分机互相呼叫。2.2 编译 oSIP 库和 eXosip用 oSIP 开发 SIP 服务器时通常我们直接用 libosip2 就够用了。但如果你想更快速地处理注册、呼叫事务会搭配 libeXosip 一起用。eXosip 在 oSIP 之上封装了一层更友好的会话控制接口很多业务逻辑可以直接用 eXosip 的 API 完成。在 2.3.1.0 这个版本时代对应的 eXosip 一般是 3.x 系列。编译安装步骤非常简单下载源码后依次执行tar -zxvf libosip2-2.3.1.0.tar.gz cd libosip2-2.3.1.0 ./configure --prefix/usr/local/osip make -j4 sudo make install如果你还需要 eXosip同样方式编译注意 configure 时指定头文件和库路径让它能找到 oSIP./configure --prefix/usr/local/osip --with-osip/usr/local/osip make -j4 sudo make install装完后记得把库路径加入系统配置不然运行程序时可能找不到动态库echo /usr/local/osip/lib | sudo tee /etc/ld.so.conf.d/osip.conf sudo ldconfig这里有一个很多新手容易忽略的细节configure 时最好指定--enable-debug这样生成的库带着调试符号后续用 gdb 回溯问题会舒服很多。生产环境再重新编译成 release 也不迟。2.3 初始化 oSIP 协议栈的关键选项在写业务代码之前先搞定协议栈初始化。oSIP 提供了一个全局初始化函数osip_init但使用 eXosip 时通常只需要初始化 eXosip 就行。伪代码大致是这样#include eXosip2/eXosip.h int main() { struct eXosip_t *ctx eXosip_malloc(); if (!ctx) return -1; int res eXosip_init(ctx); if (res ! OSIP_SUCCESS) { printf(eXosip init failed: %d\n, res); return -1; } res eXosip_listen_addr(ctx, IPPROTO_UDP, 0.0.0.0, 5060, AF_INET, 0); if (res ! OSIP_SUCCESS) { printf(listen failed: %d\n, res); return -1; } eXosip_set_user_agent(ctx, MySIPServer 1.0); // ... 主循环 }关键点是eXosip_listen_addr这个函数。0.0.0.0表示监听所有网卡接口端口是 5060。如果服务器有多个网卡而你想只监听某个具体 IP就写那个 IP 地址避免某个网卡不可达时出现奇奇怪怪的行为。我自己就遇到过服务器有两个网卡一个内网一个公网结果只监听内网 IP 后公网的分机死活注册不上最后检查才发现监听地址写错了。初始化完成后主循环通常就是一个事件循环。eXosip 的做法是用eXosip_event_wait阻塞等待事件拿到事件后用eXosip_event_type判断事件类型再分发处理。整体结构非常像一个 GUI 程序的消息循环理解了这个后面写业务逻辑就顺畅了。3. 核心功能实现注册、鉴权与呼叫路由3.1 实现 REGISTER 注册与在线状态管理注册功能是整个服务器的地基。软电话启动后会向服务器发 REGISTER 请求服务器要在 200 OK 响应里告诉它“我接受你的注册”。但光回 200 OK 不够服务器内部还必须记录“这个 AOR 当前在哪个 Contact 上”。用 eXosip 处理这个逻辑核心事件是EXOSIP_REGISTRATION_SUCCESS和EXOSIP_REGISTRATION_FAILURE。但这两个事件是给“注册客户端”用的当我们的程序作为服务器时需要监听更底层的事件EXOSIP_MESSAGE_NEW。因为服务器收到的是一个 REGISTER 请求它不是一个注册动作的发起者而是一个被请求的接收者。一个最小可用的实现思路在主循环中拿到EXOSIP_MESSAGE_NEW事件通过事件结构中的evt-request拿到 SIP 消息。判断osip_message_get_method(request)是不是 REGISTER。如果是解析 From 头里的 AOR 地址、Contact 头里的联系地址、Expires 头里的注册有效期。把映射关系写进一张哈希表AOR - 联系地址 有效期。构造一个 200 OK 响应调用eXosip_lock(ctx)加锁后发送。关键代码逻辑可以这样写if (evt-type EXOSIP_MESSAGE_NEW) { osip_message_t *req evt-request; if (MSG_IS_REGISTER(req)) { osip_from_t *from req-from; osip_contact_t *contact req-contacts; osip_expires_t *expires req-expires; const char *aor from-url-username; const char *contact_addr contact-url-host; // 将 aor - contact_addr 存入注册表 register_user(ctx, aor, contact_addr); // 构造 200 OK osip_message_t *answer NULL; eXosip_message_build_answer(ctx, answer, evt-tid, 200, NULL); eXosip_lock(ctx); eXosip_message_send_answer(ctx, evt-tid, 200, answer); eXosip_unlock(ctx); } }这段代码少了鉴权处理实际项目中如果注册端口暴露在公网必须加摘要鉴权Digest Authentication。但局域网内做学习测试先不加也问题不大。注册表我建议用一个支持超时清理的哈希表定期清掉过期的注册记录不然分机下线后旧的映射会一直占着位置导致后续呼叫被转到无人接听的地址上。3.2 实现 INVITE 呼叫与 RTP 媒体流配合注册只是第一步真正有意思的是 INVITE 呼叫。主叫分机向服务器发 INVITE服务器要把这个请求转到被叫分机。这里的核心逻辑是解析出被叫号码查注册表拿到被叫的 Contact 地址然后把 INVITE 转发过去。用 eXosip 做转发时的做法通常是构造一个新的 INVITE 请求这里要注意一个容易犯的错误不要直接复用收到的原请求。SIP 协议中 Via 头、Call-ID、From tag 都已经属于原事务直接改容易破坏事务匹配。正确做法是读取原 INVITE 中的必要信息重新构造一个 INVITE 发给被叫同时记录主叫事务 ID 与新的对话 ID 的对应关系用于后续 180、200 等响应的回传。具体流程收到EXOSIP_CALL_INVITE事件eXosip 已经把底层事务封装成 call 级别。调用eXosip_call_build_initial_invite构造一个新的 INVITE 请求把 Request-URI 设成被叫的地址把 SDP 体原样复制到新请求里。调用eXosip_call_send_initial_invite发送给被叫。当被叫回 180 Ringing 或 200 OK 时服务器要把这些响应转发回主叫让主叫知道被叫的状态。这里就涉及 SIP 与 RTP 配合的关键点INVITE 请求里携带的 SDP 报文描述的是主叫想收媒体流的地址和端口。被叫回复 200 OK 时SDP 报文描述的是被叫想收媒体流的地址和端口。信令服务器只负责把 SDP 报文原样传递媒体流本身并不经过服务器而是主被叫之间直连。所以你会发现如果你的软电话在 NAT 后面SDP 里写的 IP 可能是内网地址公网分机收到后无法直接访问——这时就需要 STUN、TURN 或者服务器做媒体代理。这个我在后面问题排查里再展开。这里给一个实操建议在实现转发逻辑时可以在每次转发 INVITE 前打印一次被叫的 SDP 端口同时在收到 200 OK 时打印一次主叫的 SDP 端口这样抓包时对照非常方便。case EXOSIP_CALL_INVITE: { osip_message_t *invite evt-request; char *to_user NULL; osip_uri_to_str(invite-req_uri, to_user); // 被叫号码 // 查注册表 const char *target_contact lookup_contact(to_user); if (!target_contact) { // 被叫不存在回复 404 osip_message_t *not_found NULL; eXosip_call_build_answer(ctx, evt-tid, 404, not_found); eXosip_call_send_answer(ctx, evt-tid, 404, not_found); break; } osip_message_t *new_invite NULL; eXosip_call_build_initial_invite(ctx, new_invite, target_contact, NULL, NULL, NULL); osip_message_set_body(new_invite, invite-body, invite-body_length); eXosip_call_send_initial_invite(ctx, new_invite); // 记录当前对话映射用于响应回传 save_call_mapping(evt-cid, new_invite-call_id); break; }3.3 用软电话实测呼叫流程服务器逻辑写完编译运行后接下来要做的就是用两个软电话进行真实呼叫测试。我一般用 MicroSIP 和 Zoiper 组合一个装 Windows 虚拟机里一个装手机里这样能顺便测试跨设备互通。注册测试的步骤启动服务器确认日志里打印出“listening on 0.0.0.0:5060”之类的信息。打开 MicroSIP添加账号服务器填虚拟机 IP用户名填 1001密码随意因为暂时没做鉴权。打开 Zoiper添加账号服务器填同一 IP用户名填 1002。查看服务器日志应该能看到两条 REGISTER 记录注册表里多了两个 AOR 映射。用 1001 拨打 1002观察服务器日志中 INVITE 的转发记录然后接听测试双方能不能听到声音。这一步如果通了说明你的 SIP 服务器已经具备最基本的“电话交换机”能力了。如果声音不通那就是典型的 RTP 路径问题我在后面专门讲。4. 常见问题排查与避坑实录4.1 注册失败、收不到响应怎么办这是最常见的第一道坎软电话上点了注册状态一直停在“Registering”过一会儿变“Failed”。排查思路要有一个优先级第一步抓包确认 SIP 消息有没有到服务器。这是最基础也最重要的步骤。在服务器上执行sudo tcpdump -i any port 5060 -vv再点一次注册看看有没有来自软电话的 REGISTER 包。如果啥都没有说明消息根本没到服务器问题在网络层检查防火墙规则、服务器 IP 是否可达、端口是否被占用。如果消息到了但服务器没回 200 OK那问题就在程序逻辑。用 gdb 挂进程或者在eXosip_event_wait返回处打日志看看事件类型是不是EXOSIP_MESSAGE_NEW。很多新手在这里会发现MSG_IS_REGISTER(req)这个判断没过原因是他们先判断了事件类型不是 MESSAGE_NEW 而是 REGISTRATION_SUCCESS这个事件只有当程序自己是注册客户端时才会收到。还有一点容易被忽略服务器回 200 OK 时Via 头必须原样复制。SIP 事务匹配时客户端收到响应后会检查 Via 头的 branch 参数是否一致。如果服务器自作主张改了 Via 头或者没有将接收到的 Via 头完整放进响应里客户端会直接丢弃这个响应现象就是“收不到响应”。用 Wireshark 能看到响应已经发出但软电话就是不认就是因为 Via 头被改坏了。4.2 SIP 480 Temporarily Unavailable 深度排查SIP 480 响应是排查时绕不开的一个状态码。它表示“被叫方暂时不可用”。很多入门者看到 480 就懵其实它代表的场景非常具体被叫分机未注册或已离线这是最常见的。服务器在注册表里找不到被叫的 AOR于是回 480。注意有的服务器会让“用户不存在”时回 404而“用户存在但当前离线”时回 480。我这里在实际实现中的策略是注册表里查无此人回 404查到了但联系地址不可达比如过期、端口不响应回 480。被叫端主动拒接被叫软电话收到来电时如果用户拒绝通常回 486 Busy Here 或 480。有的软电话直接配置成“自动拒绝”也会这样。SIP 代理转发时找不到路由如果你的服务器配置了多级代理上一级返回 480你这一级也会把 480 透传给主叫。NAT 注册的 Contact 地址失效分机在内网注册时写的是公网 IP但这个 IP 早变了服务器转发 INVITE 时发到一个黑洞里收不到任何响应最终由服务器超时后转为 480。这类问题很隐蔽排查方法也是抓包看看服务器发出的 INVITE 到底被发到了哪个 IP。根据我实际排查的经验流程一般是先看服务器日志里收到 INVITE 后查注册表的结果如果查不到就明确是注册问题如果查到了就用抓包看 INVITE 是否成功发出、被叫有没有回响应。80% 的 480 都是注册表里没有有效映射导致的所以把注册稳定性做上去这个问题就能解决一多半。另外补充一点软电话注册时通常有有效期默认是 3600 秒。如果分机网络不好注册过期了但服务器没有及时清理注册表就会导致“显示在线一呼就 480”。所以我前面的注册表加超时清理不是可有可无而是必须做。4.3 macOS 上的 SIP系统完整性保护与 SIP 协议别搞混这里要专门讲一个非常容易踩的坑很多人搜“macOS 怎么关闭 SIP”结果看到的是 SIP 协议服务器教程完全不是一个东西。macOS 里的 SIP 是 System Integrity Protection系统完整性保护它和 Session Initiation Protocol会话初始协议只是缩写相同风马牛不相及。如果你用的是 Apple Silicon 芯片的 Mac比如 M4 芯片在 macOS 上做开发时有些涉及系统目录写入、内核扩展加载的操作会被系统完整性保护拦住。如果你想关闭它来方便调试步骤是关机后长按电源键进入恢复模式打开“终端”执行csrutil disable然后重启。重新启用时同样步骤执行csrutil enable即可。但我强烈建议不要为了调试 SIP 服务器去关闭 macOS 的系统完整性保护。因为我们的 SIP 服务器完全可以跑在用户态监听 5060 端口也不需要关 SIP。如果你只是需要在 Mac 上跑服务直接正常创建一个普通用户目录下的项目编译运行就行完全碰不到需要关闭系统完整性保护才能做的事。我见过有朋友为了“安装某个抓包工具”关掉 SIP结果系统安全性降低后来悔得不行。分清这两个“SIP”能帮你少走一大段弯路。4.4 与 RTP 相关的声音问题排查信令都通了、电话也显示接通了但双方听不到声音或者只有单向声音这种问题在 VoIP 调试里占比极高。我总结的排查顺序先确认媒体流走的是不是直连。看抓包里 200 OK 响应中的 SDP 部分里面有一个c行和m行比如cIN IP4 192.168.1.101 maudio 40000 RTP/AVP 0 8 101c是 IP 地址maudio 40000是 RTP 端口。也就是说对端要往192.168.1.101:40000发 RTP 音频包。如果这个 IP 是内网地址而主叫在另一个子网那 RTP 包就发不过去自然没声音。再确认防火墙有没有放行 UDP 大范围端口。RTP 的端口不是你预先固定的而是软电话随机挑选的一般是 10000-60000 之间的偶数端口。如果你在服务器或者路由器上做了 ACL只放行了 5060 端口那 RTP 包会被全部丢弃。解决方案有两个一是放行你信令中能看到的端口段二是让软电话固定 RTP 端口范围。MicroSIP 的配置文件里可以设置 rtp_port_min 和 rtp_port_max这个很有用。然后看是不是 NAT 问题。两个分机都在不同内网时SDP 里的 IP 是各自内网 IP互相不可达。解决办法配置软电话的 STUN 服务器让软电话发出 RTP 时使用 STUN 探测到的公网 IP。或者让服务器做 RTP 代理所有媒体流经过服务器转发。eXosip 本身不直接做 RTP 转发你可以用 rtpengine 或 mediaproxy 组件配合。下面给一个速查的 RTP 问题判断表现象可能原因快捷检查方法双向无声音防火墙拦截 RTP 端口Wireshark 过滤 rtp看有没有 RTP 包单向有声音一方 NAT 不对称对比两端的 SDP c 地址是否可达有 RTP 包但无声编解码协商不一致看 SDP 里双方是否有共同的编解码如 PCMU/PCMARTP 包乱序严重网络质量差看 Wireshark 里的 jitter 参数有一个很隐蔽的坑编解码协商不一致。服务器转发 SDP 时如果因为处理逻辑错误把主叫支持的编解码列表写坏了被叫只能选一个它自己支持但主叫可能并不支持的编码就会导致出现 RTP 包却完全听不到声音的诡异现象。所以实现转发时最好是原样透传 SDP 报文不要做任何修改除非你自己实现了一个转码网关。4.5 常见问题速查表SIP 服务器版我在多年调 SIP 服务器时整理了一份问题速查这里直接放出来覆盖了上面没有细讲的部分场景问题症状推荐排查命令/工具大概率的原因软电话注册失败sudo tcpdump -i any port 5060防火墙未放行 UDP 5060注册成功但一呼就 404查看服务器日志注册表被叫号码未注册或注册已过期一呼就 480查看服务器转发日志注册映射失效/被叫离线呼叫建立但无声音Wireshark 过滤 rtpRTP 端口被防火墙阻断或 NAT 问题通话中突然中断查看服务器日志 BYE 消息注册有效期过期服务器主动断开服务器 CPU 100%strace -p 进程号消息解析出错导致死循环多个分机同时响铃但只有一个能接查看服务器分支转发的逻辑你实现了并行振铃forking但没做 winner 处理这里最值得说的是“通话中突然中断”这个坑。默认注册有效期 3600 秒如果软电话在后台没有重新注册服务器上的映射就会过期。有的服务器实现里通话建立后不会去检查注册状态但也有服务器因为重注册失败而主动释放对话。我的建议是生产环境把注册有效期调大比如 7200 秒同时软电话端开启周期注册两边配合就能避开这个问题。4.6 关闭 macOS 系统完整性保护的完整步骤慎用既然前面提到了 macOS 的 SIP系统完整性保护还是把完整步骤写出来方便真正有需要的人参考。但再次强调这个操作会让系统安全性下降一般开发场景都不需要请确认你确实需要再动手。Intel 芯片的 Mac重启电脑按住 Command R 进入 macOS 恢复模式。打开顶部菜单“实用工具”里的“终端”。输入csrutil disable回车。重启电脑。Apple Silicon 芯片的 Mac包括 M4关机。长按电源键直到出现“正在加载启动选项”。点“选项”进入恢复模式。同样打开终端输入csrutil disable。重启。恢复原状时把disable换成enable即可。如果你进恢复模式后终端提示“csrutil 无法执行”先检查是不是没有进入正确的恢复环境或者 Mac 被 MDM 锁定了。5. 从服务器到可用的扩展方向前面讲的都是最小可用版本如果你真打算把这个服务器用在真实环境中有几个方向是必然要扩展的。第一个是鉴权。现在的代码里任何人发 REGISTER 都能注册任意号码这相当于你家大门不设防。SIP 协议的摘要鉴权Digest Authentication实现起来不算复杂核心是根据用户名查密码然后用 MD5 计算哈希值和请求里的 Authorization 头做比对。oSIP 提供了osip_authorization_parse等接口能帮你解析 Authorization 头字段剩下的就是你自己维护一个用户密码表。第二个是多线路并发。目前这个版本是单线程事件循环同一时间只能处理一个事件。如果分机数量多或者消息频率高建议把事件处理做成线程池eXosip_event_wait返回事件后投递到工作线程。注意 eXosip 的线程安全所有对协议栈的操作都要包在eXosip_lock/eXosip_unlock里否则并发下会出现内存崩溃。这一块我在实际项目中吃过亏当时只是加了一个日志打印线程结果对同一个事务同时读写直接段错误。第三个是媒体代理。如果分机都在公网或者同一内网直连 RTP 没问题。但如果跨 NAT你就得引入媒体代理。媒体代理的实现思路是服务器主动介入 RTP 媒体路径让分机把 RTP 发到服务器再由服务器转发到对端。这样可以避免 NAT 穿透的问题但代价是增加服务器带宽压力。开源方案中 rtpengine 和 RTPProxy 都值得研究配合我们自己的信令服务器调用它来做端口分配和转发。我个人认为把这三个扩展做完你这个基于 oSIP 2.3.1.0 的服务器就已经具备一个商用入门级软交换的能力了。当然如果你不需要这么复杂只是想快速搭一个测试环境那也可以直接跳到用现成的 Asterisk 或 FreeSWITCH。但如果你想真正搞懂 SIP 服务器内部发生了什么自己从 oSIP 写起的这段经历是任何配置现成软件都无法替代的。
返回列表