做VoIP的人一定绕不开SIP协议。不管你是做软电话、呼叫中心、还是对接语音网关,最终都会落到同一个问题:怎么用代码去收发那些REGISTER、INVITE、BYE请求。我在Go项目里为了这件事翻过不少轮子,最后稳定跑起来的方案,就是基于GoSIP生态的github.com/emiago/sipgo这个库。这篇文章从一个SIP Server的视角切入,讲清楚怎么用GoSIP把UAS(被叫侧)和UAC(主叫侧)两头都跑起来,顺便把收发请求时最容易踩的坑一起梳理掉。适合谁看?打算在Go服务里集成SIP能力、或者想自己写一个轻量SIP服务器的开发者,这篇应该能帮你节省至少两天的试错时间。
1. 项目整体设计与思路拆解
1.1 为什么坚持用Go写SIP服务
很多老牌SIP Stack都是用C/C++写的,比如pjsip、sofia-sip,性能确实强,但接入Go项目时总免不了CGO编译,一碰到交叉编译就头疼。Go这边虽然生态没有C那么成熟,但胜在部署简单、并发模型契合,而且纯Go的SIP库已经能覆盖绝大多数生产场景。
我做这个项目时选Go还有一个现实原因:整个服务的其他模块(REST API、数据库、WebSocket信令)都是Go写的,如果SIP部分用C库,那就要维护两套构建链,CI又得额外装一堆依赖。用纯Go的SIP库之后,一个二进制文件搞定所有事,部署直接扔到服务器上跑,省心太多了。
1.2 GoSIP库怎么选,我为什么用sipgo而不是其他库
Go生态里能用的SIP库不算多,主要看这几个:
github.com/asiortino/go-sip:比较早期的纯Go实现,消息解析和事务层都有,但维护频率低,很多年没大更新了,部分API设计偏底层,拿来学习可以,上生产要谨慎。github.com/cloudwebrtc/go-sip:这个库功能很全,还支持WebSocket传输,适合和浏览器端软电话打通,但依赖比较多,事务层状态机用起来稍重。github.com/emiago/sipgo:目前社区活跃度最高的纯Go SIP库,API设计干净,内建事务层,支持UDP/TCP/WS传输,官方还提供了UA、UAC、UAS、Server等高层封装。我最终选的就是这个。
选sipgo还有几个实际的考量。第一,它的事务层实现完整,UDP重传、定时器这些都有现成处理,不用自己造轮子。第二,它提供Server这个高层抽象,可以直接绑定端口监听请求,也提供UAC用来主动发请求,收发两边的API风格一致,学习成本低。第三,它本身不强制绑定某个传输层,同一份业务代码可以同时跑在UDP和TCP上,后面如果要接WebSocket,也不用推翻重来。
1.3 SIP在Server里到底是怎么流转的
SIP协议本身是文本协议,类似HTTP,但和HTTP最大的不同在于它是一个“会话初始化”协议,只管建立、修改、拆除多媒体会话,不负责传输媒体流本身。媒体流通常走RTP,由SDP协商出的端口承载。
一次典型的SIP通话流程是这样的:
- 主叫(UAC)发出INVITE请求,里面带着SDP,说明自己能接收媒体的IP和端口。
- 被叫(UAS)收到请求后,回100 Trying表示已经在处理,然后回180 Ringing表示开始振铃。
- 被叫接听后,回200 OK,同样附带自己的SDP。
- 主叫收到200 OK后,回复ACK确认,此时媒体通道建立,双方开始通话。
- 任一方挂机,发出BYE请求,对方回200 OK,整个会话结束。
这个流程里,Server端要做的事其实很明确:接收请求、解析消息、执行业务逻辑、返回对应的响应。GoSIP库帮你把Socket监听、事务匹配、消息解析都处理好了,你要做的就是注册业务处理器。
1.4 架构上的取舍:无状态转发还是有状态业务
在搭建SIP Server之前,先想清楚一个问题:你到底需要处理什么级别的业务?
如果是做一个简单的代理、负载均衡器,那可以“无状态”处理,转发完请求就完事。但如果你要做的是注册服务器、呼叫控制服务器,那必须“有状态”处理,因为你得记录用户是否注册、当前通话是否在振铃、超时之后要做什么。
GoSIP的Server模型天然适合有状态处理。每个到达的请求都会生成一个ServerTransaction,你可以持有这个事务对象,在业务逻辑执行完后主动回响应。如果只是简单地同步响应,也可以直接调用tx.Respond返回结果。实际项目里,我通常会在事务里挂一个业务ID,用Context把请求链路串起来,方便后续查日志。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心细节解析与实操要点
2.1 先把SIP消息结构吃透
SIP消息分两种:请求和响应。请求长这样:
code复制INVITE sip:100@192.168.1.10:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.5:5060;branch=z9hG4bK-12345
From: <sip:200@192.168.1.5>;tag=abc
To: <sip:100@192.168.1.10>
Call-ID: abcdef-12345@192.168.1.5
CSeq: 1 INVITE
Contact: <sip:200@192.168.1.5:5060>
Max-Forwards: 70
Content-Type: application/sdp
Content-Length: 123
响应长这样:
code复制SIP/2.0 180 Ringing
Via: SIP/2.0/UDP 192.168.1.5:5060;branch=z9hG4bK-12345
From: <sip:200@192.168.1.5>;tag=abc
To: <sip:100@192.168.1.10>;tag=def
Call-ID: abcdef-12345@192.168.1.5
CSeq: 1 INVITE
Contact: <sip:100@192.168.1.10:5060>
Content-Length: 0
``
