sip

在此之前我们已经介绍了SIP的基础协议——rfc3261,本篇主要谈谈自己对SIP协议的理解,内容多少有些主观,若有任何错漏,敬请读者批评指正。

背景

SIP主要被用于IP通话,由于其协议结构的特点,非常适合通过请求头/响应头增加各种元信息。而其核心功能,同样和请求头息息相关。

当然,这里不是说请求行或请求体不重要,而是它们承载的内容和功能较为固定,比如请求行要满足对应上下文的约束,请求体的内容依赖于请求头的说明,故在本文中不作为重点。

SIP的核心是路由,又因为其使用场景中常常存在诸多中间代理,所以在首次发起会话时如何自适应的路由到目标,同时又要尽量在后续的交互过程中减少不必要的路由开销就成了SIP协议本身的重中之重。

下面的内容也是聚焦于为上面这种需求服务的请求头。

请求头

我认为与此相关最重要的请求头莫过于Contact、Via、Route和Record-Route了。搞清楚了这四个请求头,那么对SIP的理解就已经到一个阶段了。

当然,Call-ID、From和To等请求头也是非常非常重要,甚至必要性高于前者,但其意义比较明确,这里只是简单说明:

  1. Call-ID:会话的唯一标识;同时SIP用户在向同一个Registrar注册时,使用的也是同一个Call-ID。
  2. From:会话发起方的逻辑标识
  3. To:会话接收方的逻辑标识

Via

一般的SIP路由路径是很长的,有些临时响应比如101直接返回到上一级请求方即可,但一般的响应都需要确保前面的每一个请求者都被依次响应。那怎么保证一致的响应返回路径呢?而这就是Via发挥作用的时刻了。

每次请求时将自己的物理地址作为Via的内容放在Via列表的顶端,响应时只要将响应发回给第一个Via并移除顶端的Via即可。

所以接收者收到的响应中可能带有一长串的Via,而对最初的请求者来说,收到的响应往往并没有额外的Via。

Contact

逻辑地址一般都是确定的,但SIP的实际物理地址常常是变动的,所以SIP用户一般需要通过注册流程将当前的逻辑地址和物理地址映射保存在Registrar中。

其中From和To通常都是承载着逻辑地址,所以声明物理地址这个重任就交到了Contact手中。

携带Contact的报文可以理解为“如果你要和我通信,请通过下面这个地址联系我”。

其中请求方的物理地址通过请求发出,响应方的物理地址通过响应返回。这样二者就可以在后续的交互过程中考虑直连。

Route和Record-Route

通过Via我们能够保证请求和响应的路径统一。通过Contact我们能在第一次动态路由找到目标后直连通信,从而减少路由开销。

但最初我们就提到过SIP实现灵活业务的关键是请求头/响应头,所以实际上在大部分场景下需要很多中间代理做二次甚至多次处理,正是在这些代理的逻辑中承载着真正的业务。

而这个需求实际上就是由Route和Record-Route驱动的。

每当中间代理需要显式告知会话的发起方和接收方“在接下来的交互过程中也不要忘了经过我,我也想做点事、帮点忙”这种想法时,会在请求报文中加上一个Record-Route头,其中承载着自己的物理地址。这样的报文在到达接收方时,当中可能会存在一个有序的Record-Route列表,接收方保存这个列表并生成自己的Route列表;接着响应通过Via按原路返回到达请求方,这样请求方也能通过其中的Record-Route列表生成自己的Route列表。

这样后续任何一方在该会话中主动发起通信时,都会带上这个Route列表。

其实Route列表就是告知接下来的每一跳路由是怎样的,所以其优先级是高于Request-URI的。

Request-URI在松散路由中可以理解为末尾的Route,在严格路由中可以理解为下一跳路由。

通过上述四个请求头,整个SIP的路由路径可谓是相当清晰了。一旦通信链路是直观清晰的,剩下的就是专注你自己的业务了。