ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SIP协议详解:从信令流程到VoIP故障排查实战

SIP协议详解:从信令流程到VoIP故障排查实战 做通信协议调试这行当SIP是你绕不过去的一道坎。不管是搞VOIP网关、接运营商线路还是自己搭PBX给公司换掉模拟电话信令这东西不弄明白出了问题就只能干瞪眼。SIP协议全称Session Initiation Protocol说人话就是通信世界的“电话接线员”它负责把一通呼叫从A端完好无损地带到B端至于两边接通之后说什么、怎么传声音那是RTP和SDP的活。很多人一听到“信令”两个字就头大觉得那是高大上的二进制玩意儿。其实恰恰相反SIP是明文文本协议格式上跟HTTP非常像用抓包工具看得一清二楚门槛远没有想象中高。这篇文章我会从SIP消息结构讲起把注册、呼叫建立、挂断、会话修改这些核心信令流程逐个拆开配上可以直接对着看的报文示例中间穿插我自己调试设备时踩过的坑和总结的排查思路。适合刚开始接触VoIP的运维、做网关集成的开发以及被客户电话打得没脾气的技术支持同学参考。题外话说一句网上搜SIP关键词跳出来一堆“Mac关闭SIP”的教程那个SIP是macOS的系统完整性保护机制跟咱们通信圈这个会话发起协议八竿子打不着别搜混了。下面咱们进入正题。1. 先搞清楚SIP是什么以及它凭什么能成为主流1.1 SIP的定位只负责“牵线”不负责“说话”很多人第一次接触SIP最大的困惑是SIP通了是不是就能打电话了答案是否定的。SIP协议只管会话的建立、修改和拆除它本身不承载任何语音数据。真正把声音从一端送到另一端的是RTP协议而双方用什么样的编码格式、采样率、端口是靠SIP消息里携带的SDP会话描述协议来协商的。打个比方SIP是打电话时的“总机接线员”帮你把两个分机连起来SDP就是双方在通话前对好的“通话规格”——说中文还是英文、语速多少而RTP才是那条真正传声音的“线路”。所以调试的时候你会看到SIP信令握手成功但通话就是没声音这时候问题大概率出在RTP媒体流或者SDP协商结果上跟信令本身没关系了。一套完整的VOIP通话流程通常涉及三个协议角色SIP负责信令控制SDP负责媒体参数协商RTP负责媒体数据传送。三者的关系捋顺了后面排查问题就有了基本框架。1.2 为什么H.323被SIP打得找不着北在SIP出现之前视频会议和VOIP领域的主流协议是H.323那是ITU-T制定的一个庞大协议族。H.323的问题在于太复杂它用ASN.1二进制编码消息结构对人不友好调试时必须上专业协议分析仪才能看懂开发技术门槛也高。而SIP是IETF这帮互联网工程师设计的思路就是“像HTTP一样简单”。你直接用tcpdump、Wireshark就能抓到明文信令看到哪个字段不对立刻就能定位问题。我最早调试H.323设备的时候包里全是二进制编码的PER结构看一条消息的报文字段得靠解码工具效率极低。后来切到SIP那种畅快感就像从手工记账切换到了Excel。加上SIP的扩展性特别好新需求来了通过扩展方法就能实现比如即时消息用MESSAGE、文件传输用MSRP、点击回呼用REFER整个生态越滚越大。现在市面上主流的通信平台从FreeSWITCH、Asterisk到Kamailio、OpenSIPS再到商业IPPBX清一色是SIP的天下链路对接也基本都基于SIP。1.3 SIP网络里那些角色的关系理解SIP网络架构先记三个角色UAC用户代理客户端发起呼叫的一方、UAS用户代理服务器接收呼叫的一方、SIP服务器负责注册、路由、重定向。A手机拨给BA就是UACB是UAS中间经过的服务器可以充当注册服务器、代理服务器或者重定向服务器。实际部署中一台IPPBX通常同时承担三种角色它作为注册服务器给分机发账号作为代理服务器帮分机转发呼叫配置里设了呼叫转移的话还可能充当重定向服务器。分机们不需要互相知道IP地址只需要知道服务器的地址就行。这个模型跟办公室找前台转接电话是一个道理你不需要知道对方工位在哪拨个内线让前台帮你转就行了。2. SIP消息结构拆解像读HTTP报文一样读SIP2.1 请求与响应起始行规定了每一次交互的基调SIP消息由三部分组成起始行、头字段、消息体。请求消息的起始行叫请求行格式是“方法 SIP-URI SIP版本”响应消息的起始行叫状态行格式是“SIP版本 状态码 原因短语”。消息头和消息体之间用一个空行分隔。我随便贴一个最简单的INVITE请求你大概率在抓包里经常看到INVITE sip:1002192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7a83d;rport Max-Forwards: 70 From: 1001 sip:1001192.168.1.10;tagae8d2f To: sip:1002192.168.1.10 Call-ID: c3d2f0a8e7b1192.168.1.20 CSeq: 1 INVITE Contact: sip:1001192.168.1.20:5060 Content-Type: application/sdp Content-Length: 159 v0 o1001 2890844526 2890844526 IN IP4 192.168.1.20 sSIP Call cIN IP4 192.168.1.20 t0 0 maudio 4000 RTP/AVP 8 0 101 artpmap:0 PCMU/8000 artpmap:8 PCMA/8000 artpmap:101 telephone-event/8000响应的状态行长这样SIP/2.0 200 OK Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7a83d;received192.168.1.20;rport5060 From: 1001 sip:1001192.168.1.10;tagae8d2f To: sip:1002192.168.1.10;tag9f8e7d6 Call-ID: c3d2f0a8e7b1192.168.1.20 CSeq: 1 INVITE Contact: sip:1002192.168.1.30:5080 Content-Type: application/sdp Content-Length: 143 v0 o1002 2890844530 2890844530 IN IP4 192.168.1.30 sSIP Call cIN IP4 192.168.1.30 t0 0 maudio 6000 RTP/AVP 0 101 artpmap:0 PCMU/8000 artpmap:101 telephone-event/8000你看无非就是把“我是谁、我从哪来、我要干什么、我的条件是什么”四件事说清楚比HTTP多了一部分会话描述。调试的时候习惯这一类格式看几分钟就能上手。2.2 必须背下来的状态码SIP响应状态码分六类跟HTTP的状态码分法几乎一样状态码范围含义常见例子1xx临时应答请求正在处理中100 Trying、180 Ringing、183 Session Progress2xx成功请求已被接收处理200 OK3xx重定向需要到新的地址重试302 Moved Temporarily4xx客户端错误请求本身有问题401 Unauthorized、403 Forbidden、404 Not Found、486 Busy Here、488 Not Acceptable Here5xx服务器错误500 Server Internal Error、503 Service Unavailable6xx全局失败所有服务器都处理不了603 Decline状态码是排查问题最快的入口。用户说“拨号没反应”你先看抓包里返回的是486对方忙、404号码不存在还是408超时没响应方向基本就定了。我最常碰见的是488这个后面讲SDP协商时会展开因为它十有八九是双方编解码谈不拢导致的。2.3 SIP URI地址长什么样SIP地址的格式是sip:userdomain:port其中domain可以是域名也可以是IP地址端口默认可省略。比如sip:1001192.168.1.10:5060代表用户1001在192.168.1.10这个SIP服务器上通过5060端口通信。URI里还可以带参数如sip:1001192.168.1.10;transporttcp表示用TCP传输。还有一种tel: URI用来描述普通电话号码比如tel:8613800138000在网关和运营商对接的场景很常见。两者可以互相转换网关经常收到SIP INVITE后把Request-URI改成tel格式再转发到E1中继。记住一个原则SIP URI描述的是一个“网络会话地址”tel URI描述的是一个“普通电话号码”别搞混。3. 核心信令方法每种方法解决什么事3.1 六大基本方法REGISTER、INVITE、ACK、BYE、CANCEL、OPTIONSSIP协议标准定义了六种基本方法绝大多数基础信令流程只要用这几个就够了方法名作用典型场景REGISTER分机向服务器登记自己当前的地址软电话开机后自动注册INVITE发起一个会话请求携带SDP媒体参数拨打电话、呼叫视频ACK确认最终响应特别用于确认INVITE的2xx响应对方应答后主叫发ACKBYE主动结束一个已建立的会话挂断电话CANCEL取消一个尚未被应答的请求还没振铃或振铃中拨出后对方还没接就挂断OPTIONS查询服务器或对端的能力服务商用它做节点保活和状态探测特别要强调ACK的重要性。INVITE和普通请求不一样它属于“需要特别确认”的请求。如果被叫返回200 OK主叫必须再发一个ACK确认收到双方会话才算正式建立。如果主叫不回ACK服务器会一直重传200 OK直到放弃。这个机制挨着“可靠消息传递”的设计原则现实中很多回铃音异常、呼叫刚接通就掉线的问题追根溯源都是ACK这块处理不对。OPTIONS这个方法经常被忽略但它特别实用。运营商会定期向你的网关发OPTIONS探测判断网关是否在线很多SIP服务商的“心跳保活”就是靠它实现的。你如果看到抓包里有大量周期性的OPTIONS别惊讶那是正常的保活流量。3.2 常用扩展方法别被海量RFC吓到实际业务中六大基本方法往往不够用于是出现了很多扩展方法。它们不是标准非要你支持而是解决特定场景的扩展方法作用常见场景INFO会话中的带外信息传输传递DTMF按键、计费信息UPDATE在不影响现有会话的前提下更新媒体参数早期媒体转正式媒体的参数更新PRACK对1xx临时响应的可靠确认配合100rel扩展使用SUBSCRIBE/NOTIFY订阅与通知事件呈现状态、语音信箱状态MESSAGE发送即时消息SIP聊天、短信透传REFER请对方转接或转移会话呼叫转移、代接PUBLISH发布事件状态在线状态发布我最常用的是INFO和REFER。跟运营商对接计费或接收DTMF按键时如果对方不支持RFC 2833/4733的带内DTMF就得靠INFO通道传呼叫转移功能则几乎离不开REFER。平时不用背全部RF C遇到一个查一个用多了自然熟。3.3 SIP事务与对话理解状态的关键SIP里有“事务”和“对话”两个概念搞懂了它们很多信令行为就变得顺理成章。一个事务指的是从客户端发送的一个请求开始到收到最终响应为止的整个过程。比如“INVITE事务”包括INVITE请求、各个临时应答、最终响应以及对INVITE来说还要算上ACK。CANCEL、ACK其实不属于被CANCEL的那个事务它们在逻辑上限定了各自独立的生命周期。一个对话则是一对UAC和UAS之间持续一段时间的关系靠三个字段标识From标签、To标签、Call-ID。一通电话从INVITE开始建立到BYE结束这个期间所有请求都属于同一个对话。服务器和分机都靠这三个标识识别“这包消息属于哪一通电话”。为什么老工程师总说“先看Call-ID再看tag”因为抓包里消息多而杂同一次呼叫的所有消息Call-ID是相同的先从Call-ID筛出属于这一通电话的报文再通过tag变化看对方身份。如果你看到从A到B的INVITE的From tag在后续消息里被换了那基本可以判定是某台设备在耍流氓没按规矩保留tag。4. 从注册到挂断完整信令流程拆给你看4.1 注册流程REGISTER与401摘要认证分机首次注册通常要经历一次“认证挑战”。客户端发REGISTER不带认证信息服务器回401并给出realm认证域、nonce一次性的随机字符串等参数客户端用密码算出摘要响应然后带Authorization再发一次REGISTER服务器验证通过后回200 OK。我用最常见的摘要认证举例完整过程如下。第一步软电话上线发REGISTERREGISTER sip:192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK1245;rport Max-Forwards: 70 From: sip:1001192.168.1.10;tag1a2b3c To: sip:1001192.168.1.10 Call-ID: register001192.168.1.20 CSeq: 1 REGISTER Contact: sip:1001192.168.1.20:5060 Expires: 3600 Content-Length: 0服务器回401SIP/2.0 401 Unauthorized Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK1245;received192.168.1.20;rport5060 From: sip:1001192.168.1.10;tag1a2b3c To: sip:1001192.168.1.10;tagserver_tag_01 Call-ID: register001192.168.1.20 CSeq: 1 REGISTER WWW-Authenticate: Digest realmasterisk, nonce5e4f8a9b, algorithmMD5 Content-Length: 0客户端计算摘要后带着Authorization重发REGISTERREGISTER sip:192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK1246;rport Max-Forwards: 70 From: sip:1001192.168.1.10;tag1a2b3c To: sip:1001192.168.1.10;tagserver_tag_01 Call-ID: register001192.168.1.20 CSeq: 2 REGISTER Contact: sip:1001192.168.1.20:5060 Authorization: Digest username1001, realmasterisk, nonce5e4f8a9b, urisip:192.168.1.10, response6f8d3c1b2a9e4f0c Expires: 3600 Content-Length: 0服务器验证通过回200 OK。摘要认证的计算逻辑不复杂简单说就是用MD5把用户名、realm、密码组合一次再把方法、URI组合一次最后用随机数再加权一次。实际调试时不用自己算Wireshark里有解密工具关键是理解这个“先挑战、再应答”的流程。注册失败十有八九是密码不对、时间不同步nonce过期、或者realm不匹配。4.2 主叫到被叫一次标准呼叫的完整信令流程现在假设公司有台FreeSWITCH服务器192.168.1.10分机1001和1002都注册在它上面。1001拿软电话拨1002整个信令流程可以分成三个阶段呼叫建立、通话中、呼叫释放。我把关键报文按真实流程贴出来。先看呼叫建立阶段// 消息1: 1001 - FreeSWITCH INVITE sip:1002192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1 Max-Forwards: 70 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10 Call-ID: call001192.168.1.20 CSeq: 1 INVITE Contact: sip:1001192.168.1.20:5060 Content-Type: application/sdp Content-Length: 159 v0 o1001 2890844526 2890844526 IN IP4 192.168.1.20 sSIP Call cIN IP4 192.168.1.20 t0 0 maudio 4000 RTP/AVP 8 0 101 artpmap:0 PCMU/8000 artpmap:8 PCMA/8000 artpmap:101 telephone-event/8000服务器的处理逻辑收到INVITE后先回100 Trying告诉1001“我知道了正在帮你转”。然后按照被叫号码1002查注册表找到1002的Contact地址是192.168.1.30:5080于是作为代理服务器转发INVITE给1002同时把自己加进Record-Route确保后续请求能经过它。// 消息2: FreeSWITCH - 1001 SIP/2.0 100 Trying Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1;received192.168.1.20;rport5060 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10 Call-ID: call001192.168.1.20 CSeq: 1 INVITE Content-Length: 0 // 消息3: FreeSWITCH - 1002 INVITE sip:1002192.168.1.30:5080 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.10:5060;branchz9hG4bKproxy888 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1;received192.168.1.20;rport5060 Record-Route: sip:192.168.1.10;lr Max-Forwards: 69 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10 Call-ID: call001192.168.1.20 CSeq: 1 INVITE Contact: sip:1001192.168.1.20:5060 Content-Type: application/sdp Content-Length: 1591002的软电话收到INVITE后开始振铃回180 Ringing// 消息4: 1002 - FreeSWITCH SIP/2.0 180 Ringing Via: SIP/2.0/UDP 192.168.1.10:5060;branchz9hG4bKproxy888 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1;received192.168.1.20;rport5060 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10;tagcallee_tag_b2 Call-ID: call001192.168.1.20 CSeq: 1 INVITE Contact: sip:1002192.168.1.30:5080 Content-Length: 0注意看To里出现了callee_tag_b2这个tag。此时早期对话就建立了1002的软电话已经知道这一通电话的归属。1001收到的是服务器转发过来的180也会看到To里的tag。两边从这开始认“亲戚”。1002接听软电话回200 OK并带上自己的SDP应答表示“我选PCMU编码我这边媒体在192.168.1.30的6000端口”// 消息5: 1002 - FreeSWITCH SIP/2.0 200 OK Via: SIP/2.0/UDP 192.168.1.10:5060;branchz9hG4bKproxy888 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1;received192.168.1.20;rport5060 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10;tagcallee_tag_b2 Call-ID: call001192.168.1.20 CSeq: 1 INVITE Contact: sip:1002192.168.1.30:5080 Content-Type: application/sdp Content-Length: 143 v0 o1002 2890844530 2890844530 IN IP4 192.168.1.30 sSIP Call cIN IP4 192.168.1.30 t0 0 maudio 6000 RTP/AVP 0 101 artpmap:0 PCMU/8000 artpmap:101 telephone-event/8000服务器把200 OK转发给1001然后1001回ACK。这里有个细节ACK是点到点直接发的。如果通话双方都支持RTP直连媒体流可能根本不经过服务器只有信令走服务器。如果服务器强制B2BUA模式或者做媒体代理RTP也会经过服务器中转。// 消息6: FreeSWITCH - 1001 SIP/2.0 200 OK 内容与消息5基本一致Via里去掉proxy888那一层 // 消息7: 1001 - FreeSWITCH ACK sip:192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c4 Max-Forwards: 70 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10;tagcallee_tag_b2 Call-ID: call001192.168.1.20 CSeq: 1 ACK Content-Length: 0 // 消息8: FreeSWITCH - 1002 ACK sip:1002192.168.1.30:5080 SIP/2.0 同上经由服务器转发以上过程走完双方SDP协商完成编码选了PCMUG.711μ律1001的RTP发送端口是40001002的RTP接收端口是6000媒体流开始传输。通话中状态就没有SIP信令了全是RTP包。挂断时假设1002先挂机// 消息9: 1002 - FreeSWITCH BYE sip:192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.30:5080;branchz9hG4bKbye99 Max-Forwards: 70 From: sip:1002192.168.1.10;tagcallee_tag_b2 To: sip:1001192.168.1.10;tagcaller_tag_a1 Call-ID: call001192.168.1.20 CSeq: 101 BYE Content-Length: 0 // 消息10: FreeSWITCH - 1001 BYE sip:1001192.168.1.20:5060 SIP/2.0 内容类似由服务器改写Contact // 消息11: 1001 - FreeSWITCH SIP/2.0 200 OK // 消息12: FreeSWITCH - 1002 SIP/2.0 200 OK这个完整流程跑一遍你对SIP信令的基本节奏就有感觉了。我建议你本地搭一台服务器用两个软电话实际拨一通边拨边抓包对照着看一遍收获比读十遍文章都大。4.3 呼叫失败与取消486、CANCEL与487真实环境里不是每个呼叫都能接通。占线、无人接听、被叫拒接每种失败都有对应的信令表现。如果1002正在通话1001拨它1002的软电话或者服务器可以直接回486 Busy HereSIP/2.0 486 Busy Here Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1;received192.168.1.20;rport5060 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10;tagcallee_tag_b2 Call-ID: call001192.168.1.20 CSeq: 1 INVITE Content-Length: 0主叫收到486后要回一个ACK确认收到最终响应这个ACK不需要经过服务器逐跳确认但按协议规范还是要发。注意非2xx的ACK和2xx的ACK机制上是有区别的前者属于原来那个INVITE事务的组成部分后者属于新的独立事务。如果是振铃过程中1001等不及了直接挂机软电话会发CANCELCANCEL sip:1002192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bK7d3c1 Max-Forwards: 70 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10 Call-ID: call001192.168.1.20 CSeq: 1 CANCEL Content-Length: 0服务器收到CANCEL后会回200 OK表示CANCEL成功同时向被叫转发CANCEL。被叫侧收到CANCEL后终止振铃对原来的INVITE事务回487 Request Terminated然后主叫侧再对这个487回ACK。整个流程看起来像“CANCEL 200 487 ACK”三部曲。这里容易混淆的是CANCEL收到200 OK只是代表“CANCEL这个请求被接受了”并不是对话结束真正的结束信号是487配合ACK。4.4 通话中修改会话re-INVITE与UPDATE通话中改编码、加视频、保持通话这些操作靠的是re-INVITE和UPDATE。re-INVITE是在已建立的对话里再次发起INVITE用于修改会话参数UPDATE则用于早期对话阶段修改参数且不会影响整个对话的状态。最典型的场景是呼叫保持。用户按下保持键已经建立的会话里保持方发出re-INVITESDP里把自己的媒体置为只发不收或只收不发INVITE sip:1002192.168.1.30:5080 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branchz9hG4bKhold01 Max-Forwards: 70 From: sip:1001192.168.1.10;tagcaller_tag_a1 To: sip:1002192.168.1.10;tagcallee_tag_b2 Call-ID: call001192.168.1.20 CSeq: 2 INVITE Contact: sip:1001192.168.1.20:5060 Content-Type: application/sdp Content-Length: 143 v0 o1001 2890844526 2890844527 IN IP4 192.168.1.20 sSIP Call cIN IP4 192.168.1.20 t0 0 maudio 4000 RTP/AVP 0 101 artpmap:0 PCMU/8000 artpmap:101 telephone-event/8000 asendonly对端回200 OKSDP里带arecvonly双方就心照不宣地进入保持状态。取消保持时再发一个re-INVITESDP恢复asendrecv。这个过程不需要重新振铃对用户完全无感但抓包里能清清楚楚看到一次“快速INVITE-200-ACK”循环。理解re-INVITE对排查通话中途单通、静音问题特别有帮助。5. 关键头字段解读调试时逐行看什么5.1 必看头字段速查表SIP头字段有几十个绝大多数调试场景用得上的就那几个。我整理了一张速查表头字段作用调试要点Via记录请求经过的每一跳防止环路检查branch参数是否正确received/rport有没有被改写From请求发起方标识带tag参数同一对话中From tag不能变To请求接收方标识被叫响应后带tag比较响应中的To与实际被叫是否一致Call-ID全局唯一的呼叫标识过滤同一通呼叫的所有消息CSeq命令序号方法名保证消息顺序检查CSeq是否连续方法名是否匹配Contact后续请求可以直接发往的地址NAT场景下重点看IP/端口是否正确Max-Forwards每经过一跳减1默认70出现408/环路时看它有没有被减到0Record-Route/Route强制后续请求经过代理看代理是否插入了路由路由是否带lrAllow本端支持的方法列表功能不起作用时先核对方法是否被支持Supported本端支持的特性扩展涉及100rel/precondition时核对Session-Expires会话超时时间掉话问题检查此字段看到一段信令养成习惯按这个顺序扫一遍。先确定消息类型再看状态码定方向然后通过Call-ID把同一通呼叫的消息串起来接着比对CSeq、tag是否一致最后看Contact、Route和SDP。这套读包顺序我用了很多年基本百试百灵。5.2 从抓包里快速定位问题的读包顺序用Wireshark抓SIP包时第一步先过滤sip然后在“电话”菜单里可以按呼叫统计查看。但真正的实战中我更喜欢直接用过滤表达式sip.Call-IDcall001192.168.1.20 sip.MethodINVITE sip.Status-Code 400 sip[Status-Code] 486 rtp先把同一通呼叫的所有SIP消息筛出来按时间排序看一个完整流程。然后重点关注第一个非1xx的响应是什么最终状态码多少发起方有没有对最终响应回ACK或者BYESDP协商结果是什么媒体流是否在预期端口上流动这里说一个经验如果看到INVITE发出后服务器回了100 Trying然后卡住没有后续消息十有八九是服务器在等下游超时。可能是被叫地址写错了、被叫不在线或者防火墙把服务器到被叫的包丢了。如果看到的是408 Request Timeout那基本可以确定是路由或防火墙问题而不是被叫本身拒绝。注意区分“服务器没响应”和“被叫没响应”这两类问题的排查方向完全不同。5.3 NAT和端口相关的头字段陷阱NAT环境是SIP调试的重灾区。私有网络里的分机通过家庭路由器上网路由器把SIP信令的源地址和端口做了地址转换但SIP报文里Via、Contact、SDP的IP和端口都是内网地址服务器拿到这些信息去回包或转发媒体时就会把包发到内网地址结果可想而知。解决方案通常在两个层面。信令层面服务器要支持RFC 3581的rport机制从请求的源地址和源端口推导出公网映射地址然后改写Via的received和rport参数还要按照Contact和SDP的实际来源改写这两处的IP和端口。设备自身层面可以采用STUN探测公网映射或者在路由器上做端口映射把公网端口映射到内网设备。踩坑最多的是路由器上的SIP ALG。很多家用路由器号称“为SIP优化”实际上粗暴改写SIP报文里的端口和地址有时候改对了有时候改出个驴唇不对马嘴。我遇到过客户通话时单通、断断续续折腾了一下午最后进路由器把SIP ALG关掉问题立刻消失。现在遇到SIP相关的异常我的第一反应就是先请客户关掉SIP ALG再重新抓包。这一条经验至少帮我省了几十个小时的无用功。6. 常见故障排查实录6.1 注册类问题速查表分机注册不上是运维每天都会碰到的问题。我把高频注册问题整理成一张速查表现象报错原因方向检查项注册超时10060/408网络不通、端口被封能否ping通服务器、5060端口是否监听认证失败401/407密码错误、realm不匹配核对分机密码、realm配置注册被拒403 Forbidden服务器ACL限制、账号未启用检查ACL白名单、分机状态注册间隔太短423 Interval Too Brief服务器要求最小注册周期调整Expires大于Min-Expires找不到分机404/604分机号未创建、域名错误核对分机号和服务器域名注册问题的排查顺序先看网络通不通再看端口通不通然后看认证流程最后看服务器策略。我见过不少人一上来就翻服务器日志其实先抓个包看信令交互一分钟就能定位到前面三个环节。抓注册包时重点看服务器回的是401认证问题还是403策略问题两个方向完全不同。6.2 呼叫类问题实录注册正常呼叫失败这就是更高一层的挑战了。下面几种情况是我这些年遇到最多的。第一种能注册但不能呼叫。这种情况往往不是信令协商的问题而是拨号计划、ACL、权限配置的问题。检查服务器里这条呼叫路由是否匹配分机的呼叫权限是否允许以及ACL是否放行了信令来源IP。服务器日志和抓包双管齐下通常很快能发现。第二种呼叫通了但没声音。这是“单通”或“全无声音”的典型场景。排查思路先看SDP协商结果确认双方选择的编码是否一致然后看RTP包的IP和端口是否是公网可达的。如果SDP里写的是192.168.x.x而对方在远端的公网那必然没声音。解决方向就是把媒体改走服务器中转或者想办法让SDP携带公网可达的地址。还有一个常见原因是防火墙只放行了5060端口没放行RTP端口范围通常10000-20000媒体包成了漏网之鱼。第三种回铃音问题。被叫已经振铃了但主叫那边听不到“嘟——嘟——”的回铃音。这往往是因为服务器只发了180 Ringing而运营商侧要求必须收到183 Session Progress或者早期媒体流才播放回铃音。解决办法是在服务器里把早期媒体触发方式配置好让180带上SDP或者干脆发183并播放本地回铃音。这个坑在对接运营商中继时特别常见本地分机之间一般不受影响。第四种通话中突然掉线。常见原因有两个一是NAT映射老化长时间没媒体流量导致路由器的映射表被清掉后续的SIP消息发不进来。解决方法是定期发OPTIONS/UPDATE保活或者把NAT映射超时时间调大。二是Session-Expires协商不一致会话定时器到期后没有成功刷新服务器主动断开。查看抓包里是否有BYE之前出现Session-Expires相关的UPDATE失败基本就能确认。第五种DTMF按键没反应。SIP里DTMF最常见的是通过RFC 4733的telephone-event带内音传输协商时SDP里要有artpmap:101 telephone-event/8000这一行实际发送时RTP包里带有marker位和事件编码。如果协商里没有这一行或者双方payload type的数字对不上按键就会丢失。另外有些IVR系统要求DTMF用SIP INFO传这时就需要把网关或软电话的DTMF模式改成INFO。调试时先抓包看DTMF是作为RTP事件还是INFO消息发出再对症下药。6.3 抓包与压测工具我的家用排查三板斧工具不在多顺手就行。我现在排查SIP问题最常用的三样Wireshark、sngrep、SIPp。Wireshark是通用抓包神器分析SIP报文时可以右键“Follow Stream”把一次会话的完整信令流拉出来看还能用“Telephony - VoIP Calls”直接列出所有呼叫和状态。缺点是抓包文件大了以后纯文本SIP和RTP混在一起筛选起来稍微费劲。sngrep是纯命令行工具专门为SIP设计界面类似top命令。它能按Call-ID把一通呼叫的所有SIP消息分组展示还能看到呼叫流程概览甚至直接在命令行里回放RTP听一下音频有没有问题。排查线上服务器时SSH连上去直接sngrep -q -d eth0比在远端装Wireshark再导出文件方便太多。我强烈建议每个做SIP运维的人装一个。SIPp是压测和模拟工具可以构造大量并发呼叫测试服务器的承载能力。它的脚本是XML格式需要写场景文件刚开始学习成本略高但跑通了之后非常顺手。压测前先确认服务器ACL里放行了测试机的IP不然每个请求都被拒测了个寂寞。另外压测时别用太小的间隔冲击线上服务器我见过有人模拟10路呼叫把生产PBX压崩最后被客户追着骂的场面。7. 实操建议与学习路径如果你是个新手想用最短时间把SIP信令搞明白我的建议是先搭一套本地实验环境。找一台普通的PC装FreeSWITCH或者Asterisk再装两个软电话MicroSIP和Zoiper注册两个分机号然后拨一通电话全程用Wireshark抓包把本文第4章的流程对着报文逐条标一遍。这个过程一两个小时就能完成但对SIP的理解会有一个质的飞跃。学习过程中有个心态要摆正SIP的文本语法很简单真正难的是把每个字段放在正确的场景里理解。REGISTER的Contact和INVITE的Contact作用就不一样同样叫200 OK对REGISTER和INVITE的含义和使用方式也不同。这些细微差别靠背文档是很难记住的必须结合抓包和实际故障去体会。工具链上手顺序建议先从软电话和Wireshark开始然后学sngrep提升线上排查效率最后学SIPp做压力和场景模拟。WebRTC网关、SIP over WebSocket这些高级话题等基础信令流程烂熟于心之后再碰会顺利得多。最后再分享一个小技巧调试时把抓包文件的过滤器保存为一套模板比如sip || rtp再配合“Telephony - VoIP Calls”视图一通电话从头到尾的信令和媒体状况一目了然。我现在处理任何SIP相关的问题都是先抓包再分析最后才动配置。抓包不是万能的但不抓包去瞎猜配置十有八九会把问题越改越复杂。
返回列表