You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js SIP实现类结构问询:Dialog与Transaction交互逻辑

核心概念澄清(严格遵循RFC 3261)
  • 事务(Transaction):是SIP请求与对应响应的最小交互单元,由Via头的branch参数唯一标识。事务完全可以独立于对话存在——比如REGISTER、OPTIONS这类不需要持久会话的请求,全程只依赖事务处理,不需要创建Dialog。事务有自己的状态机:非INVITE事务包含TRYING、PROCEEDING、COMPLETED、TERMINATED状态;INVITE事务额外多了CONFIRMED状态。
  • 对话(Dialog):是两个UA之间的持久化状态会话,由Call-ID、From标签、To标签共同标识。只有特定请求(如INVITE、SUBSCRIBE)会触发Dialog创建,用于后续会话内的请求(如BYE、UPDATE)。Dialog不直接处理消息发送,而是依赖事务完成底层交互。
消息匹配逻辑的最佳位置

消息匹配逻辑必须放在**主SIP类(你的Client实例)**中,流程如下:

  1. 收到SIP消息后,先提取Via头的branch参数,在事务池中查找匹配的Transaction:
    • 找到则将消息交给该事务处理,触发事务相关事件(如401、200)。
  2. 若未匹配到Transaction,再提取Call-ID、From标签、To标签,在Dialog池中查找匹配的Dialog:
    • 找到则将消息交给该对话处理,触发会话相关事件(如INVITE、BYE)。
  3. 若都未匹配到,判断请求类型:
    • 若是REGISTER、OPTIONS这类无Dialog请求:创建新Transaction并加入事务池,直接处理。
    • 若是INVITE这类需创建Dialog的请求:先创建Transaction处理,收到2xx响应时自动生成Dialog并加入Dialog池。
重构实现建议(兼顾无状态设计与RFC规范)

1. 拆分Dialog与Transaction职责

  • Transaction类:负责底层消息发送、事务状态管理、响应匹配逻辑,是无会话类请求的核心处理单元。
  • Dialog类:仅维护会话状态(如EARLY、CONFIRMED、TERMINATED),生成会话内请求的必要头(如递增CSeq、维护From/To标签),但消息发送仍通过Transaction完成。
  • Message类:保持完全无状态,只负责SIP消息的解析与构建。

2. 调整REGISTER的实现

让Client.Register()返回Transaction而非Dialog,因为REGISTER不属于会话范畴:

Client.Register().then(transaction => {
    transaction.on('200', () => {
        console.log("REGISTERED");
        call("69");
    });
    transaction.on('401', (res) => {
        // 生成授权后的REGISTER请求,复用当前事务发送
        const authReq = Client.Message("REGISTER", {...}).Authorize(res);
        transaction.send(authReq);
    });
});

3. 保留事件监听机制

  • Transaction触发事务生命周期事件(如timeout、terminated)和具体响应码事件(如200、401)。
  • Dialog触发会话相关事件(如invite、bye)和状态变更事件(如confirmed、terminated)。
  • 主Client类触发顶层事件(如newInvite、newRegister),方便上层业务快速监听新请求。

4. 状态管理优化

仅Transaction和Dialog维护自身状态,Client类仅持有事务池、对话池的引用,不存储业务状态。事务结束(如非INVITE事务收到最终响应)后自动从池内清理;Dialog结束(如收到BYE的2xx响应)后同样自动清理。

现有呼入逻辑的调整示例
// 主Client类触发新INVITE请求事件
Client.on('newInvite', (inviteMsg) => {
    console.log("Received INVITE");
    // 创建事务处理该INVITE请求
    Client.Transaction(inviteMsg).then(transaction => {
        // 发送100 Trying响应
        transaction.send(inviteMsg.CreateResponse(100));
        
        transaction.on('180', () => {
            console.log("Ringing");
        });
        
        // 收到200响应时创建Dialog
        transaction.on('200', (res) => {
            Client.Dialog(res).then(dialog => {
                console.log("Call connected");
                // 监听对话内的BYE请求
                dialog.on('bye', (byeMsg) => {
                    const okResp = Client.Message("200", {
                        callId: byeMsg.callId,
                        cseq: byeMsg.cseq_count,
                        fromTag: byeMsg.fromTag,
                        toTag: byeMsg.toTag
                    }).create();
                    dialog.send(okResp);
                });
            });
        });
    });
});

内容的提问来源于stack exchange,提问作者Nik Hendricks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:37:30