您的位置:首页 > 其它

SIP简介

2011-07-29 15:01 183 查看

SIP简介,第1部分:SIP初探

http://blog.csdn.net/IOKE/archive/2006/04/28/694445.aspx

会话发起协议(SessionInitiationProtocol,SIP)是一种信令协议,它对于通信业有着重要的意义。本文从技术上对SIP进行了一般性的介绍,并说明了SIP如何为通信解决方案提供重要支持。

简介

  我曾经构想过一种软件,它可以“浮于”应用程序之上,提供辅助作用。这不是一个哑的“帮助”系统,而是一个活动的技术支持代理,可以在Internet上进行讨论。那时曾有人告诉我,“现有的工具、库、协议或带宽实现不了这样的要求!”

  时代已经不同了!

  许多人家中已经拥有基于DSL、光纤和其他技术的宽带网络。存在大量高质量的工具和库,无论是商业的还是开源的。标准驱动应用。现在是时候来实现上述创新思路了。

了解SIP

  首先,我将向您介绍SIP,也就是会话发起协议。SIP是一种轻量级的可扩展请求/响应协议,用于在两个端点之间开始通信会话。这听起来是不是很熟悉?SIP在概念上源自于HTTP和SMTP,但是它的目的却不同。可以把SIP消息与CB(民用波段)隐语10-code和Q-signal进行比较。

图1.用于管理CB呼叫的隐语

  在这个例子中,真正的消息包含在专用的呼叫协商消息中。

  SIP是IETF于1999年提出的,在2002年进行了修订。RFC3261对它进行了描述。本文中有关SIP的信息就选自RFC。对SIP存在很多扩展,这些扩展中的很多都能在SIP-relatedRFCsanddrafts清单中找到。

  SIP有哪些优点呢?通常情况下,两个端点使用它来协商一次“呼叫”。这里的协商包括介质(文本、语音等)、传输(通常是RTP、RealTimeProtocol)和编码(codec)。一旦协商成功,两个端点就会使用选中的方法相互交谈——这就与SIP无关了。“呼叫”完成之后,SIP用于指示断开连接。因此,SIP最好用作一种信令机制。SIP及其扩展还提供一些相关功能,比如即时消息传递、注册和到场。

  SIP术语中的端点叫做用户代理。它可以是“软件电话”、即时消息收发器、IP电话,甚至是手机。服务器用户代理提供集中式的服务,比如登记员、代理或应用服务器。

  听起来SIP非常简单,而且它也确实简单。这种简单性对于协议的稳定很重要,而且它也没有降低协议的有用性,所以该协议得到了广泛的应用。

  例如,考虑一下HTTP。协议本身的定义很少。但是使用它的方式多不胜数。SIP也是可以扩展的。存在大量针对SIP的扩展,它们涵盖了很多应用。现在,我们进一步来考察SIP,并分析它为什么会如此重要。

SIP的重要性

  有人说,SIP对于通信,就像HTTP对于Web一样。

  SIP对通信业产生了巨大的影响。从事蜂窝技术的公司已经决定为了未来的应用,对SIP进行标准化。VoIP(VoiceoverIP)供应商、Internet电话和即时消息传递应用程序(例如,MicrosoftMSNMessenger)都准备基于SIP进行标准化。

  目前已经存在一些信令协议和P2P技术。这就引发了一个问题:SIP相对于这些协议和技术有何优势?SIP具有以下的明显优点:

·
稳定性。该协议已经使用了多年,现在十分稳定。

·
速度。基于UDP的小型协议效率特别高。

·
灵活性。这个基于文本的协议十分容易扩展。

·
安全性。它提供像加密(SSL、S/MIME)和身份验证这样的功能。对SIP的扩展还提供其他安全性功能。

·
标准化。随着整个通信行业都在向SIP靠拢,SIP已经讯速成为一种标准。其他技术可能具有SIP所没有的优势,但是它们没有得到全球范围内的采用。

  这意味着,如果您想让您的应用程序与其他工具、设备和服务器进行互操作,SIP是最佳选择。供应商对互操作性是很关注的,会定期开会对其产品进行测试。这些会议称为SIPitforSIP
InteroperabilityTests(以前叫做Bakeoff,是Pillsbury提出更改的)。

剖析SIP呼叫

  现在,让我们更深入地了解这门技术。SIP通常基于UDP进行传输,但是SIP工具必须同时支持TCP。一条SIP消息由两部分组成:

·
信封(envelope),以头字段的形式描述一个请求或请求的结果(响应)。

·
有效负载(payload),即内容(可选),包含有关请求的数据。

  信封是文本格式,但是内容可以是文本,也可以是二进制格式。

  例如,让我们具体分析一次典型的SIP呼叫。在这个场景中,用户A想呼叫用户B。图2说明了这次呼叫:

图2.一次典型的SIP呼叫

  下面对所有的消息进行了解释:

1.UserAgentA发送一个SIP请求INVITE给UserAgentB,表达UserA想跟UserB进行谈话的愿望。这个请求包含语音流协议的细节。payload中使用会话描述协议(Session
DescriptionProtocol,SDP)就是为此目的。SDP消息包含一个清单,其内容为UserA支持的所有介质编码。(这些编码使用RTP进行传输。)

INVITE

sip:UAB@example.com

SIP/2.0

Via:SIP/2.0/UDP10.20.30.40:5060

From:UserA<sip:UAA@example.com>;tag=589304

To:UserB<sip:UAB@example.com>

Call-ID:8204589102@example.com

CSeq:1INVITE

Contact:<sip:UserA@10.20.30.40>

Content-Type:application/sdp

Content-Length:141

v=0

o=UserA28908445262890844526INIP410.20.30.40

s=SessionSDP

c=INIP410.20.30.40

t=30344236190

m=audio49170RTP/AVP0

a=rtpmap:0PCMU/8000

2.UserAgentB读取该请求,然后告诉UserAgentA它已经收到请求。

SIP/2.0

100Trying

From:UserA<sip:UAA@example.com>;tag=589304

To:UserB<sip:UAB@example.com>

Call-ID:8204589102@example.com

CSeq:1INVITE

Content-Length:0

3.当电话响铃时,UserAgentB发送临时消息(响铃)给User
AgentA,这样它就不会超时和放弃。

SIP/2.0

180Ringing

From:UserA<sip:UAA@example.com>;tag=589304

To:UserB<sip:UAB@example.com>;tag=314159

Call-ID:8204589102@example.com

CSeq:1INVITE

ContentLength:0

4.最终,UserB决定接受呼叫。此时,User
AgentB发送一个OK响应给UserAgentA。在响应的payload中,还有另一条SDP消息。它包含一组两个用户代理都支持的介质编码。此时,双方正式处于呼叫中。使用200类型的响应可以接受所有类型的SIP请求。

SIP/2.0

200OK

From:UserA<sip:UAA@example.com>;tag=589304

To:UserB<sip:UAB@example.com>;tag=314159

Call-ID:8204589102@example.com

CSeq:1INVITE

Contact:<sip:UserB@10.20.30.41>

Content-Type:application/sdp

Content-Length:140

v=0

o=UserB28908445272890844527INIP410.20.30.41

s=SessionSDP

c=INIP410.20.30.41

t=30344236190

m=audio3456RTP/AVP0

a=rtpmap:0PCMU/8000

5.UserAgentA最后使用一条ACK消息进行确认。对于这种请求类型来说,没有重试和响应消息,即使消息丢失。ACK只在INVITE消息中使用。

ACK

sip:UAB@example.comSIP/2.0

Via:SIP/2.0/UDP10.20.30.41:5060

Route:<sip:UserB@10.20.30.41>

From:UserA<sip:UAA@example.com>;tag=589304

To:UserB<sip:UAB@example.com>;tag=314159

Call-ID:8204589102@example.com

CSeq:1ACK

Content-Length:0

6..两个用户代理现在使用最后一条SDP消息中选定的方法进行连接。

RTP使用PCMU/8000编码对在端口49170&3456上双向传输的音频数据进行打包。

7.在通信会话结束时,其中一个用户挂断。此时,这个用户的用户代理发送一个新的请求BYE。这条消息可以由任一方发送。

BYE

sip:UAB@example.comSIP/2.0

Via:SIP/2.0/UDP10.20.30.41:5060

To:UserB<sip:UAB@example.com>;tag=314159

From:UserA<sip:UAA@example.com>;tag=589304

Call-ID:8204589102@example.com

CSeq:1BYE

Content-Length:0

8.另一用户的用户代理接受该请求,然后使用一条OK消息作为应答。呼叫连接至此断开。

SIP/2.0

200OK

To:UserB<sip:UAB@example.com>;tag=314159

From:UserA<sip:UAA@example.com>;tag=589304

Call-ID:8204589102@example.com

CSeq:1BYE

Content-Length:0

  SIP消息的第一行包含消息的类型和所使用的SIP版本(2.0)。在请求中,这一行还包含一个叫做SIPURI的地址。这代表消息的目的地。

这个例子说明了如何使用请求消息INVITE、ACK和BYE,以及200OK响应消息。SIP中还存在许多其他消息。下面给出一些请求:

消息

用法

INVITE

呼叫一个用户代理,传送一次呼叫。

ACK

确认呼叫。

BYE

终止呼叫。

CANCEL

终止还未OK的呼叫。

REGISTER

提供一项注册服务,带有一个联系地址和可以用来代替的别名。例如,在前面的例子中,地址sip:UAA@example.com就是sip:UserA@10.20.30.40的别名。然后,注册服务器example.com就可以把呼叫转发给地址10.20.30.40。

OPTIONS

询问一个用户代理的“能力”(例如,该用户代理能够识别的消息和编码)。

  现在给出一些经常使用的响应消息:

消息

用法

100Trying

消息已收到,但是最终用户代理尚未进行处理。请等待。

180Ringing

最终用户代理已经收到消息,正在提示用户。请等待。

200OK

最终用户已经接受消息。

301MovedPermanently&302Moved
Temporarily

用户代理的地址已经改变,新的永久或临时地址位于Contact字段中。

400BadRequest

普通错误消息。客户端不能识别消息。

401Unauthorized&407Proxy
AuthenticationRequired

请使用证书重试。

404NotFound

要联系的用户不存在或尚未注册。

408RequestTimeout

另一方没有响应。这意味着SIP消息永远不会OK。所有重试都将被丢弃。这并不意味着电话响太长时间(电话可以永远响铃)。

  消息使用类似的头字段类型。下面给出其中的一些:

头字段

用法

From

SIP请求的发送者。

To

SIP请求的接受者。这通常与SIPURI相同(可以是一个“别名”或一个实际地址)。

Contact

用户代理的实际地址。

Call-ID

这并不是呼叫者的电话号码。它惟一地代表两个用户代理之间的完整呼叫或对话。所有相关的SIP消息都使用同一个Call-ID。例如,当一个用户代理收到一条BYE消息,根据Call-ID,它就知道要挂断哪次呼叫。

CSeq

消息的顺序编号。这在一次对话或一个Call-ID中是惟一的。这用于区别新的消息和“重试消息”。当一条初始消息没有及时OK时,重试就会进行,并会定时发送。

Content-Type

消息内payload的MIME类型。

Content-Length

payload的大小,以字节为单位。信封和payload之间由一空行隔开。

  还有一些与消息路由选择功能相关的头字段,如:Via、Route和Record-Route。许多头字段提供像Accept、User-Agent和Supported这样的功能。其他头字段则提供像Authorization、Privacy和WWW-Authenticate这样的安全性功能。还有很多其他的头字段存在。此外,这些字段中许多都有缩写语法(比如,From=f,To=t,等等)。

SIP的其他功能

  使用SIP及其扩展可以实现很多应用:

·
VoIP

·
视频会议

·
针对文本和数据的即时消息传递,比如MSNInstantMessenger

·
注册(我在线!)

·
到场(我的伙伴在不在?)

·
Click-to-talk(点击通话,即点击这里便可与一个技术支持代理交谈)

·
应答机器/互动式语音应答(InteractiveVoiceResponse,IVR)系统(“请输入您的密码。请记录您的姓名。英文请按1,西班牙语请按2…”)

·
网络游戏,比如Quake和一些手机游戏(甚至基于语音和IM)

·
基于手机的应用程序

·
移动电子商务

  基本上,如果是两个端点之间进行通信,SIP就能完成。

但是,关于活动的Web技术支持代理的想法又如何呢?现在我们能否使用SIP来实现它?我们能否使用Java来实现它?干脆点说,可以。

Java中的SIP

  我经常使用SIP。我可以负责任地说,Java为SIP提供了绝佳的支持。Java技术的一个分支把与开发SIP应用程序相关的许多细节抽象出来,这对于SIP开发人员大有帮助。下面这些技术大部分位于JAIN(JavaAPIsfor
IntegratedNetworks)工作组中。

·
JAINSIPAPI(JSR32)

·
SIPServletAPI(JSR116)

·
JAINSIPLite(JSR125)

·
SIPAPIforJ2ME(JSR180)

·
JAINSIMPLEPresence(JSR164)

·
JAINSIMPLEInstantMessaging(JSR165)

其他相关技术有:

·
JAINSDP(JSR141)

·
JavaMediaFrameworkforRTP
(J2SE可选包,并非JAIN的)

  如果您希望开发一个客户端应用程序,就需要一个客户端SIP引擎或者“堆栈”。在这里可以找到一个优秀的开源JavaSIP堆栈。它还支持SDP。如果不想自己开发SIP电话,您可以使用这个。

结束语

  本文简要介绍了SIP、它的使用场景,以及一些SIP语法。我们还了解了各种与SIP相关的Java技术。尽管本文不够详尽,我还是希望它能够激发您的兴趣,并促使您开始使用它。SIP的时代已经到来,现在使用它可以实现很多很酷的想法。

在本系列文章的第2部分中,我将说明如何使用SIPServletAPI编写一个聊天室应用程序。

参考资料

·
SIP
Forum——SIP工作组的社区站点

·
SIP
Center——另一个广受欢迎的社区站点

·
RFC3261——SIP规范

·
SIP的相关RFC和草案

·
开源JAIN
SIP堆栈

·
开源SIP软件电话

  原文出处:http://dev2dev.bea.com/pub/a/2005/09/introduction-sip-part-1.html

作者简介

EmmanuelProulx是一位J2EE和EnterpriseJavaBeans方面的专家,也是一位获得认证的WebLogicServer7.0工程师

SIP简介,第2部分:SIPSERVLET

时间:2006-04-19

作者:EmmanuelProulx

本文关键字:SIP,SessionInitiationProtocol,JAIN,RFC3261,WebLogicCommunicationsPlatform,会话发起协议,信令

http://dev2dev.bea.com.cn/techdoc/20060419772.html

摘要

  会话发起协议(SessionInitiationProtocol,SIP)是一个重要的信令协议,它正在迅速被电信业采用以构建下一代应用程序。Java是用于SIP开发的极好平台,尤其是在进行服务器端开发时。类似于HTTPservlet,SIPServletAPI使SIP服务的开发变得更轻松。本文将介绍SIPservlet技术,并提供一个带注释的例子。

简介

  即时消息传递正在改变人们的生活。它是一个非常有用的工具,结合了电子邮件、Internet电话以及文件传输应用程序的优点。用户甚至可以看到谁在线、谁的状态为“忙碌”。当然了,人们可以用它来长时间地进行不创造任何效益的聊天。但是,员工也可以利用它在老板会见客户时向他发送极为重要的信息。

  所以,市场上出现如此多的不同种类的即时消息传递应用程序也就不足为怪了。有这么多的选择应该是一件好事,可是如果员工使用的应用程序与老板使用的不同,那又会怎么样呢?这将是一个大问题,因为这些应用程序大多都使用专有的协议。

  SIP为我们带来了福音。SIP很有可能会成为标准的即时消息传递协议。

  在本文中,我将开发一个简单的SIP应用程序——一个允许SIP即时messenger(消息传递应用程序)彼此联系并互相传播消息的聊天室服务器端。

SIPSIMPLE

  SIMPLE
,即SIPInstantMessagingand
PresenceLeveragingExtension(SIP即时消息和现场支持扩展)的缩写,是一个工作组以及一组SIP扩展。其中的一个扩展是MESSAGE消息。可以用它来发送包含文本和二进制内容的任意组合的即时消息。这种消息使用起来非常简单,这也是我决定使用它来开发第一个SIP应用程序的原因。

TextClient

  为了测试我们的应用程序,我提供了一个小型SIP即时messenger应用程序(参见文章结尾处的“下载”部分)。该应用程序向其他messenger或服务器发送MESSAGE消息。用户界面中包含了客户端的地址、好友地址的输入字段、一个文本消息以及一个提交按钮。图1显示了正在运行的TextClient。

图1.运行中的TextClient

  要启动TextClient,只需使用以下命令:

  java
-jartextclient.jardev2dev.textclient.TextClientusernameport

该命令使用JAINSIPAPI参考实现作为一个SIP协议栈。我们提供了该工具的源代码,如果您希望了解更多,我推荐您读一下源代码。

ChatRoomServer

  下面是示例应用程序的需求

  聊天室是一个虚拟空间,不同的即时messenger应用程序可以在其中进行交互。传入聊天室的消息将向聊天室中其他所有的人进行广播。换句话说,所有的消息都可以被所有用户看到。这意味着,当一个消息到达服务器端应用程序时,用户的地址将被添加到一个列表中。然后消息将被发送到该列表中的所有用户。

  此外,还可以实现“命令”。命令以正斜杠(/)开头,它不被广播,而是由服务器自己处理,用于特定功能。我将实现的命令包括:

·
/join:默默地进入一个聊天室,不广播任何消息。

·
/who:打印一份该聊天室所有用户的列表。

·
/quit:离开聊天室,不再有消息传入。

SIPServlet
API

  SIP
ServletAPI(JSR116)是一个服务器端接口,它描述了一个SIP组件或服务的容器。这正适合用于开发ChatRoomServer。下载该规范,并解压缩。生成的文件夹包括一些库(servlet.jar、sipservlet.jar)以及文档。我无法获得运行示例SIPservlet的参考实现,所以我想您也不必费心去找它了。

  SIP
servlet最核心的概念是包含。SIP服务是部署或运行在在一个容器或SIP应用服务器上的打包SIPservlet。容器提供了可供应用程序使用的许多服务,比如自动重试、消息调度和排队、分流和归并,以及状态管理。应用程序中只需包含高级的消息处理和业务逻辑。这使SIP服务的开发成为一件轻而易举的事情。

  本文的目的不是要提供对SIPServletAPI技术的全面介绍。因此我只简要概述了该API和示例代码,更多信息请参见文章结尾处的“参考资料”部分。

服务器端代码

  如果您曾经开发过HTTPservlet,那么服务器端的代码会让您感到非常熟悉。如果您还不知道什么是servlet,您应该首先了解一下。SIPServlet规范是HTTPServlet规范的扩展。其语法、容器行为,甚至方法名都是相似的。

  下面我将详细分析该例子。它主要由3个部分组成:

1.生命周期方法

  这些方法在启动或关闭servlet时被容器调用:

publicclassChatRoomServerextendsSipServlet{


/**Contextattributekeytostoreuserlist.*/

publicstaticStringTHE_LIST="dev2dev.chatroomserver.userList";


/**Initparameterkeytoretrievethechatroom'saddress.*/

publicstaticStringTHE_NAME="dev2dev.chatroomserver.name";


/**Thischatroomserver'saddress,retrievedfromtheinitparams.*/

publicStringserverAddress;


/**Thisiscalledbythecontainerwhenstartinguptheservice.*/

publicvoidinit()throwsServletException{

super.init();

getServletContext().setAttribute(THE_LIST,newArrayList());

serverAddress=getServletConfig().getInitParameter(THE_NAME);

}


/**Thisiscalledbythecontainerwhenshuttingdowntheservice.*/

publicvoiddestroy(){

try

{

sendToAll(serverAddress,"Serverisshuttingdown--goodbye!");

}catch(Throwablee)

{//ignoreallerrorswhenshuttingdown.

e.printStackTrace();

}

super.destroy();

}


...

  在初始化方法中,我创建了一个所有会话共享的全局属性。这是用户的列表。我还获得了该聊天室的地址(servlet参数)以备将来使用。

2.消息处理方法

  SIP
servlet与HTTPservlet稍有不同。对于HTTPservlet,您处理传入的请求,并发送响应消息。而对于SIPservlet,可以发送和接收请求和响应。我将说明如何做到这一点。

  当收到消息(请求或响应)时,容器将调用下面的方法。容器将按照下面图表的顺序调用这些方法,也可以重写这些方法来根据消息的类型处理消息:

voidservice(ServletRequest,ServletResponse)如果对其进行重写,不要忘记调用super.service()其默认实现调用以下方法之一:

voiddoRequest(SipServletRequest)

如果对其进行重写,不要忘记调用super.doRequest()。

其默认实现调用以下方法之一:

void
doResponse(SipServletResponse)

如果对其进行重写,不要忘记调用super.doResponse()。

其默认实现调用以下方法之一::

下列请求方法之一(自解释):

1.
doAck(SipServletRequest)

2.
doBye(SipServletRequest)

3.
doCancel(SipServletRequest)

4.
doInfo(SipServletRequest)

5.
doInvite(SipServletRequest)

6.
doMessage(SipServletRequest)

7.
doNotify(SipServletRequest)

8.
doOptions(SipServletRequest)

9.
doPrack(SipServletRequest)

10.
doRegister(SipServletRequest)

11.
doRequest(SipServletRequest)

12.
doResponse(SipServletResponse)

13.
doSubscribe(SipServletRequest)

下列响应方法之一:

14.
doProvisionalResponse(SipServletResponse)—对应于1xx-类响应消息。

15.
doSuccessResponse(SipServletResponse)—对应于2xx-类响应消息。

16.
doRedirectResponse(SipServletResponse)—对应于3xx-类响应消息。

17.doErrorResponse(SipServletResponse)—对应于4xx-、5xx-以及6xx-类响应消息。

  例如,MESSAGE可以调用以下方法:

18.service(),传入一个SipServletRequest(必须进行类型转换)以及null

19.doRequest()

20.doMessage()

  通常只重写最后一级的方法,除非使用了非标准的SIP消息,或者希望收集有关消息的统计信息。

下面是处理即时消息的代码:

/**ThisiscalledbythecontainerwhenaMESSAGEmessagearrives.*/

protectedvoiddoMessage(SipServletRequestrequest)throws

ServletException,IOException{


request.createResponse(SipServletResponse.SC_OK).send();


Stringmessage=request.getContent().toString();

Stringfrom=request.getFrom().toString();


//Auseraskedtoquit.

if(message.equalsIgnoreCase("/quit")){

sendToUser(from,"Bye");

removeUser(from);

return;

}


//Addusertothelist

if(!containsUser(from)){

sendToUser(from,"Welcometochatroom"+serverAddress+

".Type'/quit'toexit.");

addUser(from);

}


//Iftheuserisjoiningthechatroomsilently,nomessage

//tobroadcast,return.

if(message.equalsIgnoreCase("/join")){

return;

}


//WecouldimplementmoreIRCcommandshere,

//see'target='_blank'>http://www.mirc.com/cmds.html[/code]
sendToAll(from,message);

}


/**

*Thisiscalledbythecontainerwhenanerrorisreceived

*regardingasentmessage,includingtimeouts.

*/

protectedvoiddoErrorResponse(SipServletResponseresponse)

throwsServletException,IOException{

super.doErrorResponse(response);

//Thereceiverofthemessageprobablydroppedoff.Remove

//thereceiverfromthelist.

Stringreceiver=response.getTo().toString();

removeUser(receiver);

}


/**

*Thisiscalledbythecontainerwhena2xx-OKmessageis

*receivedregardingasentmessage.

*/

protectedvoiddoSuccessResponse(SipServletResponseresponse)

throwsServletException,IOException{

super.doSuccessResponse(response);

//Wecreatedtheappsession,wehavetodestroyittoo.

response.getApplicationSession().invalidate();

}

  第一个方法在收到一个MESSAGE消息时被调用。最初以一条200OK消息响应,表明收到了消息。然后它处理服务器命令,比如/join。最后,它调用一个业务逻辑方法来广播传入的消息。

  传入的错误响应消息表明上一个请求失败了。这可能意味着有一个用户被断开了。只需将该用户从列表中移除即可。

  成功的响应消息表明上一个MESSAGE消息被即时messenger正确地接收了。因此不再需要该会话,可以将其删除了。通常,MESSAGE消息是以无状态的形式发送的,并不保存消息之间的连接信息。(对于INVITE消息来说,情况不是这样的,它打开一个有状态的会话直到发送BYE。)

3.业务逻辑代码

  其余的代码由helper方法组成。前两个方法向即时messenger发送消息。要发送消息,使用一个工厂创建以下两项:

0.一个SipApplicationSession(稍后将详细介绍)

1.一个请求消息

  此时,可以随心所欲地修改消息。在我们的例子中,我们在有效负载中添加即时消息文本。最后,发送该消息。

privatevoidsendToAll(Stringfrom,Stringmessage)

throwsServletParseException,IOException{

SipFactoryfactory=(SipFactory)getServletContext().

getAttribute("javax.servlet.sip.SipFactory");


Listlist=(List)getServletContext().getAttribute(THE_LIST);

Iteratorusers=list.iterator();

while(users.hasNext()){//Sendthismessagetoallonthelist.

Stringuser=(String)users.next();


SipApplicationSessionsession=

factory.createApplicationSession();

SipServletRequestrequest=factory.createRequest(session,

"MESSAGE",serverAddress,user);

Stringmsg=from+"sentmessage:\n"+message;

request.setContent(msg.getBytes(),"text/plain");

request.send();

}

}


privatevoidsendToUser(Stringto,Stringmessage)

throwsServletParseException,IOException{

SipFactoryfactory=(SipFactory)getServletContext().

getAttribute("javax.servlet.sip.SipFactory");

SipApplicationSessionsession=factory.createApplicationSession();

SipServletRequestrequest=factory.createRequest(session,

"MESSAGE",serverAddress,to);

request.setContent(message.getBytes(),"text/plain");

request.send();

}


privatebooleancontainsUser(Stringfrom){

Listlist=(List)getServletContext().getAttribute(THE_LIST);

returnlist.contains(from);

}


privatevoidaddUser(Stringfrom){

Listlist=(List)getServletContext().getAttribute(THE_LIST);

list.add(from);

}


privatevoidremoveUser(Stringfrom){

Listlist=(List)getServletContext().getAttribute(THE_LIST);

list.remove(from);

}


}

部署描述符

  对于HTTP
servlet,还必须编写web.xml部署描述符。而在SIPservlet中,对应的文件是sip.xml,我们在其中列出SIPservlet、初始化参数以及映射(哪个SIPservlet处理哪些SIP消息)。关于该文件语法的更多信息,请参见SIPServlet规范中15.5节的DTD。其语法类似于web.xml,但<servlet-mapping>标签除外。它不会将一个URL模式映射到servlet,而是(基于字段和子字段的内容)描述一个条件,SIP请求必须满足这个条件才能被映射到servlet。SIPServlet规范第11节描述了所有的字段、子字段以及用于该映射的条件。

  注意,该映射只用于初始请求;同一个会话/对话中的后续请求由处理初始请求的同一servlet处理。

下面是用于ChatRoomServer的XML代码:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEsip-app

PUBLIC"-//JavaCommunityProcess//DTDSIPApplication1.0//EN"

"http://www.jcp.org/dtd/sip-app_1_0.dtd">

<sip-app>

<servlet>

<servlet-name>ChatRoomServer</servlet-name>

<servlet-class>dev2dev.chatroomserver.ChatRoomServer</servlet-class>

<init-param>

<param-name>dev2dev.chatroomserver.name</param-name>

<!--Thiswillbereplacedbythebuildscript-->

<param-value>sip:chatroomname@serveraddress</param-value>

</init-param>

</servlet>


<servlet-mapping>

<servlet-name>ChatRoomServer</servlet-name>

<pattern>

<and>

<equal>

<var>request.uri.user</var>

<!--Thiswillbereplacedbythebuildscript-->

<value>chatroomname</value>

</equal>

<equal>

<var>request.method</var>

<value>MESSAGE</value>

</equal>

</and>

</pattern>

</servlet-mapping>


</sip-app>

  代码看起来很复杂,其实并非如此。Servlet映射说明了:

  如果请求URI的用户名部分等于chatroomname,则将传入的MESSAGE请求映射到ChatRoomServerServlet

  该聊天室名称只是一个占位符。在编译过程中,会用实际的聊天室名称替换关键字“chatroomname”。

  这么做有什么用呢?您可以将同样的服务部署多次,每次都使用其独有的聊天室名称,而消息可以自动发送到相应的servlet。

构建、打包、部署

  需要对SIP
servlet进行编译,并将其打包到SAR(ServletARchives)文件中。这些文件在功能上等效于WAR文件,结构也相同。参见图2:

图2.SAR文件结构

  最后一步是部署,这根据SIP应用服务器的不同而不同。通常需要将SAR文件复制到一个部署文件夹中,然后部署应用程序。

  下面的Ant脚本可以帮助部署:

<projectname="ChatRoomServer"default="build"basedir=".">


<!--Changethistospecifythenameofthechatroom.Inorderto

sendmessagestothischatroom,simplydeployjust4fun.sar,and

usetheaddresssip:just4fun@10.0.2.5060:5060.-->

<propertyname="chatroomname"value="just4fun"/>


<!--ChangethistotheaddressandportoftheSIPserver.-->

<propertyname="serveraddress"value="10.0.2.69:5060"/>


<!--ChangethistothelocationoftheSARdeploymentfolder.-->

<propertyname="sar.deployment"value=""/>


<propertyname="src"value="${basedir}/src"/>

<propertyname="lib"value="${basedir}/lib"/>

<propertyname="tmp"value="${basedir}/tmp"/>


<pathid="classpath">

<filesetdir="${lib}"/>

</path>


<targetname="build">

<mkdirdir="${tmp}"/>

<mkdirdir="${tmp}/WEB-INF"/>

<mkdirdir="${tmp}/WEB-INF/classes"/>

<mkdirdir="${tmp}/WEB-INF/lib"/>

<javacdebug="true"srcdir="${src}"destdir="${tmp}/WEB-INF/classes">

<classpathrefid="classpath"/>

</javac>

<copytodir="${tmp}/WEB-INF"file="${basedir}/sip.xml"/>

<replacefile="${tmp}/WEB-INF/sip.xml"token="chatroomname"value="${chatroomname}"></replace>

<replacefile="${tmp}/WEB-INF/sip.xml"token="serveraddress"value="${serveraddress}"></replace>

<zipdestfile="${basedir}/${chatroomname}.sar">

<zipfilesetdir="${tmp}"/>

</zip>

<copyfile="${basedir}/${chatroomname}.sar"todir="${sar.deployment}"/>

</target>


</project>

结果

  聊天室应用程序运行之后,试着通过运行两个TextClient实例来访问它。要确保运行在同一机器上的SIP应用程序使用的是不同的端口。下面的例子显示了运行在同一机器上的3个应用程序:

·
运行在ChatRoomServer上的SIP应用服务器,地址是sip:just4fun@10.0.2.69:5060。

·
地址为sip:snoopy71@10.0.2.69:5061的Text
client。

·
地址为sip:maria119@10.0.2.69:5062的Text
client。

  图3显示了结果。

图3.TextClient与ChatRoomServer交互

复杂应用程序

  我知道本文中的例子相对于我们通常要构建的应用程序来说有点过于简单了,现实中的大多数SIP应用程序都由大量代码组成。

  会话和状态:通常,SIP应用程序是一个状态机(statemachine),其中呼叫或会话都是长时间保持的(有状态的),直到断开。对于SIPservlet,呼叫是由SipApplicationSession表示的,它可以带属性(状态)。在呼叫中,每个会话(呼叫的分支)由SipApplicationSession中的SipSession表示。(两人间的back-to-back会话要使用一个SipApplicationSession和两个SipSession。会议呼叫可能包含更多的SipSession。这些都可以带属性。容器会根据消息的上下文自动提供相应的会话对象。

  分层设计:最糟糕的是将所有的代码放入单个的大型SIPservlet。应该按照相对独立的层来设计复杂的应用程序。一个明显的层就是包括连接池的数据库层。也可以包含一个与SIP信令分离的业务逻辑层。另一个方面是有效负载分析,它应该被构建为一个可重用的层。

  其他技术:存在许多先进的SIPservlet技术,包括请求代理、重定向和循环、会话超时管理、身份验证、国际化、TCP支持、计时器、会话监听程序以及错误管理。很明显,本文并没有涵盖所有这些方面,但是您可以在SIPServlet规范中找到相关内容。

  例子:可以参见“参考资料”部分,其中有可以帮助您了解更复杂的SIP编程的例子。

结束语

  标准促进了互操作性,从而促进了协作。而协作——不管它是用于朋友间的轻松聊天,还是用于重要的文件传输——都是一件好事。

  SIP是一个大有前途的电信标准,而SIPServletAPI则是轻松快速地开发服务器端SIP应用程序的极佳方式。在本文中,我们通过一个简单的例子,概述了SIPservlet编程。希望通过本文,能够帮助您在协作的道路上迈出一大步。

下载

·
TextClient应用程序

·
ChatRoomServer应用程序

参考资料

·
JAINSIPServletAPI

·
SIPServletDeveloper'sHomepage——包括大量信息以及SIP
servlet的例子

·
SIP:Creatingnext-generation
telecomapplications——对SIP的简介,以及一个示例SIP
servlet

·
SIPServletIntroduction&
SampleApplication白皮书(需要填写表单才能阅读白皮书)

原文出处:http://dev2dev.bea.com/pub/a/2006/02/sip-servlet.html

作者简介

EmmanuelProulx是一位J2EE和EnterpriseJavaBeans方面的专家,也是一位获得认证的WebLogicServer7.0工程师

一些和sip协议相关的FAQ

http://blog.csdn.net/IOKE/archive/2004/09/21/112030.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: