JAIN SIP API 简介
页码: 1, 2, 3

接收响应

之前您注册了一个监听传入消息的监听器。监听器接口 SipListener 包含 processResponse() 方法,在 SIP 响应消息到达时由 SIP 堆栈调用该方法。processResponse() 接受类型为 ResponseEvent 的单个参数,该参数封装了一个 Response 对象。现在我们来实现这一方法。

                         

public void processResponse(ResponseEvent evt) {
        Response response = evt.getResponse();
        int status = response.getStatusCode();

        if( (status >= 200) && (status < 300) ) { //Success!
                messageProcessor.processInfo("--Sent");
                return;
        }

        messageProcessor.processError("Previous message not sent: " +
                        status);
}
                      

在该方法中,您将检查先前的 MESSAGE 消息的响应是表示成功(2xx 范围的状态码)还是错误(在该范围之外)。然后通过回调接口将此信息重新转发给用户。

通常在 processResponse() 方法中只读取 Response 对象。唯一的例外是对 INVITE 消息的成功响应;在此情况下,您必须直接回发 ACK 请求,如下所示:

                         

Dialog dialog = evt.getClientTransaction().getDialog()
Request ack =  dialog.createAck()
dialog.sendAck( ack );
                      

有关 Response 接口的描述,请参见附录

接收请求

接收 SIP 请求消息与接收响应一样轻松。只需实现 SipListener 接口的另一个方法 processRequest(),SIP 堆栈就会自动调用该方法。此方法只有一个参数,即 RequestEvent 对象,该对象包含(您猜对了)一个 Request 对象。这与您先前看到的类型相同,并具有相同的方法。但是,不要在传入请求上设置任何字段,因为这样没有意义。

processRequest() 的典型实现将分析请求,然后创建相应的响应并将其发送回去。下面演示现在应如何操作:

                         

public void processRequest(RequestEvent evt) {
        Request req = evt.getRequest();

        String method = req.getMethod();
        if( ! method.equals("MESSAGE")) { //bad request type.
                messageProcessor.processError("Bad request type: " + method);
                return;
        }

        FromHeader from = (FromHeader)req.getHeader("From");
        messageProcessor.processMessage(
                        from.getAddress().toString(),
                        new String(req.getRawContent()));
        Response response=null;
        try { //Reply with OK
                response = messageFactory.createResponse(200, req);
                ToHeader toHeader = (ToHeader)response.getHeader(ToHeader.NAME);
                toHeader.setTag("888"); //Identifier, specific to your application
                ServerTransaction st = sipProvider.getNewServerTransaction(req);
                st.sendResponse(response);
        } catch (Throwable e) {
                e.printStackTrace();
                messageProcessor.processError("Can't send OK reply.");
        }
}
                      

在此情况下,您始终使用成功响应 (200) 进行回复,但还可以传回任何错误响应(通常为 4xx 范围)。本文给出了 SIP 状态码的有用列表。

处理错误情况

SipListener 接口中还有一些您尚未实现的其他方法。由于某些原因无法发送请求时 SIP 堆栈将调用这些方法。例如,当接受消息的端点未能即时应答时,将调用 processTimeout()。这是一个没有响应的特殊情况,因此 Response 对象不可用。除其他内容外,TimeoutEvent 参数还包含了超时请求的 ClientTransaction,您可以根据需要使用它重新链接到原始请求。在本实现中,您只是使用回调接口通知用户:

                         

public void processTimeout(TimeoutEvent evt) {
        messageProcessor.processError("Previous message not sent: " +
                        "timeout");
}
                      

类似地,使用以下方法处理输入/输出 (IO) 错误:

                         
public void processIOException(IOExceptionEvent evt) {
        messageProcessor.processError("Previous message not sent: " +
                        "I/O Exception");
}
                      

点对点与客户端/服务器

SIP 客户端应用程序可以单独使用(点对点),也可以与服务器一起使用以提供代理或呼叫路由等额外功能。

建议您阅读我关于 SIP Servlet 的文章。这篇文章中包含一个简洁的 SIP 服务器应用程序,可与 TextClient 结合使用来提供聊天室类型的服务。该文介绍如何将 TextClient 与 BEA WebLogic SIP Server 结合使用,使其作用倍增。

下载

在此下载 TextClient 源代码

总结

本文概述了 JAIN SIP API,并介绍了如何编写简单的应用程序来使用此技术。现在,您应较好地了解了可用的 API,并了解了如何使用 SIP 编写自己的 IM 客户端。

不过,何必止步于此?我可以向此应用程序添加更多功能。如前所述,如果客户端与服务器应用程序交谈,可令作用倍增。如果需要建议,请考虑以下内容:

  • 自动文本应答器、存储和转发(例如,“John is offline right now, but he will receive your messages as soon as he logs back in”)
  • 简洁的联网检查器视频游戏
  • 针对笔记本电脑的基于位置的服务
  • 媒体共享的客户端
  • 类似 RSS 的客户端

可能性几乎是无限的。

参考资料

附录

本部分是 JAIN SIP API 中可用的各种类和接口的参考。

API 概述

以下概述了 JAIN SIP API 参考实现中的主要类和接口。

类/接口 说明
SipFactory / AddressFactory / HeaderFactory / MessageFactory 创建系统的各种对象的工厂类。这些类将返回实现标准接口的对象。
SipStack 您需要的第一个接口,用于创建 ListeningPointSipProvider
ListeningPoint 此接口封装一个传输/端口对(例如,UDP/5060)。
SipProvider 此接口用于发送 SIP 消息。还可以使用此接口为传入 SIP 消息注册监听器。参见下面的 SipListener
SipListener 必须实现此接口才能接收传入 SIP 消息。
RequestEvent / ResponseEvent 表示传入 SIP 请求、响应。将传递到 SipListener 进行处理。分别包含 RequestResponse 对象。
TimeoutEvent 表示传出请求未得到答复的故障情况。将传递到 SipListener 进行处理。
IOExceptionEvent 表示一种故障情况,此时发送传出请求出现输入/输出问题。将传递到 SipListener 进行处理。
Request / Response 表示 SIP 请求、响应。两者都是 Message 接口的子接口。可通过它们访问 SIP 消息的头、内容及其他部分。
Dialog 此接口的对象封装了 SIP 对话。(提醒:在一个对话中,所有消息与同一呼叫相关联;对话通常起始于 INVITE,结束于 BYE。)
ClientTransaction / ServerTransaction 封装 SIP 事务。(提醒:所有事务起始于请求,结束于最终响应。事务通常生存于对话中。)

Message 接口

Message 接口是 SIP 消息的基接口。下面概述了可用的方法,供您参考:

方法 说明
void addHeader(Header)
void setHeader(Header)
将头字段设置为 SIP 消息。第一个方法可用于可重复或者可以有多个值的头,如 Contact 头。第二个方法删除此类型的现有头,然后添加单个头值。
void removeHeader(Header) 删除此类型的现有头。
ListIterator getHeaderNames() 返回所有头名称。
ListIterator getUnrecognizedHeaders() 返回非标准头类型的头名称。
Header getHeader(String)
ListIterator getHeaders(String)
特定头的 getter。第二种形式返回可重复头(或具有多个值的头,如 Contact 头)的所有值。
void setContent(Object, ContentTypeHeader) 设置消息的负载以及 Content-Type 头。如果类型为字符串,还将设置 Content-Length,否则使用 void setContentLength(ContentLengthHeader)
byte [] getRawContent()
Object getContent()
检索消息的负载。
void removeContent() 清空负载。
void setContentLength(ContentLengthHeader)
ContentLengthHeader getContentLength()
void setContentLanguage(ContentLanguageHeader)
ContentLanguageHeader getContentLanguage()
void setContentEncoding(ContentEncodingHeader)
ContentEncodingHeader getContentEncoding()
void setContentDisposition(ContentDispositionHeader)
ContentDispositionHeader getContentDisposition()
特殊的与负载有关的头访问器。极少使用。
void setExpires(ExpiresHeader)
ExpiresHeader getExpires()
管理 Expires 头。
void setSipVersion(String)
String getSipVersion()
SIP 版本元素的访问器。极少使用,默认为 SIP/2.0。
Object clone() 创建消息的副本。极少使用。

Request 接口

现在来大致了解 Request 接口(上面 Message 的子接口):

方法 说明
String getMethod()
void setMethod(String)
方法元素的访问器。可以为任何 SIP 方法,包括 Request 接口常数中的方法:ACK、BYE、CANCEL、INVITE、OPTIONS、REGISTER、NOTIFY、SUBSCRIBE、MESSAGE、REFER、INFO、PRACK 和 UPDATE。
URI getRequestURI()
void setRequestURI(URI)
请求 URI 的访问器,即 SIP 请求的第一行。通常,这是 SipURI 的实例。

Response 接口

Response 接口也扩展了 Message 接口:

方法 说明
void setStatusCode()
int getStatusCode()
状态码的访问器。这可以是任何 SIP 状态码,包括 Response 接口的常数成员中的状态码。以下是其中几种:RINGING (180)、OK (200)、BAD_REQUEST (400) 等等。
void setReasonPhrase(String)
String getReasonPhrase()
易读的状态码说明的访问器。

Emmanuel Proulx 是 J2EE 和 SIP 方面的专家。他是经过认证的 WebLogic Server 工程师。