摘要
本文介绍如何在 Java SE 上使用会话发起协议 (SIP) 开发客户端应用程序。文中展示 JAIN SIP API 这种强大的“SIP 堆栈”。首先介绍一个简单的 IM 应用程序,然后对其进行剖析来了解该技术。
关于 JAIN SIP API
集成网络 Java API (JAIN) 是一个管理电信标准的 JCP 工作组。会话发起协议 (SIP) 是一种标准通信协议,之前的文章中已对其进行了讨论。将 Java 与 SIP 结合起来就得到了 JAIN SIP API,这是一种强大的标准电信 API。这一想法始于 1999 年的 JSR 32。该 参考实现是开源的、非常稳定且广泛应用。如果您使用 Java 编写 SIP 应用程序,使用此 SIP 堆栈则非常合适。
此 API 通常用于客户端应用程序开发。其他基于容器的技术,如 SIP Servlet API(例如,参见 BEA WebLogic SIP Server),更适合于服务器端开发。之前的一篇文章重点介绍了 SIP Servlet API。现在我们来看看客户端 API 堆栈。
先决条件
本文需要对 Java 有充分了解。同时,建议您熟悉一下 SIP,因为使用 JAIN SIP API 要求对此协议有充分了解。SIP 信令(尤其是消息和头)特别重要。有关相关信息的链接,请参见本文结尾处的“参考资料”部分。
库
要获得 JAIN SIP API 库,请访问 jain-sip 项目主页 。单击此链接可转到下载页。您将需要获取以下文件:
JainSipApi1.2.jar:SIP 接口和主类
JainSipRi1.2.jar:SIP 参考实现
log4j-1.2.8.jar(包含在 jain-sip/lib 文件夹下的 jain-sip-1.2.jar 文件中):日志记录服务
concurrent.jar(包含在 jain-sip/lib 文件夹下的 jain-sip-1.2.jar 文件中):并发性实用程序
本示例不需要其他文件。请在您的项目中包括上面这些库。
有关 JAIN SIP API 中提供的各种类和接口的列表,建议您查看附录的第一部分。这些类和接口是标准的,免费提供,很快您就可以看到如何将其用作 SipLayer 示例类的一部分。
TextClient 示例应用程序
作为 JAIN SIP API 程序的一个示例,我们来剖析一个应用程序,如果您已阅读我先前所写的一篇有关 SIP Servlet 主题的文章,可能已经熟悉该应用程序。TextClient 是一个即时消息传递应用程序,可通过 SIP 协议发送和接收文本消息。该应用程序的一个实例可以将消息发送到另一个实例,但理论上可以使用此客户端将消息发送到其他类型的 SIP 即时消息传递客户端,甚至发送到 SIP 服务器应用程序。图 1 显示了一个屏幕截图。
图 1. TextClient 返回值
要运行该应用程序,首先需要下载所提供的源代码。其次,必须使用提供的 Ant 脚本构建该应用程序。这将产生一个 JAR 文件。最后,使用以下命令运行该应用程序:
java -jar textclient.jar
您可以运行此客户端的多个实例(确保它们使用不同的端口)并在彼此之间发送消息。本文的其余部分将研究此示例应用程序的代码。
TextClient 代码概述
整个 TextClient 代码由两个类和一个接口组成。下表介绍了这些类和这个接口:
类/接口
说明
TextClient
主类,包含应用程序小部件的 Swing 窗口。参见图 1。
SipLayer
处理所有 SIP 通信的类。它由 TextClient 类实例化,并通过 MessageProcessor 接口回调。
MessageProcessor
回调接口(观察器模式),用于将 SipLayer 与其容器相分离。
在下面的部分中,我将介绍 MessageProcessor,然后大部分时间用来介绍 SipLayer。我几乎不会谈到 TextClient 类,因为它只包含用户接口 Swing 代码,并且与本文主题无关。有关详细信息,请参见本文附带的源代码。
消息处理器
在开始介绍 SipLayer 类之前,我要先简单介绍一下 MessageProcessor 接口。为使 SIP 层与 GUI 层分离,您需要使用回调接口,该接口允许从前者发送信息,而无需知道后者的签名。该接口如下所示:
public interface MessageProcessor
{
public void processMessage(String sender, String message);
public void processError(String errorMessage);
public void processInfo(String infoMessage);
}
SipLayer 构造函数将接受此接口的一个实现(即 TextClient 对象)作为参数并保存在该对象上。稍后您就可以使用此对象将信息发送回 GUI。
SIP 堆栈准备
我们来开始编写 SipLayer 类。TextClient 必须能够接收从其他 SIP 端点传来的异步消息。为此使用了观察器模式:SipLayer 类实现 SipListener 接口以处理传入消息:
public class SipLayer
implements SipListener {
此接口具有以下方法:
void processRequest(RequestEvent evt);
void processResponse(ResponseEvent evt);
void processTimeout(TimeoutEvent evt);
void processIOException(IOExceptionEvent evt);
void processTransactionTerminated(TransactionTerminatedEvent evt);
void processDialogTerminated(DialogTerminatedEvent evt);
在本示例中,最重要的方法显然是用于处理传入消息的 processRequest() 和 processResponse()。稍后我再来看这两个方法。
接下来是两个用来存储稍后所需对象的字段。这两个字段与 SIP API 没有直接关系,但本例中需要它们。第一个是前面所讨论过的 MessageProcessor 对象。您还需要准备好用户名。这两个字段均有 getter 和 setter,为简单起见,本文不介绍它们。
private MessageProcessor messageProcessor;
private String username;
接下来是构造函数。启动 JAIN SIP API 应用程序的典型方式(TextClient 也遵循这种模式)是创建一系列稍后将用到的对象。我将谈谈一些工厂,以及一个已初始化的 SIP 堆栈。
private SipStack sipStack;
private SipFactory sipFactory;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
private SipProvider sipProvider;
public SipLayer(String username, String ip, int port) throws
PeerUnavailableException, TransportNotSupportedException,
InvalidArgumentException, ObjectInUseException,
TooManyListenersException {
setUsername(username);
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
Properties properties = new Properties();
properties.setProperty("javax.sip.STACK_NAME",
"TextClient");
properties.setProperty("javax.sip.IP_ADDRESS",
ip);
sipStack = sipFactory.createSipStack(properties);
headerFactory = sipFactory.createHeaderFactory();
addressFactory = sipFactory.createAddressFactory();
messageFactory = sipFactory.createMessageFactory();
...
SIP 工厂用于实例化 SipStack 实现,但由于可能存在多个实现,您必须通过 setPathName() 方法指定所需的一个实现。名称“gov.nist”表示所获取的 SIP 堆栈。
SipStack 对象接受一些属性。至少必须设置堆栈名称。其他属性是可选的。下面我将设置堆栈所使用的 IP 地址,针对一台计算机具有多个 IP 地址的情况。注意,有些是标准属性,即所有 SIP API 实现必须支持的属性,还有一些是非标准属性,这些属性依赖于具体的实现。有关这些属性的链接,请参见“参考资料”部分。
下一步是创建一对 ListeningPoint 和 SipProvider 对象。这两个对象提供发送和接收消息的通信功能。一组对象用于 TCP,一组对象用于 UDP。在此还要选择 SipLayer (this) 作为传入 SIP 消息的监听器:
...
ListeningPoint tcp = sipStack.createListeningPoint(port, "tcp");
ListeningPoint udp = sipStack.createListeningPoint(port, "udp");
sipProvider = sipStack.createSipProvider(tcp);
sipProvider.addSipListener(this);
sipProvider = sipStack.createSipProvider(udp);
sipProvider.addSipListener(this);
}
至此,构造函数完成。您已经使用 JAIN SIP API 创建了一个 SipStack 实例、一组工厂、两个 ListeningPoint,还有一个 SipProvider。后面的有些方法将需要使用这些对象来发送和接收消息。