博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
最常用的18个SIP呼叫业务流程详解(6~18)
阅读量:2055 次
发布时间:2019-04-28

本文共 22856 字,大约阅读时间需要 76 分钟。

目录


 

在《》中介绍了三种保持(Hold)和两种转接(Transfer)的详细流程,本节继续介绍剩余的流程。

 

6、Transfer - Instant Messaging

  Transfer - Instant Messaging,我们称之为即时消息转发。其主要的工作方式是,首先用户Alice和Bob创建了一个会话,然后Bob呼叫Carol,Bob发送即时消息给Carol,即时消息中包含一个Alice的URL和一个嵌入的Replace头。如果Carol点击这个URL链接,Carol的SIP终端会对Alice发送一个INVITE请求,并且替换了会话中的Bob。在这个示例中,其URL传递是使用的SIP MESSAGE method来实现(参考RFC3428),也可以使用其他IM即时协议来完成Bob和Carol之间的URL传递工作。具体的处理流程经过11个步骤:

  现在,我们配合SIP消息来进一步说明如何实现即时消息转发方式。

  首先,Alice对Bob发送INVITE消息(F1):

  Bob回复180(F2) :

  紧接着Bob对Alice发送 200 OK(F3):

  Alice对Bob发送Ack确认,开始RTP流(F4):

  然后Bob对Carol发送一个message call(F5),在这个即时消息中携带了Alice的URL和Replaces头。

  Carol看到以后,发送一个200 OK给Bob:

  接下来,Carol点击URL,控制这个呼叫,对Alice发送一个INVITE请求,并且替换了Bob(F7)。

  Alice对比dialog中消息和Replaces的头消息,确认以后,接受Carol的请求。Alice对Carol回复一个200 OK(F8):

  Carol对Alice回复了ACK消息(F9),并且开始RTP流。

  Alice和Carol开始通话以后,并且因为已经替换了Replaces头,所以Alice对Bob挂机处理,Alice对Bob发送BYE消息(F10):

  Bob对Alice发送200 OK(F11),到此为止,即时消息转接的流程完成。

7、Call Forwarding Unconditional

  Call Forwarding Unconditional,我们称之为无条件呼叫前转。从字面意思,读者也可以看明白,被呼叫方会把呼叫进行无条件转移。与之对应的是忙状态呼叫前转和无应答呼叫前转。这些业务都涉及到了运营商接入业务(PSTN或者IMS),不是专门针对内网IPPBX用户之间的电话转接功能。我们会在后续的章节中分别介绍这两种呼叫的流程。这里,我们仅对无条件前转进行分析。

  这里,我们假设Alice呼叫Bob(这里没有标识Bob的流程)。Bob会把呼叫无条件前转到PSTN网络。如果实现此场景,呼叫必须经过Proxy和Gateway来实现处理。Gateway看作为另外一个Proxy的URL地址,同时支持了PSTN的接口。在以下的场景中,Alice对Bob进行呼叫,这个INVITE消息会经过Proxy,Proxy则会重写请求的URL地址,然后前转这个INVITE到Gateway地址。以上两步是前转最重要的部分,所以,我们仅针对这个部分进行深入分析,对网关侧和Bob的响应不做分析。

  这里,读者需要注意,在以上示例中的F3中,Proxy是通过返回Alice 一个181 响应消息来实现的呼叫前转处理。严格地说,在这个使用场景中,Proxy仅扮演着用户代理的角色,它不能生成non-100的临时响应回复消息。另外需要说明的是,呼叫前转的处理也可以通过redirect的方式来实现,就是通常所说302 Moved Temporarily response。以下图例是一个完整的无条件呼叫前转流程和具体的SIP消息操作。

  接下来,我们针对无条件呼叫前转的具体细节,结合请求响应消息进行分析介绍。首先,Alice对Bob发送INVITE请求,通过Proxy执行呼叫(F1):

  然后,Proxy回复Alice 一个100 trying(F2),表示正在处理这个请求,忽略了流程图。接下来,Proxy会对Alice发送一个181响应消息(181 Call is Being forwarded),执行F3流程;同时Proxy对Gateway发出一个INVITE请求(F4)。这里,Proxy对Gateway发送到INVITE请求中已经重写了请求URL地址。

  读者可以看到,Proxy和Gateway都没有做任何其他的条件判断,直接转发到下一个目的地地址。在F5中,Gateway直接对Proxy回复一个180 Ringing,然后在F6中,Proxy回复Alice一个180 Ringing。

  Gateway对Proxy发送180 Ringing以后,紧接着,Gateway对Proxy发送200 OK(F7),然后Proxy对Alice发送200 OK(F8)。

  Alice收到200 OK以后,接下来,Alice对这个INVITE请求进行确认,发送ACK消息(F9)。Proxy收到ACK消息以后,直接对Gateway发送ACK确认信息(F10)。双方RTP语音流正式创建。

  通话完成后,Alice挂机,对Proxy发送BYE消息(F11),然后Proxy对Gateway发送BYE消息(F12)。

 

  最后,Gateway对Proxy发送200 OK(F13),Proxy再对Alice发送200 OK(F14),到这里,双方的无条件呼叫前转正式结束。

8、Call Forwarding - Busy

  Call Forwarding - Busy,我们称之为遇忙呼叫前转。简单来说,就是呼叫方通过Proxy呼叫被呼叫方时,被呼叫方此时处于忙状态,然后Proxy把呼叫再次转发到第三方用户。例如,Alice通过Proxy呼叫用户1,此时,如果用户1处于忙状态,Proxy收到用户1的回复信息,Proxy会把此呼叫通过一个INVITE再次转发到用户2终端。遇忙呼叫前转到流程图如下:

  下面,我们配合具体的SIP消息示例来进一步说明整个遇忙呼叫前转的处理过程。

  首先,Alice对Proxy发送一个INVITE请求,需要呼叫用户1(F1),Proxy

  接下来对用户1发送一个INVITE请求,通知用户1,Alice要呼叫对方。

  在处理以上流程的同时,Proxy会对Alice发送一个100 Trying(F3),Proxy告诉Alice正在处理此呼叫,忽略了100 Trying图例。经过一定时间后,用户1此时此刻正在处于电话的忙状态,不能接听此呼叫。然后用户1对Proxy发送了一个486 Buy响应消息(F4)。

  Proxy获悉用户1是处于忙状态后,对用户1发送一个ACK确认信息,表示收到了用户1的状态响应(F5)。

  为了处理用户1处于忙状态不能接听电话的问题,Proxy首先通知Alice,proxy需要进行电话前转。所以,Proxy需要对Alice发送一个180 call is Being Forwarded的消息,通知Alice,你的呼叫正在被前转。同时,Proxy再次发起一个INVITE请求,这次,对用户2发出INVITE请求进行呼叫前转。

  接下来,用户2对Proxy发送180 Ringing响应消息(F8),Proxy然后对Alice发送180 Ringing响应消息(F9),表示前转接受。

 

  发送180 Ringing以后,紧接着,用户2对Proxy发送200 OK(F10),然后Proxy对Alice发送200 OK(F11)。

  Alice收到Proxy发送的200 OK以后,发送确认消息ACK(F12)。然后Proxy对用户2发送确认消息ACK(F13)。接下来,Alice和用户2创建媒体流,双方开始正常通话。

  通话结束后,Alice对Proxy发送BYE消息(挂机,F14),然后Proxy对用户2发送BYE消息(F15)。

  最后,双方进行挂机的最终确认,首先由用户2发送200 OK到Proxy(F16),然后Proxy对Alice发送200 OK(F17)。

 

  到此为止,遇忙呼叫前转的处理流程正式结束。

9、Call Forwarding - No Answer

  Call Forwarding-NO Answer, 我们称之为无应答前转。具体的实现过程和前面谈论忙呼叫前转的处理方式基本一致,唯一不同是,用户1是无应答,然后Proxy通过定时器检测超时,进行对第三方用户2前转处理。其余的处理流程基本和遇忙前转的处理流程一致。简单来说,Alice通过Proxy呼叫用户1,用户1在一定时间内没有应答此呼叫,然后Proxy把这个呼叫转接到用户2,对用户2进行呼叫处理。在请求超时后(F6),Proxy对用户1发送取消请求,然后通知Alice,同时对用户2再次进行INVITE请求处理,进行呼叫请求。具体的呼叫流程如下:

  接下来,笔者配合具体的SIP消息来进一步说明无应答呼叫前转的处理方式。

  首先,Alice通过Proxy对用户1发出INVITE请求,对用户1进行呼叫(F1),然后Proxy对用户1发送INVITE请求,对用户2进行呼叫请求处理。

  接下来,Proxy马上对Alice发送一个100 Trying(F3忽略),表示正在对用户1进行呼叫,通知Alice等待。用户1对Proxy发送180 Ringing(F4),Proxy也对Alice发送180 Ringing(F5)。

  这时,用户1处于振铃状态,在Proxy设定的定时器超时设置范围内,用户1终端无人接听此呼叫。因此,Proxy对用户1发送Cancel消息,通知用户1停止振铃。

  然后,用户1对Proxy发送200 OK(F7):

  接下来,用户1马上对Proxy发送一个487 响应消息,表示定时器超时,在一定振铃时间内无人应答此呼叫。

  Proxy收到超时消息以后,对用户1发送ACK确认消息,确认超时事件(F9):

  这时,Proxy需要做两件事。Proxy知道用户1是因为振铃超时,无人应答这个呼叫。接下来,Proxy重新调整呼叫流程,再次进行呼叫转接到处理。首先,Proxy先对Alice发送一个电话转接到响应消息(181 电话前转),通知Alice,你的呼叫正在被前转到其他用户终端(F10)。紧接着Proxy对用户2发送一个INVITE请求,对其进行呼叫(F11):

  然后,用户2对Proxy发送180 Ringing(F12),Proxy又对Alice发送180 Ringing(F13):

  振铃发送以后,用户2继续对Proxy发送200 OK(F14),Proxy又对Alice发送200 OK(F15):

  Alice收到200 OK以后,继续对此流程进行进一步的处理,以便开始双方的通话或RTP流。Alice马上回复Proxy ACK确认消息(F16),Proxy也马上对用户2回复ACK确认消息(F17):

 

  经过一定时间段通话以后,Alice首先挂机,对Proxy发送BYE消息(F18),Proxy再对用户2发送BYE消息(F19),通知用户2挂机:

  最后,用户2对Proxy发送最后200 OK消息(F20),然后Proxy对Alice发送最后的200 OK(F21)。到此为止,无应答呼叫前转的呼叫流程正式完成。

  这里读者一定要注意,我们讨论的呼叫前转方式方式可以通过IPPBX或者软交换的配置来实现,proxy或者媒体服务器的设置会影响振铃的时长设置,这样可能导致电话振铃问题,被呼叫方如果接听不及时的话,可能产生无应答呼叫。

  所以,在部署前转时一定要配合IPPBX或者其他的软交换来解决。另外,结合一些其他的应用需求,还有一些IPPBX可以设置为无应答或者分机随行的方式,如果终端无应答,可以转接到用户手机或者其他的终端,或者留言等方式。这些需要用户支持其他的接入方式来实现。

10、3-Way Conference - Third Party Is Added

  3-Way Conference - Third Party Is Added,我们称之为三方会议邀请。简单来说,此呼叫业务就是一个简单的三方电话会议。通常来说,如果是两方通话,我们称之为正常呼叫业务。如果介入了第三方或者更多人的话,我们称之为三方会议或者多方会议。

  一般的三方会议至少需要一个混音处理单元。在以下的部署场景中,Alice呼叫Bob,然后双方进行通话。这时,Bob想把第三方Carol也邀请到电话会议中来一起讨论问题。当然,Bob自己本身必须具备处理第三方媒体混音的功能。然后,Bob首先对Alice发送一个re-INVITE,在INVITE中,修改了多个Contact URLs为一个URL,这个URL表示Bob的混音单元。然后,Bob对Carol发送一个INVITE请求,邀请Carol参加会议,并且使用同样的Contact URL。

  这里,读者要注意,Bob邀请的处理方式可能有所不同。Bob可以先等Carol应答以后,再对Alice发送re-INVITE。Bob也可以呼叫Carol之前,先把Alice设置为等待状态。另外,Bob邀请会议时,Bob包含了一个功能标签tag-“isfocus”,表示Bob是一个会议处理中心。"isfocus"是在规范rfc3840中定义:

  Summary of the media feature indicated by this tag: This feature tag

  indicates that the UA is a conference server, also known as a

  focus, and will mix together the media for all calls to the same

  URI [13].

  关于会议中focus的定义和规范,读者可以参考rfc4579的第三章节。以下是一个完整的会议第三方邀请处理流程。

  下面,我们通过SIP消息结合具体的流程来说明第三方会议邀请是如何进行的。

  首先,Alice对Bob发送INVITE请求,对其进行呼叫(F1):

  Bob对Alice回复180 Ringing(F2):

11、3-Way Conference - Third Party Joins

  3-Way Conference - Third Party Joins,我们称之为三方会议加入。和上面的三方会议邀请不同的是,这里的第三方是自己主动希望加入到电话会议中,而不是由其他人邀请加入。因此,在处理三方会议时,其处理流程和前面的被邀请的方式完全不同。

  通过以上的处理流程我们来详细解释会议加入的方式是如何处理的。这里,我们假设Alice和Bob两个人正在进行双方的语音通话。Carol通过其他学习方式或者对Bob的订阅消息,例如Bob发送的其他非SIP消息或者Bob对Carol发送到NoTIFY消息,其消息中含有dialog事件包。关于dialog的事件订阅的处理,读者可以参考RFC4579中的第5.8章节和RFC4235的规范。

  这时,Carol希望加入到Alice和Bob之间的电话呼叫中。这时,Carol会对Bob发送一个INVITE请求(F5),这个请求中包含一个Join头,在这个头中,包含Alice和Bob两者之间的dialog,以此来保证Carol加入到是正确的dialog,而不是其他的会议呼叫。Bob然后对Alice发送一个re-INVITE修改了通话模式(F7),从双方谈话变成一个“isfocus”模式(rfc3840),开始支持三方会议的模式。然后,Bob才接受来自于Carol的INVITE请求,最后开始三方通话或者电话会议模式,此时,三方会议加入的处理流程彻底完成。现在,我们通过完整的SIP消息配合具体的处理流程来进一步对电话加入的方式做出详细介绍。

  首先,Alice对Bob发送INVITE请求,进行电话呼叫(F1):

  然后,Bob对Alice发送180 Ringing(F2),紧接着,Bob对Alice发送200 OK(F3):

  然后,Alice对200 OK回复ACK确认信息(F4),双方开始RTP流传输:

  这时,Carol通过Bob发送到其他的非SIP消息或者NOTIFY事件了解了Alice和Bob之间正在进行电话呼叫,Carol想加入到这个电话呼叫中。因此,Carol对Bob发送一个INVITE请求,在这个INVITE请求中(F5),包含有Join的头,这个头中含有加入他们之间还有的dialog消息内容(Join中忽略了具体的内容):

  Bob先对Carol发送一个180 Ringing(F6):

  然后,Bob再对Alice发送一个re-INVITE请求,Bob通知Alice,因为Carol要加入电话会议中,我现在需要修改通话模式,修改为“isfocus”模式来支持电话会议,需要进行媒体混音处理。

  Alice收到Bob的INVITE请求后,对其请求表示同意,然后对Bob发送200 OK(F8):

  Bob对Alice 的200 OK发送响应消息,获悉Alice同意修改为会议模式(F9):

  现在,Bob完成了和Alice的协商,获悉Alice已经准备好进行电话会议。接下来,Bob才对Carol发送200 OK,接受了其要求加入电话会议的INVITE请求,这里包括了新的Contact的会议地址。

  接下来,Carol知道Bob已经允许自己可以加入到会议中,对Bob发送最后ACK确认消息,表示正式加入会议中。此时,Carol加入到了会议室,三方会议正式开启(F11),RTP流开始工作。这里的Bob构成了一个媒体混音单元,也可能是一个会议媒体服务器功能,进行三方混音。

  这里,笔者再进一步介绍一下会议的标签isfocus tag。会议模式中的isfocus参数可以支持多种使用方式,通过SIP,它可以支持以下几种方式:

  • 创建会议
  • 加入会议
  • 邀请用户加入会议
  • 踢出会议用户
  • 支持对会议URL判断,可以检查到是否是会议URL
  • 删除会议

  会议创建的方式Ad-Hoc方式临时自组的方式来创建一个会议,也可以通过REFER方式来创建一个会议室。会议邀请的方式可以通过INVITE请求,用户可以主动呼入或者用户被呼方式来进入到会议室,也可以通过REFER的方式邀请用户进入到会议室。关于会议的管理和使用部署,RFC4579中有着非常详细的说明,同时此规范也介绍了其他的协商流程。读者可以阅读此规范进一步了解多方会议的处理流程。

  在开源媒体服务器中,Asterisk/FreePBX/FreeSWITCH也支持了强大的会议功能。用户可以创建会议室,系统通过自动拨号的形式邀请会议用户来加入到会议中,用户也可以通过语音IVR的形式进入到会议室中。会议主席也可以对会议人员实现静音或者踢出等功能。如果使用Asterisk的界面管理系统FreePBX,用户也可以通过界面来创建会议室,支持多种会议的管理功能,例如会议密码设置,最大会议用户人数设置,进入会议室播放提示,音乐等待等功能。

12、Find-Me

  Find-Me, 我们称之为有效用户查询呼叫。简单来说,Alice通过Proxy呼叫Bob,Bob可能有几个Contact地址。Proxy会根据不同的Contact列表来逐一查询这些地址的状态,如果有可接通的地址,则对其进行呼叫;如果没有,Proxy会继续查询其他的地址,直到找到可接听呼叫的地址。一旦找到可接听通话的地址,则不再继续查询其他的地址。

  这里笔者提醒读者,在对用户状态的查询中,系统也可以使用并列查询的方式。我们刚才使用的是按序查询的方式,系统也可以使用并列查询的方式。并列查询状态下,Proxy会同时对所有Contact 列表中的地址发送查询可接听的地址,然后创建RTP语音流。以下图例中说明了两种重新的处理不同。并列查询的处理方式:

  按序查询的处理方式:

  在本章节的有效用户查询呼叫中,我们使用的方式是按序查询的方式。Proxy会通过Bob的Contact列表逐一查询地址状态,如果有效,则创建呼叫。

  下面,我们结合SIP消息和具体的流程进行分析。

  首先,Alice通过Proxy对Bob进行呼叫,发送INVITE请求给Proxy(F1),Proxy对Bob发送INVITE请求(F2):

  接下来,Proxy马上回Alice 一个100 Trying(这里忽略,F3),表示呼叫正在处理中。用户1对Proxy回一个180 Ringing(F4)。紧接着,Proxy对Alice回复一个180 Ringing,表示用户1已经振铃(F5)。

 

  但是,在一定的振铃时间内,用户1没有人接听电话呼叫,Proxy的定时器出现超时。然后,Proxy对用户1发送Cancel消息(F6)。

  用户1对Proxy回复一个200 OK(F7):

  然后,用户1对Proxy发送一个487 超时消息(F8):

  Proxy对487 回复一个ACK确认消息(F9):

  在获悉用户1呼叫超时以后,Proxy继续对Contact 列表中的用户2地址发送INVITE请求(F10):

  但是,用户2根本没有登录注册,所以,用户2回复480 消息(F11)

  Proxy对用户2回复ACK确认(F12):

  然后,Proxy继续对Bob的Contact 列表中的用户3地址发送INVITE请求消息(F13):

  但对用户3发送INVITE请求后,发现用户3处于忙状态,不能接听这个呼叫。用户3回复486 Busy Here(F14):

  紧接着,Proxy对用户3回复ACK确认消息(F15):

  接下来,Proxy继续对Bob的Contact列表中的用户4地址发送INVITE请求(F16):

  然后,用户4回复Proxy 180 Ringing(F17),Proxy再回复Alice一个180 Ringing(F18):

  紧接着,用户4对Proxy发送一个200 OK(F19),然后,Proxy又对Alice发送一个200 OK(F20):

  然后,Alice对Proxy发送一个ACK确认消息(F21),Proxy对用户4发送一个ACK确认消息(F22)。到此为止,整个有效用户查询呼叫的流程完成,双方开始语音流的互通。

  结合以上多种用户终端出现的情况,Proxy经过了Bob的4个Contact列表地址查询,第一个呼叫超时,第二个地址没有登录,第三个用户忙,到最后一个用户地址,才真正和Alice创建了RTP语音流的传输。在每一个异常状态下,终端都返回相应的响应消息,然后Proxy会对每一个响应发送ACK,然后再进行下一个有效用户的地址查询。

  通话一段时间后,用户4对Alice挂机,对Proxy首先发送BYE消息(F23), Proxy对Alice进行挂机处理,对其发送BYE消息(F24):

  然后,Alice对Proxy发送200 OK(F25),Proxy再对用户4发送200 OK(F26):

  Alice和用户4的通话正式结束。

13、Call Management (Incoming Call Screening)

  Call Management (Incoming Call Screening),我们这里称之为呼叫筛选或呼叫过滤。在企业通信环境中,为了节省员工的时间,提高工作效率,用户可以设置一个电话筛选机制来保证不被一些不相关的电话骚扰。简单来说,如果有外部呼叫呼入到IPPBX的终端时,终端可以显示呼叫方号码,或者名称,或者通过第三方媒体发送到语音提示。此时,终端用户可以根据终端所设置的号码地址薄选择继续接听电话,拒绝,转语音邮箱,或者对呼叫方播放一个自定义的语音媒体。以上这些功能都可以通过Proxy加媒体播放服务器来实现,或者使用企业IPPBX本身的配置功能来实现。为了更加专业地表达此功能名称,我们称之为呼叫筛选或呼叫过滤,黑白名单过滤比较通俗易懂。其实,呼叫筛选和黑白名单过滤的功能要求基本类似,从功能设置的角度来说它们的功能基本相同。

  在日常生活中,我们简单称之为黑白名单过滤。因为大量的骚扰电话的问题,我们现在已经对呼叫筛选不陌生了。我们手机app所设置的骚扰电话设置或者黑名单过滤等都可以视为呼叫筛选的功能。但是,这些基于个人终端的业务支持,不是我们这里讨论的内容。

  呼叫筛选的具体流程经过了几个步骤。假设,Alice呼叫用户Bob,Bob的终端联系方式中可能已经对Alice设置为拒绝接听的地址,因此Bob拒绝接听此呼叫。拒绝接听后,Bob对Alice返回了一个消息,通知你呼叫必须通过Proxy的安全认证机制,Bob仅接受经过Proxy确认的呼叫。因此,Alice再次对Proxy发送请求,但是因为安全设置的问题,Alice没有通过Proxy的安全认证,Proxy在错误消息返回时插入了一个新的Error-info的URL地址,Alice播放这个来自于媒体服务器或者第三方声明提示等服务器的语音提示。

  这里注意,安全认证不能使用From 头来判断,必须通过Proxy的安全机制来处理。以下是一个呼叫筛选的流程图,在图例中,一般企业用户使用IPPBX本身自带的媒体播放功能对Alice实现媒体语音提示。

  接下来,我们根据SIP消息细节结合每一个处理流程来进一步说明呼叫筛选的处理过程。

  首先,Bob看到来自于Alice的呼叫,表示Alice需要和Bob通话(F1)。

  Bob拒绝了Alice的呼叫请求,要求必须通过Proxy认证才能通话。Bob对Alice发送305消息(F2):

  Alice获悉这个错误以后,对Bob发送ACK确认消息(F3):

  然后,Alice对Proxy发送INVITE请求(F4):

  然后,Proxy对Alice发送认证响应消息407(F5):

  Alice对Proxy返回确认ACK消息(F6):

  然后,Alice对Proxy发送安全认证消息(F7),携带自己的认证信息:

  Proxy经过安全认证检查以后,发现Alice没有权限直接呼叫Bob,呼叫筛选不能通过,因此对Alice返回一个失败消息(F8),并且插入了一个Error-info,带了一个URL地址。这个地址播放语音提示。

  Alice收到403 响应错误消息以后,对Proxy发送一个ACK确认消息(F9):

  为了知道Proxy对Alice发送的是什么提示,Alice会继续对这个Error-info的地址进行访问,发送一个INVITE请求(F10)这时一个媒体播放的服务器地址,其服务器会对Alice播放刚才呼叫失败的提示音:

  然后,媒体播放服务器对Alice发送200 OK消息(F11),表示可以对Alice播放语音提示。

  另外,在F11的200 OK中,媒体播放服务器对Alice增加了一个渲染功能标签,表示此服务器仅能支持自动媒体功能automaton和rendering:

  F11 200 OK Announcement Server -> Alice

  SIP/2.0 200 OK

  Via: SIP/2.0/TLS client.atlanta.example.com:5061

  ;branch=z9hG4bK74bfj

  ;received=192.0.2.103

  From: Alice ;tag=1234567

  To: Bob ;tag=234934

  Call-ID: 12345600@atlanta.example.com

  CSeq: 4 INVITE

  Contact:

  ;automaton;+sip.rendering="no"

  Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY

  Content-Type: application/sdp

  Content-Length: …

  v=0

  o=annc 2890844543 2890844543 IN IP4 announce.biloxi.example.com

  s=

  c=IN IP4 announce.biloxi.example.com

  t=0 0

  m=audio 49174 RTP/AVP 0

  a=rtpmap:0 PCMU/8000

  注意,在RFC5359规范的2.13章节中,F11的处理流程可能存在表述错误。读者需要根据实际的场景来做出正确的判断。按照流程图的示意,F11应该是从媒体服务器到Alice的返回消息:

  但是,按照正常的处理规范来说,应该是媒体服务器对Alice的返回消息(200 OK)。另外一个矛盾的地方是,在具体的流程中(F11),规范又描述为媒体到Proxy 1的处理流程:

  以上讨论是笔者仅根据官方规范做的疑点做出的自己的判断,读者可以通过其他渠道和规范发布者确认最终结果。

  现在我们回到合理的处理流程中。接下来,Alice收到200 OK以后,对媒体服务器发送ACK确认消息,然后媒体播放服务器对Alice播放语音提示(F12):

  语音提示播放完成以后,媒体服务器对Alice发送BYE消息,表示媒体播放结束,断开连接(F13)。

  最后,Alice收到了媒体播放器发送到最后提示,表示断开连接,Alice发送确认消息(F14):

14、Call Management (Outgoing Call Screening)

  Call Management (Outgoing Call Screening),我们称之为呼出呼叫筛选。此呼叫业务和上面的呼入呼叫筛选基本类似,但是处理流程更加简单。简单举例,呼叫方Alice通过Proxy呼叫Bob,但是Bob在Alice的呼叫筛选名单中,Proxy对Alice要求安全认证的验证,Alice对Proxy发送安全认证的消息,但是因为Bob在Alice的Call Screening的筛选名单中,不能对Bob进行呼叫,因此,最后,Proxy对返回403 呼叫筛选失败,最后挂机。

  现在,我们结合具体的SIP消息,配合每个呼叫流程做进一步的细节介绍。

  首先,Alice对Proxy发送INVITE请求,要求对Bob进行呼叫(F1):

  Proxy要求Alice发送安全认证消息进行验证(F2),返回407响应消息:

  Alice获悉以后,继续对Proxy发送ACK确认消息(F3):

  然后再次对Proxy发送INVITE消息,包含了安全认证的信息(F4):

  Proxy经过检查,Alice没有权限呼叫Bob,因此对Alice返回403 Screening Failure (Originating)的消息,表示不能对Bob进行呼叫(F5)。另外,在返回的消息中插入了一个Error-info 头,包含了一个媒体播放的链接,用户可以听到失败提示音。

  Alice收到403以后,获悉自己不能呼叫Bob,发送ACK给Proxy。此时,外呼筛选流程结束(F6)。

  关于呼叫筛选的功能,很多IPPBX支持了多种配置方式和功能。比较常见的处理方式包括:不在呼叫权限组,呼叫前缀不对,用户不接受其他部门呼叫,一定时间内不接受其他无关联用户呼叫,或者其他IP地址的呼叫,出差期间不接受呼叫等筛选方式。

  这里比较关键的是筛选失败以后的处理,如何对呼叫失败方进行一个比较友好的处理方式,比如,对呼叫失败方播放语音提示,转到语音留言,抓到其他的IVR流程,或者进行后期回呼处理。在Asterisk或者FreeSWITCH平台中,这些处理策略都可以通过IPPBX的拨号规则加一定的脚本来实现筛选的路由控制。具体的实现方式用户参考网上的学习资料。

15、Call Park

  Call Park,我们称之为呼叫驻留/停靠。简单来说,呼叫驻留就是呼叫方呼叫被呼叫方时,电话被驻留在驻留服务器(一般是媒体服务器或者IPPBX)。经过一段时间后,呼叫方电话被其他第三方用户再次接听。具体的实现过程是这样的,假设Alice呼叫Bob,Bob和Alice开始通话,通话后,可能Alice想转接第三方或者Bob可能有其他事情,暂时不能继续和Alice通话,Bob通过终端按键对其通话进行电话驻留的设置。

  所以,Alice的电话被驻留在驻留服务器或者IPPBX/媒体服务器。Alice电话被驻留以后,媒体服务器会对Bob发送订阅消息,说明电话驻留状态。然后,媒体服务器对Alice发送一个INVITE请求,通知Alice,媒体服务器现在替换了Bob来对其会话进行管理。Alice获悉驻留状态后,接受此处理流程,然后,媒体服务器或者IPPBX对其播放音乐媒体提示音。接下来,Alice对Bob发送BYE消息,媒体服务器对Bob发送Bob消息,表示现在媒体服务器已经驻留了Alice的通话,Bob脱离和Alice之间的关系。第三方Carol想接听这个被驻留在媒体服务器的呼叫,因此,首先需要对媒体服务器发送订阅消息,媒体服务器接受订阅以后,获悉Carol想接听这个被驻留的呼叫,接下来Carol对Alice发送INVITE请求,并且要求替换会话中的媒体服务器。Alice收到此请求后,同意和Carol进行通话,发送协商成功的响应消息,然后双方正式开始通话,驻留处理流程完成。

  在电话驻留的流程中,几个问题需要读者注意。首先,这里的电话驻留被重新接听的是第三方的用户,当然,接听用户也可能是发起驻留电话自己本身。但是,这样的驻留方式也不符合大部分场景中的电话使用习惯。因为,大部分已经开始的通话,或者进行电话转接,或者一定时间后双方挂机,或者挂机后,双方稍晚时间后重新呼叫对方。因此,电话驻留其实也是电话转接的一种特殊方式或特殊使用场景。其次,电话驻留的方式其实也是音乐等待的一种处理方式。只是,在电话驻留服务器或者媒体服务器中,驻留处理可以分割为多个驻留空间或者slot,电话驻留基本的处理流程或原理事实上和语音等待类似。最后,电话驻留服务器开启驻留处理时也对驻留方发送了automaton,,rendering和 byeless tag标签,表示其服务器的支持能力。以下是电话驻留的处理流程:

  现在,我们结合具体的SIP消息和每一个处理的具体细节来一步步介绍整个电话驻留的处理:

  首先,Alice对Bob发送INVITE呼叫请求(F1):

  然后Bob对Alice响应180 Ringing(F2):

  紧接着,Bob对Alice发送200 OK(F3):

  Alice收到200 OK以后,对Bob发送ACK确认消息(F4),双方开始通话。

  通话后获悉,Alice可能需要被驻留,然后第三方Carol来接听。因此,Bob首先需要对Alice通话进行电话驻留处理,把Alice的呼叫停靠在驻留服务器或者媒体服务器上,Bob对服务器发送REFER消息(F5),并且通知媒体服务器在会话中使用Bob替换Alice。这里的Refer-To是Alice,Refer-By是Bob自己。注意,这里不存在Bob和媒体服务器之间的直接的会话。

  驻留服务器收到Bob的REFER以后,对Bob发送一个202 Accepted,表示接受此REFER消息(F6):

  然后,驻留服务器对Bob发送NOTIFY消息,创建一个对事件的订阅,同时提示Bob正在处理电话驻留(F7),100 Trying:

  Bob回复200表示收到订阅事件(F8):

  接下来,驻留媒体服务器对Alice发送INVITE请求,同时替换Bob(F9),增加了渲染功能的标签,表示媒体服务器的处理能力(在Contact中插入了媒体服务器的渲染功能标签:

  Contact:;automaton ;+sip.byeless;+sip.rendering="no"

  注意:以下图例中忽略了渲染功能标签

  Alice接受了媒体服务器的请求,发送200 OK(F10):

  媒体服务器对Alice发送ACK确认消息,开始媒体流处理(这里开始播放语音提示,F11):

  提示音播放开始以后,Alice对Bob发送BYE消息(F12),表示Alice已经和媒体服务器创建了新的会话关系:

  Bob获悉Alice的电话被成功驻留以后,对Alice发送一个最终的200 OK(F13):

  这时,媒体服务器再次对Bob发送NOTIFY消息,并且携带了返回的200 OK消息,表示媒体服务器成功处理了Alice的驻留(F14)。

  Bob对媒体服务器返回200 OK消息,并且确认了dialog的ID(F15),到此为止,Alice已经完全驻留在媒体服务器中。

  接下来,如果Carol想接听这个被驻留在服务器的Alice的电话时,Carol首先需要对媒体服务器发送订阅消息,否则,Carol不知道Alice电话的事件。因此,Carol对服务器发送订阅消息(F16):

  媒体服务器对Carol回复一个200 OK,获悉媒体服务器的支持能力等渲染标签(F17):

  有了新的事件以后,媒体服务器对Carol发送NOTIFY消息,通知Carol的事件具体消息内容(F18):

  Carol获悉被驻留方的地址信息后,对媒体服务器发送200 OK(F19):

  Bob通过媒体服务器发送到NOTIFY已经获得被驻留电话的地址信息,然后,为了接听Alice的驻留电话,Bob对Alice发送INVITE请求,并且要求替换媒体服务器的地址(Replaces,F20)。

  Alice接受了Carol的请求,对Carol返回200 OK(F21):

  然后,Carol对Alice继续发送一个ACK确认消息(F22),创建RTP流,双方开始正式通话,Carol接听被驻留的电话。

  然后,Alice必须对驻留服务器发送最后的通知消息(BYE,F23),表示Alice已经开始和Carol通话,驻留服务器和Alice的驻留关系释放。

  驻留服务器最后对Alice返回200 OK(F24),表示确认驻留关系释放:

  呼叫驻留到这一步,媒体服务器释放了和Alice的驻留关系,Alice通知了Bob,Carol接听了驻留电话。呼叫驻留的流程才真正完成。

  企业IPPBX基本上都支持了呼叫驻留/停靠的业务需求。IPPBX支持了多种驻留热键功能,用户可以通过热键功能来驻留呼叫或者重新被激活驻留的呼叫。在开源平台Asterisk(#72)或者FreeSWITCH(*5900)都有相应的设置,用户可以根据配置文件来实现电话驻留或重接被驻留的呼叫,另外,IPPBX用户也可以对驻留的空间进行管理,根据配置的不同,驻留空间可以支持很多个slot。还有,电话驻留的语音提示音也可以实现自定义功能,这样就可以方便地支持系统用户的操作。最后,电话驻留时长也可以通过配置文件来进行调整。FreePBX支持了电话驻留的界面配置,也支持了多种配置选项,用户可以参考其功能配置:

16、Call Pickup

  Call Pickup, 我们称之为组内代答或者呼叫代答功能。一般情况下,如果是企业客户用户的话,每个部门的员工都有各自的分机。如果有人呼叫分机1,分机1如果没有应答的话,分机2看到分机1用户分机没有人应答,分机2用户可以通过热键来替正在振铃的分机1代接此呼叫。一般情况下,如果系统用户想替其他分机代接的话,这些分机必须在一个工作组或者根据业务类型分配的部门,同一组内用户可以互相代接其他用户的呼叫。

  组内代接的具体处理流程需要经过大概以上几个步骤。假设,Alice呼叫Bob,Bob没有应答此呼叫。和Bob同组的Bill想为Bob代接此呼叫。为了代接此呼叫,Bill首先需要对Bob发送订阅消息,通过订阅消息获取到Dialog消息内容。然后,根据获取的dialog消息内容,对Alice发送INVITE请求消息,替换Bob。Alice收到INVITE请求以后,然后对Bob分机发送一个CANCEL取消的消息,通知Bob分机停止振铃。这里读者要注意,对于F11和F12相关的顺序和200 OK(F10)它们之间是不确定的。

  在以上图例中的F7流程中,Replaces头中使用了一个新的参数“early-only”,这个参数可以防止接受一个Bob可能已经接受的INVITE请求。如果Bill已经准备从Bob那里代接此呼叫,无论Bill是否应答呼叫,参数“early-only”就将不会出现在F7的流程中。另外,读者需要注意一下Bob和Bill之间的订阅会话创建的时间问题。实际上,这个订阅会话可能已经存在于Alice呼叫Bob之前。这里的图例是为了表达方便,而显示在F3,Alice呼叫之后发生,实际上,Bob和Bill的订阅可能已经发生在Alice呼叫之前。

  另外提醒读者,根据RFC5359的描述,这里可能是拼写错误,把Bill说成了Carol。以上流程图中没有Carol。

  Also note that the subscription between Bob and Carol could have been

  established prior to Alice's call.

  关于"early-only”的规范定义,用户可以参考RFC3891,此规范完整介绍了Replaces和其参数的使用方式,以及如何检查INVITE重用。现在,我们结合具体的SIP消息,配合每一个处理流程来一步步说明组内代接:

  首先,Alice对Bob发送INVITE请求,要求和Bob进行通话(F1):

  接下来,Bob对Alice发送180 Ringing,Bob分机振铃(F2)。

  这时,因为Bob分机振铃,无人接听电话,Bill打算为Bob代接呼叫,Bill对Bob发送订阅消息来获取呼叫的dialog身份确认消息(F3):

  Bob收到Bill订阅消息,然后回复200 OK(F4):

  紧接着,Bob对Bill发送NOTIFY消息,包括了订阅消息的内容和Alice的相关消息信息(F5):

  Bill回复Bob收到了订阅的dialog消息内容(F6),准备对Alice发送INVITE请求。

  Bill对Alice发送INVITE请求,替换Bob,使用了early-only(F7):

  Replaces: 12345600@atlanta.example.com

  ;from-tag=314578;to-tag=1234567;early-only

  Alice收到Bill的INVITE请求,检查匹配Replaces头中的dialog,确认以后,接受了这个来自于Bill的INVITE,然后对Bill发送200 OK(F8):

  Alice接受了Bill的INVITE以后,Alice还要对正在振铃的Bob的分机发送一个CANCEL取消的回复,通知Bob分机停止振铃(F9):

  Bob收到CANCEL以后,停止振铃,然后对Alice回复200 OK(F10):

  然后Bob最后对Alice发送487 响应消息,表示对Bob的请求正式结束(F11):

  Alice对Bob发送最后确认消息ACK(F12),他们两个人之间的会话关系正式结束。

  Bill回复Alice发送的200 OK(F8)的ACK确认消息(F13),双方开始RTP语音流的传输,组内代接成功。

  经过一段时间通话后,Alice先挂机,对Bill发送BYE消息(F14):

  然后,Bill对Alice发送最后的200 OK(F15),双方通话结束。

  大部分的企业IPPBX都支持了组内代接功能。顾名思义,如果需要实现组内代接功能的话,用户首先需要创建一个呼叫组,组内成员则可以通过IPPBX的系统热键来代接电话。在asterisk或FreePBX平台上,用户可以按热键*8来实现代接功能。当然,其他平台也可以通过热键设置来接听代接电话。另外,用户可以通过比较灵活和人性化的设计可以支持代接成功或者失败的的语音提示,这样可以让用户获得更好的用户体验。

17、Automatic Redial

  Automatic Redial,我们称之为自动呼叫重拨功能。简单来说,如果呼叫方呼叫被呼叫方时,对端如果处于忙状态或者其他状态,在一定时间后,呼叫方再次呼叫被呼叫方。具体的实现过程相对比较简单(查看以下图例)。

  假设,Alice呼叫Bob,这时Bob处于忙状态可能正在接听其他电话呼叫,Bob正在通话中。Alice呼叫后,对Bob发送一个订阅消息,对Bob分机状态进行订阅。当Bob处于空闲状态后,Bob对Alice发送一个提醒消息,通知Alice现在Bob分机处于空闲状态,可以对Bob分机进行呼叫。Alice收到这个提醒消息以后,结束对Bob的状态订阅,然后,Alice对Bob发送一个INVITE请求,对Bob进行呼叫,双方互相通话。

  下面,我们根据SIP消息内容,结合每一个具体的处理流程来进一步分析如何实现呼叫重拨功能。

  首先,Alice呼叫Bob,对Bob发送INVITE请求(F1):

  因为Bob此时正处于忙状态,所以,Bob回复Alice 一个486 响应消息(F2):

  Alice回复一个ACK确认消息(F3):

  为了获悉Bob分机的状态,Alice对Bob发送了一个订阅(F4),获取dialog消息内容:

  Bob回复Alice的订阅,回复200 OK(F5):

  Bob对Alice发送NOTIFY消息(F6):

  Alice回复200 OK(F7):

  Bob分机处于空闲状态,再次对Alice发送NOTIFY消息,提醒Alice现在Bob是处于空闲状态(F8)。

  Bob收到来自于Alice的提醒消息,Bob现在处于空闲状态,回复200 OK(F9):

  然后,Alice马上对Bob发送INVITE消息,进行呼叫请求(F10):

  因为Bob已经是空闲状态,所以接听了来自Alice的呼叫。对Alice返回180 振铃消息(F11):

  Alice然后返回200 OK(F12):

  紧接着,Alice对Bob发送一个ACK确认消息(F13),重拨呼叫接通。双方开始语音流传输(F13)。

  Bob再次对Alice发送NOTIFY消息(F14):

  Alice返回200 OK(F15):

  经过以上互相发送NOTIFY和确认消息,Alice已获悉对方状态,不再对Bob进行状态订阅,结束订阅,所以Alice对Bob再次发送订阅通知,结束对Bob的订阅(F16),重置Expire:0。

  Bob回复Alice 200 OK(F17):

  Bob对Alice发送NOTIFY消息(F18),订阅结束。

  最后,Alice对Bob返回200 OK响应消息(F19),订阅结束确认。

  在以上的订阅消息结束处理中,根据RFC6665的规范(4.1.2.3),取消订阅可以通过重设Expire头,设置expire:0 来通知取消订阅。根据规范的说明,成功取消订阅会再次触发一个最终NOTIFY消息(F18)。因此,读者不用对此产生误解。另外,我们这里没有涉及更多订阅的定时器刷新等问题,更多关于订阅的处理,读者可以查阅rfc6665来做进一步的学习。

  关于订阅功能使用方面,笔者建议读者在部署IPPBX和使用SIP终端时要慎用此功能。因为,一旦开启了订阅信息,IPPBX分机之间的订阅消息会生成大量的无效的数据,这样可能影响IPPBX本身的性能和企业内网的网络的稳定性,还有用户消息传送的时延,这样可能导致其他处理流程的错误。比如,如果Bob的终端对Alice发送了NOTIFY消息,通知Alice现在Bob处于空闲状态,Alice可以对其进行呼叫,但是,可能当前的Alice也正在处于其他的状态,可能没有可能再次及时对Bob发起呼叫,因此这个流程就会出现其他的问题。

  一些技术论文对消息导致的系统性能问题和网络优化做了讨论。Ahmadreza Montazerolghaem发表的论文关于重传机制优化的论文可能对读者有所帮助,大家可以参考这个方法来进一步优化自己的网络环境。

  The Overload Reduction in SIP Servers through Exact Regulation of the Retransmission Timer of the Invite Message

  Finnian McKeon 发表的论文中对SIP即时消息互发对网络影响数据流量的影响可以给我们提供一些参考建议,用户可以查阅研究。

  另外,Maria Isabel Pous在论文-SIP-based Applications in UMTS: A Performance Analysis也对SIP消息产生的影响做了深入的分析,读者可以查阅其论文作为参考资料。

18、Click to Dial

  Click to Dial,我们称之为点击呼叫或页面点击呼叫。浏览器用户以插件的形式或者页面的形式通过浏览器访问点击界面。用户通过点击页面的一个SIP URL链接,页面点击呼叫消息传递给电脑SIP终端,终端配置了呼叫方的SIP URL地址,通过REFER发送SIP终端,然后SIP终端和被呼叫方创建一个会话连接,实现双方呼叫。

  这里的呼叫场景适合于简单的点对点的SIP呼叫场景。如果用户通过媒体服务器实现呼叫的话,处理流程和我们现在讨论的有所不同。具体的呼叫流程如下:

  现在,我们配合具体的SIP消息内容和每一个流程来简单说明点击呼叫的处理过程。

  首先,Bob的PC端SIP对BobSIP电话发送REFER消息(F1),这里的头域中设置了Refer-Sub:false,这表示PC端要求不生成REFER的dialog,仅支持2XX响应消息。关于Refer-Sub的使用方法和参数设置,读者可以查阅RFC4488。

  然后,BobSIP电话终端回复202 接受(F2):

  接下来,Bob对Carol发送INVITE请求消息,表示需要对Carol进行呼叫(F3):

  接下来,Carol对Bob SIP 电话回复180 振铃(F4):

  然后,Alice对Bob SIP电话回复200 OK(F5):

  接下来,Bob的SIP 电话回复ACK确认消息(F6),然后实现双方语音流传输。

  到此为止,整个点击呼叫的流程结束,双方开始电话互通。

  事实上,现在点击呼叫业务功能可以通过很多种方式实现,可以通过浏览器插件的形式实现,也可以通过HTML加脚本语言的形式实现,WebRTC或者邮件终端插件工具来实现。

  特别是基于开源软交换的平台,例如Asterisk/FreePBX或者FreeSWITCH都可以通过接口语言来开发更加灵活的点击呼叫功能。例如,通过脚本语言加Asterisk AMI接口实现的页面点击呼叫功能。用户可以下载以下代码来实现点击呼叫功能。以下是一个PHP的页面点击呼叫实例地址,读者可以参考:

  https://github.com/spbx/Simple-Click2Call-for-Asterisk-PBX/blob/master/click2dial.php

  基于Asterisk的点击呼叫的插件,用户可以参考TBDialOut来实现,开源项目地址:

  http://www.oak-wood.co.uk/tbdialout/

  19、总结讨论

  笔者根据RFC5359,结合一些具体的图例非常系统地讨论了最常用的18个SIP呼叫流程的具体细节。在每一个细节中,笔者增加了一些相关的讨论,帮助读者能够更加全面地了解部署使用时的问题和风险。笔者再次强调,这18个SIP呼叫业务是一个规范流程,不一定是强制执行的标准,特别是涉及到Proxy的内容,读者一定要注意。不同的Proxy或者媒体服务器可能对流程的处理有所不同,读者需要根据自己的部署平台来对照检查。整体而言,通过这18个完整的SIP呼叫业务细节讨论,笔者为提供SIP呼叫业务的技术人员提供了相对比较完整的学习资料和比较权威的参考,希望对大家有所帮助。

  另外,因为笔者水平和资源有限,肯定有很多错误之处,敬请谅解。

  参考资料:

  https://tools.ietf.org/html/rfc4579

  https://www.rfc-editor.org/rfc/rfc5359.txt

  https://tools.ietf.org/html/rfc7088

  https://www.rfc-editor.org/rfc/rfc3515.txt

  https://tools.ietf.org/html/rfc3840

  https://tools.ietf.org/html/rfc3891

  https://www.rfc-editor.org/rfc/rfc6665.txt

  http://file.scirp.org/Html/1-1730003_32286.htm

  https://arrow.dit.ie/cgi/viewcontent.cgi?

  referer=https://www.google.com/&httpsredir=1&article=1005&context=ittscicon

  http://www.cs.columbia.edu/~nahum/papers/sip-multicore-journal.pdf

  https://support.sonus.net/display/SBXDOC51/GRUU+Support

  https://www.tech-invite.com/fo-sip/tinv-fo-sip-service-99.html

  www.freepbx.org.cn

  https://svn.resiprocate.org/viewsvn/resiprocate/main/resip/recon/MOHParkServer/doc/MOHParkServer_User_Documentation.pdf?revision=8937&view=co

  http://ijsetr.com/uploads/463152IJSETR13872-273.pdf

  https://tools.ietf.org/html/rfc3665

  https://tools.ietf.org/html/rfc3265

  https://tools.ietf.org/html/rfc3515

  https://tools.ietf.org/html/rfc4317

转载地址:http://awilf.baihongyu.com/

你可能感兴趣的文章
数据结构与算法(三)——线性表
查看>>
Java8学习笔记(一)—— 函数式编程的四个基本接口
查看>>
Java8学习笔记(二)—— Lambda表达式
查看>>
Java8学习笔记(三)—— Optional类的使用
查看>>
Java8学习笔记(四) —— Stream流式编程
查看>>
Java8学习笔记(五)—— 方法引用(::双冒号操作符)
查看>>
数据结构与算法(四)—— 栈与队列
查看>>
数据结构与算法(五)—— 广义表
查看>>
微服务简介
查看>>
CAP定理
查看>>
Docker初探
查看>>
Docker镜像常用命令
查看>>
使用Dockerfile定制镜像
查看>>
Docker容器数据持久化
查看>>
Docker Compose
查看>>
GitLab克隆项目出现 “git未能顺利结束(退出码128)”问题的解决
查看>>
SpringBoot整合FastDFS(附源码)
查看>>
在RoboWare Studio下利用python语言实现话题
查看>>
科学计算库——NumPy库
查看>>
数据分析处理库——Pandas
查看>>