完整SIP:SDP媒体协商概论-STUN连接性检查及后续offer处理及ICE选项和keepalives讨论
原文出处:完整SIP/SDP媒体协商概论-STUN连接性检查-客户端流程
在前面章节中,无论是关于发送offer还是关于接收answer消息的讨论中都没有离开一个重要的步骤,那就是执行连接性检查(connectivity checks)。因为侧重点的不同,笔者在前面的章节没有详细介绍连接性检查的具体细节,在这个章节,我们重点讨论连接检查的具体内容和执行流程。
所有ICE的部署需要符合RFC5389中STUN的规范(已经更新为RFC8489)。大家知道,全部署场景的agent才是ICE部署中核心内容。全部署场景agent需要扮演STUN客户端(生成检查)和STUN服务器端(接收检查)的两种角色。轻量级的agent则作为服务器端只能接收检查(check)。因此,我们所讨论的内容将以SUTN客户端生成流程和服务器端接收流程两个部分的内容进行讨论。其中,STUN客户端流程主要包括:
- 创建权限支持转发候选地址
- 发送请求
- 处理响应
服务器端主要流程包括:
- 全场景部署agent的其他额外步骤处理
- 轻量级部署agent的其他额外步骤处理
笔者会根据以上的内容逐一进行讨论。首先,笔者将介绍关于STUN客户端的流程处理。
1 STUN客户端流程总览
这里,笔者首先说明一下关于STUN客户端的发送流程。STUN客户端发送连接检查(connectivity checks),确认连接检查是一个ordinary还是triggered check。另外,笔者说明一下,以下讨论的流程仅支持全部署场景agent。
为了保证安全,笔者在前面的文章中也提到过,转发也需要一个权限要求。如果使用relayed本地候选地址发送连接检查的话,如果以前没有创建转发权限的话,客户端必须首先需要创建一个转发权限,允许通过relayed本地候选地址进行转发。如果已经有已创建的转发权限的话,agent可以使用这个权限。如果需要创建转发权限的话,agent转发权限需要根据一定的流程来实现权限处理。具体的权限创建流程读者可以参与RFC5766-8和9章节。创建好的权限必须支持远端候选地址。一种情况需要特别处理,agent在正常情况下,发送CreatePermission请求进行连接检查创建时,RFC5245规范推荐agent要延迟TURN通道创建,直到ICE完成后再开始创建。一旦权限创建完成以后,agent必须维护权限的活动状态,一直到ICE结束此流程。
2 发送请求
在STUN客户端处理流程中,本地候选地址对远端候选地址发送绑定请求(Binding request),发送此请求后生成一个check。关于绑定请求的具体构建和生成,读者可以参与RFC8489。连接检查的安全策略必须使用STUN短期安全机制来实现。关于STUN短期安全机制(用户名称/密码+时间限定)和长期安全机制笔者在以前也做过介绍,读者也可以查阅RFC8489-9.1章节了解更多细节。注意,不能使用RFC3489实现向后兼容的支持能力。连接检查必须使用FINGERPRINT机制来实现STUN消息区分。关于FINGERPRINT机制的定义,读者可以查阅RFC8489-7。
ICE通过定义四个新属性拓展了对STUN支持,这四个属性分别是PRIORITY,USE-CANDIDATE,ICE-CONTROLLED,和ICE-CONTROLLING。在接下来的章节中笔者分别介绍这四个新定义的拓展属性。注意,这四个STUN拓展的新属性仅支持ICE的连接检查。
3 ICE定义的四个新STUN拓展说明
如果agent发送绑定请求时,它必须在其绑定请求中包含一个PRIORITY拓展属性。agent必须设定这个PRIORITY等于候选优先级排序中设定的那个权限设置。对于反射候选地址来说,这个优先级可以通过检查结果的学习来获得。除了反射候选地址中的偏好类型设置为此偏好设置以外,此优先级值计算和本地候选地址配对计算也是一样的。
在绑定请求中,被控方agent可以包含一个USE-CANDIDATE。但是,主控方一定不能在绑定请求中包含此拓展属性。此STUN拓展属性USE-CANDIDATE表示的意思是被控方agent希望退出此构件中的检查,使用来自于此构件中的候选配对执行检查。这里涉及了一个配对推荐的具体流程,笔者在未来的文章中将具体讨论此指导原则和此STUN拓展的使用。
其余两个拓展属性根据角色不同选择包含不同的属性。具体来说,如果agent是在一个主控方角色中的话,在agent发送绑定请求时,它必须在请求中包含ICE-CONTROLLED拓展属性。如果是在被控方agent中的话,它必须在绑定请求中包含一个ICE-CONTROLLING拓展属性。当然,两种角色可能会因为会话不同其内容也可能不同,具体关于其角色决定的内容处理,笔者在前面的文章中有非常详细说明。
4 构建安全信息
除了agent发送绑定请求中需要各自包含所需要的拓展属性以外,agent同时也需要发送一定的安全信息来实现安全验证。绑定请求作为一种连接检查,它也必须使用STUN短期安全机制。STUN短期安全机制需要用户名称和密码。安全策略中的用户名称是本地agent的用户名称和远端peer的用户名称的合并名称(通过括号分开),密码是远端peer提供的密码。这里的策略设置比较迷惑,读者一定要注意。

具体连接检查的处理方式如上图示例,根据连接检查的方向不同,用户名称和密码的选择组合是不同的。如果从L侧发起,L端agent需要对R端agent进行连接检查的话,它的用户名称组合方式是(RFRAG:LFRAG),密码是RPASS;反之亦然,如果从R端发起连接检查,其用户名称组合是(LFRAG:RFRAG),密码是LPASS密码。返回响应时,响应处理使用的用户名称/密码和请求中的相同(USERNAME属性不会出现)。
5 区分服务处理
如果agent在媒体数据中(例如在QoS中)使用了区分服务-Diffserv(遵从RFC2475),agent也应该对连接检查使用同样的标识方式。因为区分服务不在我们讨论的范围,具体关于Diffserv,读者查阅RFC2475,这里不做进一步讨论。
6 处理响应
当收到响应以后,这个响应需要使用事务ID和其绑定请求关联在一起。这个绑定关联的处理流程在RFC5389或RFC8489中定义。然后agent把响应绑定到候选配对上,这个候选配对是绑定请求以前发送的候选配对。针对具体的STUN使用中关于失败响应和成功响应的不同场景,这个部分定义了额外的步骤来处理这些流程。下面,我们专门针对失败响应处理和成功响应处理做进一步的介绍。
7 失败响应场景和成功响应场景详解
失败响应有很多种。如果STUN事务生成一个487错误码,这表示agent角色冲突。agent需要检查在请求绑定中是否包含了前面所说的拓展属性ICE-CONTROLLED或ICE-CONTROLLING。如果请求包含的是ICE-CONTROLLED属性,agent还没有完成角色切换的话,它必须切换到主控方角色。如果请求包含的是ICE-CONTROLLING属性,agent还没有完成角色切换的话,它必须切换到被控方角色。一旦agent完成切换的话,它必须对生成487错误的候选配对进行入队处理,然后这个队列进入到triggered check queue中。然后将此配对状态设置为等待状态。当triggered check发送以后,它将包含一个ICE-CONTROLLED或ICE-CONTROLLING属性,这个属性反映它新的角色。这里注意,tie-breaker的值一定不能重选。
角色切换以后,需要进一步的计算。因为不同角色具有不同的功能,因此agent需要重新计算配对优先级(前面文章有介绍)。另外,角色切换还会影响agent所负责的其他功能,例如基于ICE结果选择推荐配对,和生成更新的offer消息。除了ICMP错误以外,因为环境不同,可能STUN事务还能生成其他的错误响应。Agent也可以支持针对连接检查的ICMP错误接收的工作。如果STUN事务生成ICMP错误,agent将会设置此配对为错误状态。如果STUN事务生成的错误是不可恢复的错误或者超时错误的话,agent也会设置此配对的状态为错误状态。关于不可恢复的错误,读者可以查阅RFC5389-7.3.4章节关于错误的详解。
Agent必须检查源IP地址和响应端口是否等同于目的地地址和端口(目的地地址和端口是绑定请求发送过去的地址和端口),并且响应中的目的地地址和端口是否匹配源地址和端口(源地址和端口是从绑定请求中发送的)。换句话说,在请求和响应中的源地址和目的地传输地址是对称的。如果这两组地址不对称,agent将会设置此配对是失败状态。
以上笔者讨论的是检查失败响应的处理。现在,笔者讨论成功响应的处理。如果检查成功的话,检查成功,以下所有条件必须为真:
- STUN事务生成成功响应。
- 响应中的源IP地址和端口等同于目的地IP地址和端口(绑定请求中发送的)。
- 响应中的目的地IP地址和端口匹配源IP地址和端口(从绑定请求发送的)。
获得成功响应以后,agent需要进一步对响应消息和候选配对进行处理。其流程需要经过四个步骤的处理。笔者继续分别介绍这几个流程。
第一个步骤是发现peer的反射候选地址。Agent需要从响应中检查映射地址。如果传输地址不能匹配agent获知的任何本地候选地址,映射地址表示一个新候选地址,此地址就是peer反射候选地址。此反射候选地址和其他的候选地址一样,同样具有类型,基准地址,优先级和foundation。这四个属性的计算方式如下:
- 它的类型等同于peer的反射地址。
- 它的基准地址等同于候选配对中的本地候选地址,从地址来自于STUN检查发送地址。
- 它的优先级设置为绑定请求中的PRIORITY属性值。
- 它的foundation是通过foundation计算获得。
计算以后,peer反射候选地址(peer reflexive candidate)会添加到媒体流的本地候选地址列表中。对此媒体流来说,其用户名称和密码和其他本地候选地址的相同。但是,peer反射候选地址不会和其他远端候选地址配对。在此流程中,也没有必要进行反射地址和远端地址的配对处理。一对有效的配对会立刻通过构建有效配对的流程进行处理(此章节第二个步骤介绍)。如果agent希望peer反射候选地址和其他远端候选进行配对的话(这个远端候选地址不在将生成的有效配对内),agent可以生成一个更新的offer,在此offer中包含peer反射候选地址。通过这样的方式,agent可以完成此peer反射候选地址和其他远端候选地址配对的流程。
发现了反射候选地址以后,第二步的流程就是构建有效配对。agent可以开始构建一对候选配对。候选配对中,它的本地候选地址等同于响应中的映射地址,它的远端候选地址等同于绑定请求中发送的目的地地址。因为它们的有效性已经通过STUN连接检查验证,这样的配对称之为有效配对。读者需要注意,有效配对可能来自于不同的配对。具体来说,有效配对可能等同于检查生成的配对,可能等同于检查列表中不同的配对,或也可能是一对当前不在检查列表中的配对。如果这个配对是来自于检查流程生成的配对或者在当前检查列表的配对,它也可以被添加到有效列表中(VALID LIST)。agent为每个媒体流维护此列表状态。在ICE处理流程启动时,这个列表是为空状态,检查流程开始执行后在列表中逐渐增加配对,最后生成一个有效候选配对。
Agent经常也会遇到一些特殊状态-配对不在任何检查列表中。为什么会出现这样的情况呢?我们可以回顾一下这样的情景,检查列表有一对配对,其本地候选地址从来就不是一个反射候选地址。这种配对已经获得了它们的本地候选地址(这些地址已经转换成了服务器端反射候选地址的基准地址),如果这些配对重叠的话,需要对这些配对进行过滤筛选处理。当针对STUN检查的响应返回时,如果两个agent之间存在一个NAT时,映射地址将会是一个反射地址。这种情况下,有效配对将有一个本地候选地址,这个地址不能匹配检查列表中的配对的任何候选地址。
如果一对配对不在任何检查列表中的话,还要首先进行关于优先级的计算处理。Agent将会为此配对计算优先级。优先级计算基于每个候选地址优先级,其计算方式根据构建检查列表的流程来进行。本地候选地址的优先级基于其类型来决定。如果它不是peer reflexive,优先级等同于SDP中候选地址指示的优先级。如果它是peer reflexive,优先级等同于agent完成的绑定请求中的PRIORITY属性值。远端候选地址的优先级来自于对端peer的SDP中。如果没有出现远端候选地址,检查流程必须启动一个triggered check来生成一个远端候选地址。这种情况下,优先级来自于triggered check完成的绑定请求中的PRIORITY属性值。然后,这个有效配对被添加到有效列表中(VALID LIST)。
完成有效配对的构建,agent还要进行第三步处理流程。这个步骤就是更新有效配对的状态。Agent设置配对状态,这个状态是一个生成检查成功的流程。这里一定要注意,作为一种响应结果,这里的配对(生成检查流程)和构建有效配对是不同的处理方式(前面介绍过)。检查的成功可能引起其他检查的状态也发生改变。Agent必须按照以下两个步骤来执行:
- 针对同样媒体流和同样foundation,agent修改所有其他封冻状态的配对到一个等待状态。通常来说,也不总是这样,它们的其他配对将有不同的component IDs。
- 对此媒体流的每个构件来说,如果在有效列表中有一对配对,这个检查的成功可能对其他媒体流关闭封冻检查。注意,按照这一步的流程执行的话,对每个媒体流的构件来说,不仅是第一次有效列表配对在这种情况下所需要考虑按照这些流程执行,每一个后续检查获得的检查成功结果获得的配对都要添加到有效列表中。agent按照顺序对其他媒体流查询检查列表:如果检查列表是在活动状态,agent修改检查列表中所有封冻状态的配对(它们的foundation匹配了有效列表中等待状态的配对的foundation值)。
如果检查列表是封冻状态,并且在检查列表中至少有一对配对,它的foundation匹配了有效列表的配对的foundation,在检查列表中所有配对中,如果其foundation匹配了有效列表中的一对配对的foundation的话,所有列表中配对的状态会设置为等待状态。这样的处理结果会导致检查列表变为活动状态,ordinary checks开始为其工作。具体细节查阅笔者历史文档中的定时检查设置。
如果检查列表是封冻状态,并且检查列表中没有配对,它的foundation匹配有效列表中的配对的foundation的话,agent将执行以下处理流程,agent将会对所有的配对分组设置,所有配对有同样的foundation,并且,针对每个组设置,具有最低component ID的则设置其配对状态为等待状态,如果有一个以上这样的配对的话,则启用最高优先级的配对。
Agent完成了更新配对状态以后,最后agent将会执行第四步进行更新推荐配对处理。如果agent是一个被控方agent,它已经在绑定请求中包含了USE- CANDIDATE属性的话,从check生成的有效配对已经有一个推荐配对设置flag,这个设置为true状态。如果此配对的优先级在所有标识的配对中具有最高的优先级,这个标识则表示此媒体流或所有媒体流将使用这一对有效配对。如果agent是一个主控方agent,这个响应可能是triggered check的结果,这个triggered check在响应中返回到请求,此请求自己包含一个USE-CANDIDATE属性。这样的情况就是一个更新推荐配对示例,它可能导致对这个配对(这个配对是从原始请求学习获得)进行推荐flag设置。
以上介绍了失败响应和成功响应的处理。在处理响应的流程中,除了对失败响应和成功响应进行处理以外,还要对检查列表和定时器更新进行处理。无论检查是否是成功还是失败,最后事务完成需要更新检查列表和定时器的状态。如果所有在检查列表中的配对是失败或者成功的状态:
- 针对每个媒体构件来说,在有效列表中没有一对配对,检查列表的这个状态设置为失败状态。
- 对每个封冻的检查列表,agent以同样的foundation对所有配对进行分组
- 并且针对每个组,把带最低component ID的配对的状态设置为等待状态。如果有超过一个以上这样的配对,则使用具有最高优先级的配对。
如果在检查列表中没有任何配对在封冻或者等待状态的话,这个检查列表则不再认为是活动的检查列表,并且针对ordinary checks,检查列表不会在定时器计算中计入N的值。这里N是指活动检查列表数量。具体就是流程,读者可以查阅笔者上一篇历史文档关于设定定时检查的讨论。
本章节重点介绍了关于ICE连接检查中的STUN客户端的处理流程。为了避免让读者引起歧义,方便读者阅读,笔者将STUN服务器端处理流程独立分为另外一篇文章发布,关于STUN服务器端的处理流程将在下一篇文章中加以介绍。STUN服务器端主要流程包括两个场景的处理流程:首先是关于全场景部署agent的其他额外步骤处理(检测和修复角色冲突,计算映射地址,通过学习获得peer反射候选地址,Triggered Checks讨论和更新推荐配对标识),然后是关于轻量级部署agent的其他额外步骤处理。
参考资料
- https://www.rfc-editor.org/rfc/rfc5389
- https://www.rfc-editor.org/rfc/rfc8489
- https://www.rfc-editor.org/rfc/rfc5766
- https://ir.nctu.edu.tw/bitstream/11536/43505/1/655201.pdf
- https://www.cisco.com/c/en/us/td/docs/solutions/PA/ICE/icepa125.html
- https://dev.w3.org/2011/webrtc/editor/archives/20130830/webrtc.html
原文出处:完整SIP/SDP媒体协商概论-STUN连接检查-服务器端流程
在上一篇文章中,笔者讨论了STUN 连接检查的客户端的处理流程。接下来,在本章节,笔者继续讨论关于服务器端的处理流程。
客户端发送绑定请求后,对端agent必须准备好接收绑定请求,这个绑定请求是发送到了每个候选地址的基准地址。绑定请求包含在自己最近的offer或者answer消息中。因此,即使agent是一个轻量级部署场景的agent,它也要求部署方案维持一个状态来处理服务器端的流程。
接收绑定请求时,agent必须使用短期安全机制对请求进行认证和消息完整性检查。Agent必须考虑用户名称的有效性,如果用户名称含有两个用户名称构成的(通过冒号:分割),其中第一个用户名称等于一个用户名称,这个用户在此会话中,由agent在offer或者answer生成的名称。关于名称构成的讨论,完整SIP/SDP媒体协商概论-STUN连接性检查-客户端流程。有时也存在其他的可能性,例如,提供方offerer在收到对端peer的answer之前一个收到一个绑定请求。如果这样的情况发生,agent必须马上生成一个响应消息,响应消息中包含映射地址的计算数据。在这一时间点,agent已经具备了足够的信息生成响应消息。因此,agent就不需要对端peer的密码。一旦agent收到answer以后,这个answer消息就会马上通过几个步骤进行处理,包括检查修复角色冲突的流程,计算映射地址的流程,通过学习获得peer反射候选地址,Triggered Checks和更新推荐标识。提醒读者,以上这些流程都是针对全部署场景agent来说的。还有一些场景中,在收到answer之前,收到了多个STTUN请求,这样就会导致在triggered check 队列中生成多个配对队列。
Agent一定不能使用ALTERNATE-SERVER机制,也一定不能支持RFC3489规范的向后兼容机制,它必须使用FINGERPRINT机制。
如果agent在媒体数据中(例如在QoS中)使用了区分服务-Diffserv(遵从RFC2475),agent也应该使用同样的标识方式在绑定请求的响应中。因为区分服务不在我们讨论的范围,具体关于Diffserv,读者查阅RFC2475,这里不做进一步讨论。以下几个步骤的讨论将仅涉及全部署场景的agent处理流程。
1 检测修复角色冲突
正常情况下,通过agent的主控/被控方选择,双方可以选择自己的成功的角色。但是,在一些非正常的环境中,例如使用了第三方的呼叫控制,双方都成为同样的角色。下面,笔者将讨论如何检查同一角色冲突问题,以及如何修复同一角色冲突问题。
Agent必须检查绑定请求,其属性可能是ICE- CONTROLLING或ICE-CONTROLLED属性。如果要检查以上两种属性,agent需要按照以下流程进行:
- 如果在请求中,其属性既不是ICE-CONTROLLING,也不是ICE-CONTROLLED属性的话,说明agent没有遵从RFC5245规范,这里有角色冲突的问题,但是agent目前执行不了检测。
- 如果agent是一个被控方角色,并且请求中出现了ICE-CONTROLLING属性,接下来根据具体属性内容大小进行判断:如果agent的tie-breaker大于或等于ICE-CONTROLLING的内容,此agent生成一个绑定错误响应,错误响应中包含一个ERROR-CODE属性(487-角色冲突),这里,agent角色仍然保持不变。如果agent的tie-breaker小于ICE-CONTROLLING的内容,agent切换到主控方agent角色。
- 如果agent是一个主控方agent角色,并且请求中出现了ICE-CONTROLLED属性,接下来根据具体属性内容进行判断:如果agent的tie-breaker大于或等于ICE-CONTROLLED的内容,agent切换到被控方角色。如果agent的tie-breaker小于ICE-CONTROLLED的内容, 此agent生成一个绑定错误响应,错误响应中包含一个ERROR-CODE属性(487-角色冲突),这里,agent角色仍然保持不变。
- 如果agent是主控方角色agent,并且ICE-CONTROLLING属性出现在了请求中,或者agent是被控方角色agent,并且ICE-CONTROLLED属性出现在了请求中,双方角色无冲突。
修改了agent角色以后,需要对角色的状态进行修复。因为各自角色的优先级是主控方和被控方的主要执行功能,因此agent修改属性不是简单切换角色就完成了工作流程。修改角色需要agent重新计算配对优先级。这个计算方式我们在前面的历史文章中有非常详细地介绍,读者可查阅此文章。agent角色切换或者修改同样也会直接影响agent其他后续的功能执行,例如,它是否负责选择推荐配对,是否基于ICE结果生成更新offer的消息。
假设STUN服务器端对绑定请求生成了成功的响应的话,甚至于agent角色也发生了变化,agent可以继续执行其余服务器端的步骤:计算映射地址,通过学习获得peer反射候选地址等流程。
2 计算映射地址
对于在转发候选地址收到的请求来说,被STUN流程(即XOR-MAPPED-ADDRESS属性生成)使用的源传输地址是一个传输地址,这个传输地址是被STUN认为的传输地址。读者在计算映射地址是一定要注意,这里涉及了两种绑定请求的传输方式(Data Indication和ChannelData)。如果绑定请求通过Data Indication传输的话,这个源传输地址会出现在Data Indication消息的XOR-MAPPED-ADDRESS属性中。如果绑定请求通过ChannelData传输的话,这个源传输地址是绑定此channel通道的地址。关于Data Indication消息和ChannelData的定义细节,读者查阅以下两个规范草案:
- Data Indication:https://tools.ietf.org/id/draft-rosenberg-midcom-turn-08.txt
- ChannelData:https://tools.ietf.org/html/rfc5766-10
3 学习Peer Reflexive Candidates
如果请求中的源传输地址不能匹配任何现存远端候选地址的话,这说明对端的是一个新的peer反射候选地址。这个候选地址需要通过以下方式进行构建:
- 此候选地址的优先级设置为请求中获得的PRIORITY 属性值。
- 候选地址类型设置为peer reflexive。
- 候选地址的foundation设置为一个任意值,这个foundation必须区别于所有其他远端候选地址的foundation。如果在SDP的后续的offer/answer交互中包含了peer reflexive候选地址,这将表示对此候选地址来说,这是一个真实的foundation。
- 此候选地址的component ID设置为一个component ID,这个ID是为本地候选地址到远端地址(发送请求的地址)。
构建候选地址完成后,这个候选地址将被添加到远端候选地址列表中。但是,agent还不会使用本地候选地址和这个远端候选地址进行配对处理。在triggered check流程会进行本地候选地址和其配对的处理。
4 Triggered Checks处理
下一步,agent将会构建配对,在此配对中,本地候选地址等同于STUN请求接收的传输地址,远端候选地址等同于源传输地址(这是请求发出的地址),这个地址可能是通过学习获得的一个peer reflexive 远端候选地址。本地候选地址将是主机候选地址(这种情况下,请求可能不是通过转发候选地址获得),或者本地候选地址是一个转发候选地址(这种情况下,请求通过转发地址获得)。此本地候选地址从来不会是服务器端反射地址。因为这两个候选地址对agent来说都是可知的,所以,agent可以获得它们的配对,然后计算其候选配对优先级。接下来,这个配对会查询检查列表。通过查询检查列表可能获得其中一个结果:
- 如果配对已在检查列表中,继续执行以下四种检查:如果配对在等待或封冻状态,如果检查没有出现在triggered check队列中,把此配对的检查流程加入到triggered check队列中。如果配对在In-Progress 状态,agent将会取消这个事务处理。这里取消的意思是agent将不在重传请求,将不会认为缺乏响应是失败的,但是会在事务超时时间内对此响应执行等待流程。另外,通过对agent进行队列排序,此agent将会被加入到triggered check队列中,因此会让agent创建一个新的连接检查,然后,agent必须对此配对创建一个新的连接检查(重新作为一个新的STUN绑定请求事务)。最后,这个配对的状态切换为等待状态。如果配对状态是失败状态,此状态必须切换为等待状态,并且通过把agent加入triggered check队列,由此触发agent创建一个新的连接检查。接下来,agent必须为这一对配对创建一个新的检查连接(重新作为一个新的STUN绑定请求事务)。如果配对状态是成功状态,agent则无需执行进一步处理步骤。当双方agent在NAT背后时,通过以上步骤处理来支持ICE的快速处理。
- 如果配对不在检查列表时,需要进行执行以下流程:基于配对优先级,配对会插入到检查列表中配对状态设置为等待状态配对会加入到triggered check队列中
加入到triggered check队列以后,triggered check队列将会被发送出去。当triggered check队列发送以后,其构建和处理的流程按照前面讨论的发送请求来处理。具体关于发送请求的详情,读者可参与笔者上一篇文章,或者查阅RFC5245-7.1.2,这里不再重复。这些流程要求agent获得一个传输地址,部分用户名(username fragment),对端peer密码。这里读者还要再次注意用户名称和密码的设置。远端候选地址的用户名称(username fragment)等于收到的绑定请求中,冒号后面的USERNAME名称,agent使用此用户名称检查从peer收到的SIP消息(可能从多个分叉中检查),然后找到此用户名称(username fragment),然后通过此用户名称选择相应的密码。关于用户名称和密码构成的处理方式,读者可以参考上一篇文章中的介绍。
5 更新推荐Flag标识
如果必要,配对的推荐方式标识也是需要更新的。如果agent收到了一个绑定请求,绑定请求中已有一个USE-CANDIDATE设置属性,并且这个agent是一个主控方agent,agent将会查看配对状态(根据上一个讨论中的计算方式),并且继续进行判断处理:
- 如果这个配对状态是成功状态,这表示由这对配对生成的检查流程生成了一个成功的响应。这会引起agent的一个更新处理。当成功响应收到以后,agent会构建一对有效配对。关于构建有效配对的流程,读者可以参考笔者的客户端流程处理的文章。构建配对后,agent会将配对的推荐标识设置为true值。这样的设置方式可能会结束此媒体流的ICE处理流程。
- 如果配对状态是In-Progress状态的话,如果agent的检查流程生成了成功的结果,当响应抵达时,随之生成的有效配对已确定了的推荐标识设置。当响应抵达时,这样的设置方式可能会结束此媒体流的ICE处理流程。关于ICE结束处理流程的讨论,笔者将在下一篇文章做做详细说明。
6 针对Lite部署的额外流程处理讨论
本文章中以上讨论的都是基于全场景部署的agent的讨论。轻量级的部署场景agent的处理相对比较简单,使用的场景也不是非常普遍,因此没有太多的约定条件(没有优先级,foundation等计算设置,没有队列检查等流程)。这里,笔者针对轻量级部署场景的agent做一些简单说明,希望读者引起注意。如果收到的check消息中包含了USE-CANDIDATE设置属性,agent将需要构建一个候选配对。其中候选配对中,本地候选地址等于传输地址(收到请求的地址),远端候选地址等于一个源传输地址(注意,这个地址是收到请求的源传输地址)。构建成的候选配对设定一个任意优先级,然后推送到有效候选列表中(valid list)。Agent然后设置这对候选配对的推荐标识为true。针对媒体流的每个构件模块,如果这个有效候选列表包含了一对候选配对,媒体流的ICE处理流程将被视作完成状态。
到此为止,结合上一篇文章中关于STUN客户端处理流程介绍,笔者已经完成了关于ICE连接检查的讨论(客户端处理流程和本章节的服务器端处理流程)。在接下来的章节中,笔者将重点讨论关于ICE结束处理的流程。
参考资料
原文出处:完整SIP/SDP媒体协商概论-关于ICE流程结束处理
笔者在前面的两篇文章中分别介绍了STUN客户端处理流程和服务器端处理流程。在本文章中,我们将针对ICE的最后一部分的处理进行总结,这个章节包括ICE的流程结束的处理。ICE流程结束需要从两种部署场景的agent来讨论。一种是全场景部署agent的处理流程,另外一种是轻量级的部署场景agent处理流程。另外,ICE结束流程的最后还要进行对封冻候选地址的处理。和前面的文章一样,笔者这里仍然还是重点讨论全场景部署agent的处理流程,然后针对轻量级场景中agent的部署进行讨论,最后对ICE结束对封冻候选地址进行讨论。
1 全部署场景处理流程
针对全场景部署agent的处理流程中,关于ICE结束的流程涉及了推荐配对和状态机更新两个部分的内容。其中,推荐配对是由被控方agent产生。接下来,笔者会继续讨论推荐配对的处理流程。
2 推荐配对
刚才笔者已经提到,推荐配对是由被控方产生的。ICE通过Regular Nomination(正常推荐方式)或Aggressive Nomination(主动推荐方式)来选择推荐配对。选择何种方式对推荐配对进行算法处理,取决于对端pper的部署场景。如果对端peer支持的是一个轻量级部署场景,agent必须使用Regular Nomination算法来处理。如果对端peer使用了ice-options属性(ICE options),agent不能理解此选项的话,agent必须使用Regular Nomination算法。如果对端peer支持全场景部署,并且没有使用任何ICE选项或者使用了ICE选项(但是,agent可以理解),agent可以选择使用Regular Nomination或者Aggressive Nomination算法。但是,因为Regular Nomination的稳定性比Aggressive Nomination要好,所以,RFC5245规范推荐使用Regular Nomination算法。下面,笔者分别对这两种算法进行完整介绍。

当推荐配对使用Regular Nomination时,agent会让一些检查流程完成处理,在检查的每个流程中会忽略掉USE-CANDIDATE属性。针对一个媒体流构件来说,一旦一个或多个检查成功完成,成功检查完成后会生成有效配对,有效配对然后添加到有效列表中。Agent会让检查继续执行,直到检查遇到某些评判标准,然后根据某些评判标准选择有效配对。停止检查的评判标准和评估有效配对标准完全取决于逻辑优化本身自己。
当被控方agent选择了一对有效配对时,它会重复这个生成有效配对的检查,并且这次使用USE-CANDIDATE属性。因为前面的检查是成功的,所以,理论上讲,这个检查应该也是一个成功的检查。针对检查的逻辑处理方式会对配对增加一个推荐标识(nominated flag),并且仅支持这个配对。因此,针对每个媒体构件来说,在有效列表中仅有一个单个支持了推荐标识配对,并且,当检查列表的状态切换到完成状态后,ICE会选择正确配对来针对媒体构件发送和接收媒体流。根据以上介绍,读者可以看出,因为agent可以控制检查停止和检查的评判标准,因此,Regular Nomination具有更大的灵活性。这里只有一个要求,agent最后选择一个配对或者只能选择一个候选配对,然后针对此配对(支持USE-CANDIDATE属性)生成一个检查。通过增加拓展支持,Regular Nomination同时提高了ICE的适应能力,可以支持多种部署场景的变化。Regular Nomination具有更高的稳定性,它可以允许双方agent通过单个配对实现媒体支持,无需其他临时选项支持(Aggressive Nomination需要临时选项支持)。但是,Regular Nomination也有其缺点,因为Regular Nomination需要额外检查完成,因此,Regular Nomination肯定会增加处理时延。通过以上内容的介绍,笔者描述了Regular Nomination算法的处理方式,接下来,笔者介绍一下Aggressive Nomination算法的使用方式。
当使用Aggressive Nomination算法时,在agent发生的每个检查中,被控方agent会包含一个USE-CANDIDATE属性,针对一个媒体构件的检查中,一旦第一个检查是成功的,这个配对就会被添加到有效列表中,然后设置一个推荐标识。在有效列表中,所有构件的配对都设置了推荐标识后,媒体开始通过最高优先级的推荐标识配对进行传输。但是,因为agent在所有的检查中都包含了USE-CANDIDATE属性,在所有的检查中,有可能部分其他的检查还没有完成,这样就会引起其他有效配对会产生自己的推荐标识设置。因为,ICE总是从有效列表中选择最高优先级推荐标识的候选配对来进行媒体传输。因此,当ICE检查完成时,已选择的配对可能实际上暂时发生了修改,这样就会导致一系列的临时选择作为一个缓冲(三秒延迟规则,后面讲到),直到ICE检查稳定后,这样的状态才能结束。因为这个问题,因此,相对于Regular Nomination算法来说,Aggressive Nomination缺乏一定的稳定性。但是它不会增加检查延时。
3 更新ICE处理状态
无论是对于主控方agent还是被控方agent来说,ICE的处理状态取决于在有效列表中标识候选配对的当前状态和检查列表的状态。任何时间内,以下五种场景可能发生在这些处理状态中。现在,我们具体介绍一下这五种可能发生的场景。
- 对媒体流来说,如果在有效列表中没有已设推荐标识的配对,并且检查列表状态是正在运行中,ICE处理将会继续执行。
- 对媒体流来说,如果在有效列表中至少有一对已设推荐标识的配对,并且检查列表状态是正在运行中,则继续进行以下处理流程:Agent必须移除在检查列表中所有等待状态和封冻状态的配对,并且为同样component(构件)启动一个triggered check queue,作为标识配支持此媒体流。如果在检查列表中的一个In-Progress配对作为标识配对支持同样构件的话,如果配对的优先级低于最低优先级标识配对,针对此构件来说,agent应该清除检查的重传处理流程。
- 对于至少一个媒体流的每个构件来说,在有效列表中一旦至少有一对标识配对,并且检查列表的状态是正在运行中的状态,需要进一步执行以下流程:
Agent必须为此媒体的检查列表进行状态修改,修改其状态为完成状态。
Agent必须继续对任何检查做出响应,这些检查可能仍然是agent用来接收媒体的响应,并且,如果STUN服务器端流程要求的话,agent必须执行triggered check。
Agent必须为检查列表继续重传任何In-Progress 检查。
Agent可以开始为媒体流传输媒体,为全场场景部署agent和轻量级场景部署agent发送媒体的处理流程将在后续文章中介绍。
一旦每个检查列表的状态是完成状态,要求执行以下流程:Agent设置所有的ICE处理状态是完成状态。如果agent是正在处于被控状态,此agent会为每个媒体流的每个构件检查最高优先级已标识的候选配对。如果它们中的任何候选配对不同于在最近offer/answer交互消息在的默认候选配对,被控方agent必须生成一个更新的offer。具体的生成方式根据后续offer/answer交互模式的处理流程进行。
如果被控方agent正在使用Aggressive Nomination,当配对选择时,它可能导致多个更新offers。Agent可以延迟发送此更新的offe r,通过一定的时间设置进行控制(RFC5245规范建议设置为一秒钟),这个时间可以保证其已选配对进入稳定状态。如果检查列表的状态是失败状态,ICE将不能为完成针对媒体流的处理。正确的处理方式依赖于对其他媒体流的检查列表状态:如果所有的检查列表的状态是失败状态,所有ICE处理的状态将认为是失败状态,并且,agent应该认为此会话是一个失败的会话,agent不应该重新启动ICE处理流程,被控方agent应该结束整个会话。针对其他媒体流来说,如果在检查列表中至少有一个检查的状态是完成状态,被控方agent应该在其更新的offer中从此会话中移除此失败的媒体流。针对其他媒体流来说,如果在检查列表中没有任何检查是完成状态,但是,至少有一个检查是正在运行状态,agent应该让ICE进行执行。
到此为止,关于ICE结束处理中全场景部署agent的处理流程就已经结束。接下来,笔者将讨论针对轻量级部署场景agent对ICE结束流程的处理。
4 轻量级部署场景处理流程
针对轻量级部署场景agent来说,ICE结束流程相对比较直接。这里有两个情况需要注意:
- 部署场景是轻量级的部署场景,对端peer是全场景部署场景
- 部署场景是轻量级部署场景,对端peer也是轻量级部署场景
ICE结束处理会给agent带来一个后续影响,agent能够清除任何已分配的候选地址(ICE没有使用这些候选地址)。关于清除已分配候选地址的处理方式,笔者在 本文章的后续部分介绍。
如果对端peer是全部署场景的话,这种情况下,agent将会收到peer的连接检查。当agent已经收到来一个连接检查,这个检查中包含了针对一个媒体流的每个构件所支持的USE-CANDIDATE属性,此媒体流的ICE处理状态将要从正在运行状态迁移到完成状态。当针对所有媒体流的ICE处理状态是完成状态的话,所有ICE处理状态都是完成状态。轻量级部署从来不自己决定针对媒体流的ICE流程失败处理,而是宁愿让全场景部署的agent来决定。在后续的offer中,针对失败的媒体流,全场景部署将做出决定,移除或重新启动这些失败的媒体流。
如果对端peer是轻量级peer的话,ICE结束处理的流程相对比较复杂一些。一旦offer/answer交互模式完成后,双方agent都会检查它们的候选地址和它的peer的候选地址。针对每个媒体流,每个agent将会使用自己候选地址和对端peer的候选地址进行配对处理。当它们具有同样的component,使用同样的传输协议(RFC5245使用UDP传输协议),并且来自于同一IP地址类型(IPv4或IPV6),那么这两个候选地址就会配对。在生成配对后,针对每个媒体流构件中的配对数量有两种处理方式:
- 如果在每个媒体构件中,有一个单个配对的话,此配对会添加到有效列表中。如果一个媒体的所有构件只有一个配对的话,针对此媒体流的ICE处理状态将会设置为完成状态。如果所有媒体流的ICE处理状态是完成状态的话,所有ICE处理状态将会设置为完成状态。这种部署环境经常会发生在IPv4的网络环境中。
- 如果在每个构件中,有多于一个以上的配对的话,需要执行以下几个步骤:Agent必须基于本地策略选择一个配对。因为,这种情况仅发生在IPv6的环境中,RFC5245推荐agent需要根据RFC6724的处理流程选择一个单个配对。Agent会在有效列表中为每个构件添加此已选配对。然后允许开始发送媒体。这里要注意,但是在实际环境中,可能双方agent已选择了不同的配对。为了保持双方agent的配对的一致性,被控方agent必须发送一个更新的offer,在这个更新的offer中包含一个remote-candidates属性设置。关于更新offer的发送处理流程,笔者在将来的讨论中介绍。当offer发送以后,agent一定不能更新ICE处理状态。针对所有媒体流,如果此后续offer完成处理,主控方agent必须修改ICE处理流程状态为完成状态,并且设置所有ICE处理状态为完成状态。主控方agent的状态设置则基于轻量级部署场景的流程来进行处理。
5 封冻候选地址
在ICE结束处理的流程中,包括两种对后选地址的封冻处理。一种是全场景部署中封冻处理,另外一种是轻量级的部署场景封冻处理。接下来,笔者分别对其流程进行说明。
首先介绍全场景中关于封冻处理的流程。ICE结束处理的流程要求agent继续监听STUN请求,一旦对其媒体流的处理完成后,继续对媒体流生成triggered checks。RFC5245介绍了一种规则,规则描述了当agent处于安全状态时,它在一个候选地址(ICE没有选择此候选地址,然后释放候选地址)终止发送或接收检查。
当SIP网络中使用了ICE,并且offer被经过分叉处理发送到多个接收方时,ICE将会使用同样的本地候选地址并行和每个自己的answer进行处理。对所有使用那些候选地址来传输媒体流的peers来说,一旦ICE处理流程状态达到完成状态,agent应该等待另外三秒钟,然后agent可以终止对检查的响应,或在那个候选地址生成一个triggered checks。Agent可以在那个等待时间释放候选地址。注意,服务器端反射候选地址的封冻处理从来不会被明确声明,keep alive的缺失会启动封冻处理流程发生。三秒延迟的规则策略可以处理如下场景,具体来说,ICE已经完成后,agent使用了aggressive nomination算法,然后对已选配对进行快速修改。
轻量级部署场景中ICE结束处理相对比较简单。对所有使用那些候选地址来传输媒体流的peers来说,一旦ICE处理流程状态达到完成状态,轻量级部署场景可以释放任何没有被ICE选择的候选地址。
完成了关于ICE的讨论以后,笔者将进一步讨论关于后续offer/answer交互中的offer生成,answer接收,更新offer等细节。
参考资料
- https://www.rfc-editor.org/rfc/rfc6724
- https://www.rfc-editor.org/rfc/rfc3484
- https://www.rfc-editor.org/rfc/rfc5245.html
- https://datatracker.ietf.org/meeting/93/materials/slides-93-mmusic-3
- https://bugzilla.mozilla.org/show_bug.cgi?id=1034964
原文出处:完整SIP/SDP媒体协商概论-后续offer处理-Offer
根据RFC3264的规范,agent的任何一方都可以生成后续offer。在笔者前面的文章中,笔者讨论了关于ICE结束处理的流程的规则,这个结束ICE流程的规则会导致被控方agent发送一个更新offer。具体来说,当ICE从默认配对中已选择了不同的配对时,在ICE结束流程中,被控方agent会发送一个更新offer消息。但是,前面的文章中没有详细介绍agent如何实现更新offer,接收offer,以及最后更新检查连接和有效列表的步骤。如果agent要实现更新offer的处理流程,双方agent需要经过三个主要的流程。Agent需要首先要经过offer消息构建,然后对端peer接收更新的offer,生成更新的answer消息,最后更新连接检查和有效列表。
因为更新offer的内容比较庞杂,为了让读者有一个比较好的阅读体验,笔者计划将上述内容分为三个部分来介绍。本文章介绍第一个部分的内容(生成后续offer),在以后的文章中分别介绍第二部分和第三部分的内容。
本文章主要涵盖关于生成后续offer的内容:
- 整体部署场景讨论(ICE 重新启动,移除媒体流,增加媒体流)
- 全场景部署讨论:ICE运行时现存媒体流处理和ICE完成后现存媒体流处理。
- 轻量级场景部署讨论:ICE运行时现存媒体流处理和ICE完成后现存媒体流处理。
在讨论后续offer/answer交互模式时,首先需要关注的是关于如何生成更新的offer消息。在生成更新offer消息中,RFC5245分别规定了两种常见的处理流程(全场景部署场景和轻量级部署场景)。接下来,笔者会首先对整体部署场景进行讨论,然后分别对两种部署场景中关于不同ICE运行状态环境中现存媒体流的处理进行讨论。
注意:关于ICE重新启动的细节,RFC5245和RFC8445规范有非常某些区别,笔者这里以RFC5245的规范作为讨论基础。如果对最新的规范有兴趣的话,可以查阅RFC8445的细节。
1 整体部署场景讨论
在讨论后续offer/answer交互模式时,首先需要关注的是关于如何生成更新的offer消息。在生成更新offer消息中,针对agent,RFC5245分别规定了两种常见的处理流程(全场景部署场景和轻量级部署场景)。在介绍这两种部署场景的处理流程之前,我们首先介绍三个和整体部署场景相关的内容。第一个是关于重新启动ICE流程的讨论。
Agent可以对现存的媒体流重新启动ICE处理流程。重新启动ICE处理流程会导致前面的ICE处理流程被刷新,并且还要重新启动检查流程。注意,重新启动ICE处理流程和重新创建一个新会话之间有不同之处,在重新启动ICE处理流程的过程中,媒体流仍然会被发送到以前的有效配对目的地。对于一个媒体流来说,如果有以下两种情况可能发生的话,agent必须重新启动ICE流程:
- 已经生成了offer,此offer的目的是为了修改媒体流目的地。简单来说,就是agent想生成一个更新的offer消息(ICE没有使用这个offer),这样的结果是为媒体构件目的地生成一个新的值。
- Agent正在修改其部署级别。正在情况一般发生在第三方呼叫控制的环境中。对象实体正在使用的信令不是实体接收媒体的信令,并且这个实体已经从媒体 mid-session的目的地修改为另外一个实体,这个实体具有不同的ICE部署环境。
另外一些规则的修改也会引起ICE重新启动,SDP中的c行中IP地址修改为0.0.0.0会引起ICE重新启动。因此,如果需要支持呼叫等待业务功能的话,ICE部署中则不能使用以上机制,它必须使用a=inactive和a=sendonly设置。因为这里涉及了IPv6的网络场景,旧规范RFC3264已经做了更新,关于此要求最新细节,建议读者可以参考RFC6157-4.1章节的介绍。如果agent要为媒体流重新启动ICE流程的话,agent必须在offer消息中修改认证用户需要的两个属性ice-pwd和ice-ufrag。注意,RFC5245规范也允许agent在offer中使用会话级的属性设置,仅为在后续的offer中作为一个媒体级属性提供同样的ice-pwd或ice- ufrag。这样的操作不会修改密码,不会修改其呈现方式,也不会引起ICE重新启动。Agent在SDP中设置了多个其他的属性,这些属性在包含在初始化的offer中。针对此媒体流,在接下来的更新offer的处理流程中,候选地址组会根据实际情况,可能会包括其中一些参数或者没有包含任何参数,或者前面的候选地址,并且可能包括全新采集的候选地址组。候选地址采集的采集通过采集候选地址来执行(关于候选地址场景请参考历史文章)。
根据RFC3264的规范规定,如果agent需要移除媒体的话,它可以设置其端口为0。这个规则在这里也适用。除了agent设置端口为0以外,针对要移除的媒体流,它一定不能包含任何候选地址属性,并且不应该包括任何ICE相关的属性设置。具体的ICE相关属性,参考如下示例:

如果agent想增加一个新的媒体流的话,它需要在SDP中为此媒体流设置相关的域值。这些SDP中的相关域值设置需要参考初始化offer中关于SDP解码中的设置。Agent修改了这些媒体流的设置后,ICE处理流程就会开始发送媒体流。
以上内容介绍了整体部署环境中关于ICE重新启动,移除媒体流和删除媒体流的处理流程。接下来,我们将首先针对全场景部署环境中处理流程进行说明。
2 全场景部署环境处理流程
这个章节将对在全场景部署环境中,在不同ICE运行状态下对现存媒体流进行的处理做详细说明。这里要注意这三个参数环境,用户名称(ice- ufrag),密码(ice-pwd)和部署级别,前面使用的这三个条件没有发生改变。如果agent需要修改其中一个参数条件,ICE就需要重新启动。
如果agent生成了一个更新的offer,在更新offer中包括了一个以前已创建的媒体流,并且ICE检查也在运行状态时,agent需要根据以下流程执行。针对此媒体流,此agent必须为所有本地候选地址包括候选属性。在SDP标识的候选地址的属性,例如,priority,foundation,type和相关传输地址都应该保持不变。候选地址中基础的确认信息,例如,IP地址,port和传输协议一定不能发生变化(如果其中一个发生变化,则说明是一个新的候选地址)。component ID和以前的component ID保持一致。Agent可以包括以前offer中没有生成的其他候选地址,但是,自从上一次offer/answer交互以后,对agent已经采集的候选地址来说,需要包括一个peer reflexive candidates。针对媒体来说,就像在初始offer中的一样,肯定有一组候选属性可以匹配默认目的地地址,agent可以为此媒体修改默认目的地地址。
以上是ICE在运行状态时,agent更新offer需要执行的流程。除了ICE处于正在执行状态以外,ICE检查也可能是完成状态。如果agent生成了一个更新的offer,在更新offer中包括了一个以前已创建的媒体流,并且ICE检查在完成状态时,agent需要根据以下流程执行。针对媒体的默认目的地地址(例如,媒体流的m行和c行中的IP地址和端口值)必须是本地候选地址,这个本地候选地址来自于对每个媒体构件的有效列表最高优先级推荐配对。通过这样的处理方式可以“修复”目的地地址,让默认媒体的目的地地址等于ICE为此媒体的已选地址。Agent必须包含一些候选地址的候选属性,这些候选属性匹配了媒体流中每个构件的默认目的地地址,并且agent一定不能包含其他候选地址。另外,如果agent是一个被控方agent的话,它必须为每个媒体流(这些媒体流的检查列表是在ICE完成状态)包含a=remote-candidates属性。这个属性设置中包含远端候选地址,这些地址来自于此媒体流的每个构件所支持的有效列表中的最高优先级配对取值。这里可能存在一种极端情况,这种情况需要尽量避免。被控方agent选择了它的配对,但是更新offer对主控方agent执行了一个序乱的连接检查。这种情况下,对端agent可能不知道哪个配对是有效配对。因此,在更新offer使用a=remote-candidates属性避免更新offer和STUN检查协议之间因为请求/响应消息丢失出现的这种极端情况。注意,这种极端情况的处理机制讨论仅在RFC5245的规范中有规定,但是在RFC8445中取消了这种处理来自。相对于RFC5245,RFC8445针对ICE重新启动做了大幅简化处理。

3 轻量级部署环境处理流程
这个部分的讨论和前面的讨论应用,笔者也会分别针对两种ICE运行状态下的轻量级部署环境的处理流程进行介绍。
首先说明一下轻量级部署中针对现存媒体流,ICE流程处于运行状态时的处理流程。轻量级部署必须在任何后续的offer中的a=candidate为每个媒体流的每个构件的包含所有的候选地址。候选地址的构建和初始化offer中关于候选地址的构建的流程完全相同。具体的构建流程在前面的历史文章中有专门介绍,读者可以查阅历史文档。轻量级部署中一定不能在后续offer中再增加其他的主机候选地址。如果agent想增加一个新的主机候选地址的话,agent必须重新启动ICE处理流程。在后续offer中的用户名称(ice- ufrag),密码( ice-pwd)和部署级别应该和前面offer中的属性保持一致,如果以上三种参数后续offer中发生修改,agent则必须为此媒体流重新启动ICE处理流程。
针对媒体流来说,如果ICE处理状态处于完成状态,此媒体流的默认目的地地址必须设置为此媒体流构件的候选配对(在有效列表中)的远端候选地址。对轻量级部署来说,总是支持一个在有效列表中的单候选配对。另外,agent必须为每个默认目的地包含一个候选地址属性。
如果agent是一个被控方agent的话(仅发生在双方都是轻量级部署的agent),此agent必须为每个媒体流包含一个a=remote-candidates。a=remote-candidates中包含远端候选地址,这个远端候选地址来自于有效列表中的候选配对(每个媒体流的每个构件有一对候选配对)。
在接下来的章节中,笔者将继续介绍针对更新offer中对端agent接收offer和生成answer的处理流程。
参考资料
- https://www.rfc-editor.org/rfc/rfc3264
- https://www.rfc-editor.org/rfc/rfc6336
- https://www.rfc-editor.org/rfc/rfc8445
原文出处:完整SIP/SDP媒体协商概论-后续offer处理-answer生成
在上一篇文章中,笔者介绍了后续offer中关于offer生成的讨论。今天,笔者将继续介绍peer接收offer和生成answer响应的细节,主要涵盖细节包括:
- 整体部署场景讨论(检测ICE重新启动,移除媒体流,增加媒体流)
- 全场景部署讨论:无remote-candidates属性状态下,ICE运行时现存媒体流处理和ICE完成后现存媒体流处理,带remote-candidates现存媒体流处理。
- 轻量级场景部署讨论:ICE运行时现存媒体流处理和ICE完成后现存媒体流处理。
读者注意,在这里讨论的offer/answer已经涉及到了后续offer的内容,也就是更新的offer,所以有时会和初始offer时的一些属性进行对比处理,读者一定不要迷惑。
1 整体部署场景讨论
当agent在现存会话中收到后续offer时,无论前面的offer/answer交互验证是否成功,agent必须重新检查一次验证ICE支持能力(前面的文章中介绍过接收初始offer关于ICE能力支持验证流程)。虽然,前面offer/answer交互可能导致ICE不能使用,不过,这种处理结果可以作为后续offer/answer交互的结果使用。和上一篇文章中所讨论的应用,agent接收offer时,对于整体部署场景来说,仍然需要执行常规的三个步骤的处理:检测ICE重新启动,添加媒体流处理,移除媒体流处理。现在,我们分别加以介绍。
用户属性发生了修改,agent需要重新检测并且重新启动ICE。具体来说,agent和前面收到的SDP中属性对比,如果agent在收到的更新offer消息中包含了一个已修改的属性(可能是a=ice-ufrag 或者a=ice-pwd),针对此媒体流,agent需要重新启动ICE。如果所有的媒体流需要重新启动的话,所有ICE也需要重新启动。如果一个媒体流的ICE重新启动的话,agent会在answer中执行两种修改处理:
- Agent必须修改answer中的a=ice-ufrag和a=ice-pwd。
- Agent可能在answer消息中修改其部署层级。
针对媒体流,agent会修改SDP中其他属性设置,属性设置和和初始answer中的处理流程相同。针对此媒体流,在接下来的处理流程中,候选地址组会根据实际情况,可能会包括部分参数,无参数,或者包括前面的候选地址参数,并且可能包括全新采集的候选地址组。
如果agent收到一个offer,offer中包含了一个新的媒体流,针对此媒体流,就像agent在初始offer包含的媒体流一样,agent必须在answer中设置相关的域值。在answer设置以后就会导致ICE重新启动。
如果agent收到一个offer,offer中包含了一个媒体流,其端口设置为0的话,agent一定不能为此媒体流在answer中包含任何候选地址属性,并且不应该包含任何和ICE定义的相关属性,例如foundation和component-id等。
2 全场景部署环境处理流程
这个章节将对在全场景部署环境中,在不同ICE运行状态下对现存媒体流进行的处理做详细说明。这里要注意这三个参数环境,用户名称(ice- ufrag),密码(ice-pwd)和部署级别。除了agent已经从offer消息中检测到ICE重新启动之外,用户名称(ice- ufrag),密码( ice-pwd)和部署级别必须维持不变。如果agent想修改其中一个属性值的话,agent通过生成一个新的offer来重新启动ICE。agent不能在answer消息重新启动ICE处理流程。其他的流程取决于此媒体流的ICE运行状态。笔者将结合不同remote-candidates属性出现的以下三种状态进行讨论。
如果针对一个媒体流的ICE的状态处于运行状态,并且此媒体流的offer已锁定了remote-candidates属性,构建answer消息的规则和笔者上一篇关于offer生成的流程相同。
如果针对一个媒体流的ICE状态处于完成状态,并且此媒体流的offer已锁定了remote-candidates属性,构建answer消息的规则和笔者上一篇关于offer生成的流程相同,另外,应答方answerer一定不能在answer消息中包括a=remote-candidates属性。
以上两种状态都是锁定了remote-candidates属性的。如果remote-candidates是一种可变状态,出现了竞争条件的话,现存媒体流的处理就需要做另外处理。当agent对端peer对一个媒体流已包含了ICE处理流程的话,主控方agent将会收到一个带a=remote-candidates属性的offer消息。出现在offer中的这个属性用来处理介于offer接收和响应接收之间的竞争条件,通知answerer接收方ICE将会选择一个候选地址。关于竞争状态的流程,读者可以查阅RFC5246-appendix-B.6。接下来关于针对offer携带a=remote-candidates的处理流程完全取决于竞争条件中协商胜方的处理流程。
Agent为媒体流的每个构件生成一个候选配对,它需要远端候选地址和本地候选地址,具体处理流程为:
- 设置远端候选地址等于offerer提供方中的默认目的地地址。例如,针对RTP的m行和c行内容,和RTCP的a=rtcp。
- 设置本地候选地址等于传输地址,此传输地址是支持在offer中a=remote-candidates同一构件。
生成候选配对完成以后,agent看到候选配对会出现在有效列表中。如果有一个配对没有出现在有效列表中的话,说明检查流程失去了竞争条件。这样的配对称之为"losing pair"(丢失的配对)。接下来,agent会在检查列表中通过远端候选地址查找,查找所有远端候选地址等于丢失配对中的远端候选地址的配对,执行以下流程:
- 如果这些配对中无配对在In-Progress处理状态,并且至少一个配对是失败状态,这可能是因为网络问题导致,例如可能发生了网络隔离问题,严重的网络丢包。这样可能就没有出现remote-candidates,agent应该为此媒体流生成一个answer,然后为此媒体流重新启动ICE流程。
- 如果这些配对中至少有一对配对在In-Progress处理状态,agent应该等待它们的检查流程完成,并且当每个检查完成后,重新执行这部分流程,直到再没有丢失配对需要处理。
一旦完成了没有再需要处理的丢失的配对以后,agent就可以生成一个answer消息。它必须为此媒体设置默认目的地地址,默认目的地地址为remote-candidates(来自于offer)中的候选地址。它必须为此每个候选地址在answer中包括一个候选地址属性,这里的每个候选地址是在offer中remote-candidates属性中。
3 轻量级部署环境处理流程
如果在收到的offer中包含remote-candidates属性,agent为媒体流的每个构件生成一个候选配对,它需要远端候选地址和本地候选地址,具体处理流程为:
- 设置远端候选地址等于offerer提供方中的默认目的地地址。例如,针对RTP的m行和c行内容,和RTCP的a=rtcp。
- 设置本地候选地址等于传输地址,此传输地址是支持在offer中a=remote-candidates同一构件。
针对此媒体流,agent然后把这些候选地址迁移到有效列表中。ICE处理流程状态设置为完成状态。
除了以上设置以外,agent控制角色也是一个问题需要讨论。如果agent认为它自己是主控方agent,而且在offer消息中包含了remote-candidates属性,双方agent都认为自己是主控方agent。这种情况下,双方agent将可能都已经同时对对方发送了更新offer消息。这种极端情况需要负责传输offer/answer交互的信令协议来解决。最后,其中一方agent总是以竞争环境中的胜方出现。在信令协议传输offer/answer交互的流程中,对端peer发送offer之前,胜方agent会首先收到一个offer消息。胜方agent会充当主控方agent的角色,因此,这里所讨论的应答方answerer必须修改其角色为主控方角色。接下来,如果agent基于一个流程(根据RFC5245-8.2.2)发送更新offer的话,这个agent就是一个主控方agent。
除了以上控制角色修改以外,构建answer的执行流程中关于有效列表中的修改和ICE状态修改和构建offer流程的相同。读者可以查阅上一篇文章做进一步了解。
本章节介绍了在更新的offer中关于接收offer消息和生成answer消息的处理过程。接下来的章节笔者将介绍在后续offer/answer交互中check更新和有效列表更新的细节。
原文出处:完整SIP/SDP媒体协商概论-ICE选项和keepalives讨论
笔者在前面的完整介绍了关于后续offer/answer交互过程中offer接收和answer生成的细节。这里,笔者将介绍后续offer/answer交互中的最后一个部分-ICE选项支持,状态更新以及存活时间的讨论。在更新状态中又涉及了全场景部署的处理流程和轻量级场景的部署流程。现在,我们逐一介绍这细节。
1 全场景部署处理流程
在全场景部署的更新处理流程中涉及了四个方面的内容需要讨论。首先,我们介绍一下关于ICE重新启动的内容。
在ICE重新启动之前,针对媒体流的每个构件,agent必须记住在有效列表中的最高优先级标识的配对(称之为历史已选配对)。Agent将会继续使用这些配对发送媒体流。发送媒体流的流程在后面的文章中会加以讨论。一旦目的地地址收到提示信息,agent必须刷新有效列表和检查列表中的数据。然后agent重新计算检查列表和其状态。具体处理流程参考历史文章中关于构建检查列表的流程。
如果在offer/answer交互中已添加了一个新的媒体流,agent必须为此新媒体流创建一个新的检查列表,还创建一个新的初始为空的有效列表。具体处理流程参考历史文章中关于构建检查列表的流程。
如果在offer/answer交互要移除一个媒体流或answer拒绝了offer中提供的媒体流的话,agent必须为此媒体流刷新有效列表,必须结束在任何处理过程的STUN事务。agent必须为此媒体流移除检查列表,并且取消任何待处理的ordinary checks。
针对全场景部署中的更新有效列表和检查列表,ICE的处理根据agent的状态和检查列表状态的不同存在很多不同流程。首先说明,除非正在ICE重新启动,否则有效列表是不会受更新offer/answer交互影响。
针对一个媒体流来说,如果agent的状态是正在运行状态,检查列表是已更新状态(如果状态是完成状态,检查列表已无相关性)。为了实现这个要求,agent必须使用计算流程重新计算检查列表。具体处理流程参考历史文章中关于构建检查列表的流程。在计算结果中,如果发现在检查列表中有一对配对已经是全面检查列表中出现的配对的话,其配对状态是Waiting,In-Progress,Succeeded或Failed状态的话,其状态将被拷贝到检查列表中;否则其状态将设置为封冻状态(Frozen)。
如果检查列表中无任何活动配对(意味着每个检查列表中的配对是封冻状态),full-mode(双方agent都使用了ICE)的agent为第一个媒体流在有效列表中设置第一个配对,设置的第一个配对的状态为等待状态,然后把在检查列表中具有同样component ID和具有同样foundation所有其他配对设置为等待状态。
接下来,agent会逐一执行某个检查列表,最高优先级的配对首先开始执行。如果列表中有一个配对状态是成功状态,其component ID设置为1,然后继续执行以下处理。在同样检查列表中,如果所有其他封冻状态的配对具有同样foundation,并且在这些具有同样foundation配对中,如果它们的component ID不是1的话,agent将把这些封冻的配对的状态设置为等待状态。针对一个具体的检查列表,如果媒体流的每个构件的配对有一对在成功状态,为其他所有媒体流的第一个构件(可能在不同的检查列表中),agent将会把所有具有同样foundation,其配对状态处于封冻状态的配对的状态进行状态迁移,这些配对的状态从封冻状态设置为等待状态。
2 轻量级场景部署场景
轻量级的部署场景中关于更新列表检查的处理比较简单。如果为一个媒体流重新启动ICE,agent也必须为此媒体流重新启动一个有效列表。Agent也必须记得此媒体流的每个构件的上次有效列表中的配对(称之为历史已选配对)。然后根据流程继续发送媒体流。流程的规则定义在将来的文章中介绍。接下来,针对每个媒体流的ICE处理状态必须修改为正在运行状态。
3 ICE option标识
在实际应用场景中,我们经常遇到一些用户的反馈WebRTC的兼容性问题,自己也经常面临WebRTC的兼容性问题。我们不得不经常更新一些功能支持,同时也不得不不断学习新的知识。其实,我们从RFC5245以及其最新规范8445中就可以看出,这些处理流程在最近几年内进行了很多次修改。现在,我们介绍一个特殊的ICE选项。除了在RFC5245中规定的ICE启动流程以外,在最新的RFC8445中增加了一个ICE选项-ice2 选项。当ICE agent在候选交互中包含了ice2选项以后,表示需要遵从RFC8445规范。在一些特定的交互中使用ice2让对端peer获悉agent也不再使用此交互流程。例如,在RFC8445中已经移除了主动推荐标识的流程(aggressive nomination procedure),如果agent需要通知对端不再使用此交互流程时,可以增加一个ice2选项来表示。
一个agent如果遵从RFC8445的话,在候选地址交互开始时必须通过ice2通知对端peer其交互模式启用了ice2选项。否则,对端可能收到一个未知的ice选项。
关于对ice2的编码和对对端peer的消息传输,读者可以参与RFC4566中的参考规范。在其参考规范中详细说明了ICE-SIP-SDP的细节。最新的草案参考链接如下:
ice-sip-sdp:
https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39
另外,再次提醒读者,如果要开发最新的SIP和WebRTC的业务应用的话,因为ICE处理流程中SDP的频繁更迭,读者一定要关注最新的RFC8445规范以及关于SDP构建的草案。
4 Keepalives
无论是否使用ICE或者媒体流的状态如何,keepalives是终端保持存活的重要手段。大家知道,为了终端保持状态的活动状态,所有的终端都必须不断向服务器端发送存活消息。针对媒体会话来说,存活消息的目的就是为了保持NAT绑定存活状态。无论媒体流状态是inactive,sendonly,recvonly还是send recv状态,并且也不管在线状态,带宽属性设置状态,终端必须发送keepalives。即使周期会话中完全没有使用ICE,终端也必须发送keepalives。
终端应该使用对端peer能够支持的格式来发送keepalives。ICE终端允许基于STUN的keepalives支持UDP的流。具体来说,当agent是一个全场景部署的agent,并且和对端peer(轻量级部署场景或全场景部署agent)通信时,它们之间必须使用STUN keepalives。agent能够通过每个媒体会话中属性a=candidate状态决定对端peer支持ICE。如果对端peer不支持ICE的话,keepalives数据包格式的选择是本地部署的事情。根据RFC5245的推荐,keepalives的格式最好是这样的格式-在实际媒体内容缺失的情况在,其格式支持数据可以非常容易发送出去。比较典型的两个例子是RTP No-Op和RTP的舒适噪音处理。如果对端peer不支持任何目前比较采用的keepalives格式的话,agent应该使用一个不正确的版本发送RTP数据包或者其他格式发送(当然,peer可能会丢弃这些错误数据)。
在Tr秒时间内,为了一个媒体构件的处理流程,ICE使用一个候选配对发送数据,如果此候选配对中没有数据发送,agent必须为此配对生成一个keepalives。Tr值应该是可配置的值,默认设置为15秒。Tr值一定不能低于15秒设置。另外一种处理方式是,如果agent通过动态方式可以发现intervening NAT的绑定生命周期的话,agent可以使用这个绑定生命周期来设置Tr值。系统管理人员是在自己可控的网络环境中部署ICE,在网络环境允许的情况下,Tr值应该尽可能的长一点。
如果keepalives使用了STUN,STUN绑定指示需要根据RFC5389规范来执行。此绑定指示不能使用任何认证机制。绑定指示中一个包含FINGERPRINT属性实现多路分用,但是不能包含任何其他的属性。此绑定指示仅用来维持NAT绑定存活处理。绑定指示通过同样的发送媒体的本地和远端候选地址来发送。虽然绑定指示是用来处理keepalives,但是agent仍然也需要准备好接收连接检查。如果agent收到了一个连接检查的话,agent应该根据RFC5389生成一个响应消息,但是,这个处理不会影响ICE的处理。
一旦ICE选择了候选配对准备发送媒体流,或媒体开始传输,无论以上两种方式哪种方式首先发生,agent必须开始keepalives的流程处理。一旦会话结束或媒体流被移除,keepalives也需要马上结束。
这里需要补充一点关于keepalives和Voice Activity Detection (VAD)一些讨论。实际环境中,keepalives 在实际数据缺失的情况下发送,所以实际环境中如果没有使用VAD的话,从来不会产生keepavlives消息发送的情况,因此也不会存在带宽增加的可能性。当启用VAD时,keepalives消息是在静音期发送,会在每15秒-20秒之间发送一个单数据包,此数据所占用的网络资源远小于语音数据发送状态下的(每20-30毫秒之间发送)所需要的网络资源。因此,keepalives的影响不会对环境部署中网络带宽有很大的影响。
读者注意,因为keepalives涉及了多种网络环境的连接,网络设备的部署也非常复杂,简单的一种规范很难完整说明全部的具体场景。笔者可以根据不同的场景做进一步的研究:
- 关于keepalives的优化,读者可以查阅草案的3.4章节:
https://tools.ietf.org/id/draft-ietf-pcp-optimize-keepalives-00.txt - 关于keepalives的讨论,一些规范和浏览器支持做了一些调整,读者可以查阅笔者参考资料链接获得详情。RFC5245仅提供了ICE的keepalives的讨论,读者也可以结合RFC6263关注RTP中NAT绑定中的keepavlives的讨论。
参考资料
- https://www.rfc-editor.org/rfc/rfc5389
- https://tools.ietf.org/id/draft-ietf-pcp-optimize-keepalives-00.txt
- https://www.cisco.com/c/en/us/support/docs/ip/serial-tunnel-stun/16398-50.html
- https://tools.ietf.org/html/draft-muthu-behave-consent-freshness-04
- https://tools.ietf.org/html/rfc6263
- https://www.semanticscholar.org/paper/NAT-Traversal-Techniques-and-UDP-Keep-Alive-Widmer/9f730e024dd212186c7b2ced75c877edad6951f0
- https://www.rfc-editor.org/rfc/rfc8445#section-10
- https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39