使用
Microsoft
实时通信
API
增强多客户端通信
Jim Huang
Sr. Technical Marketing Engineer
Intel Corporation
2003
年
2
月
适用于:
Microsoft® Windows® XP
摘要:
学习如何使用实时通信
(RTC)
客户端
API
,结合出席信息、配置文件和好友列表等功能来创建社区并跟踪用户的可用性。本文以前一篇文章为基础,将多客户端通信与
Microsoft
实时通信
API
集成到一起。
目录
简介
本文将进一步介绍使用
RTC
客户端
API
添加出席信息、配置文件和好友列表等功能来创建社区的简单性。您将学习使用第一篇文章中创建的
Windows XP
应用程序来实现此功能的步骤。
准备工作
本文假设您熟悉
XML
架构。有关初始化
RTC
对象的信息,请参阅第一篇文章。
本文附带的源代码有两个版本:
·
AVDConf2.exe
示例代码(可从
Intel®
的
Real-Time Communication Sample Application [
英文
]
页面获得)中包含的功能与
RTCSample.exe
示例代码包含的功能相同。另外还包含白板共享、应用程序共享、带有超线程技术检测的
Intel® Pentium® 4
处理器和
CPU
利用率指示器。
AVDConf2.exe
代码使用了很多
RTCSample.exe
中的示例,
RTC
代码节选自
Microsoft Software Developers Network Web
站点。
注意:
超线程技术要求计算机系统中安装了
3.06 GHz
或更高频率的
Intel Pentium 4
处理器、使用该技术的芯片组和
BIOS
以及针对该技术进行了优化的操作系统。根据所使用的特定硬件和软件,性能会有所不同。有关详细信息,请参阅
Hyper-Threading Technology
(英文)。
您需要安装:
·
Visual Studio® 6.0 SP5
·
Microsoft Platform SDK
·
SIP Server
或
Windows .NET Server Beta 3
会话启动协议服务器
目前,要将出席信息、配置文件和好友列表集成到应用程序中,需要使用
SIP
服务器或
Windows .NET Server Beta 3
。需要出席信息(例如启用好友列表功能)时,要求使用
SIP
注册服务器。用户可以在服务器上注册其出席信息,并通过此服务检索其他人的出席信息。
服务器使用会话启动协议
(SIP)
及其相关协议
SIMPLE
作为底层通信协议。
SIP
为多模式通信提供了出色的支持。
SIP
和
SIMPLE
不仅是文本消息共享协议,而且可以管理声音、视频、应用程序共享等。
会话启动协议
SIP
协议用于在
IP
网络中启动会话并注册出席信息。会话可以是
PC
到
PC
的简单双向通信,也可以是协作的多媒体会议会话。
SIP
是一种
Internet
工程任务组
(IETF)
信号处理协议,用于建立、操作和销毁会话。
SIP
的主要用途是帮助会话启动者向各地可能的会话参加者发出邀请。
SIP
被描述为
“
简单、可扩展
”
的
IP
电话信号处理协议。
初始化出席信息和好友列表通知
要接收出席信息、好友列表和配置文件的新事件通知,请设置以下事件过滤器掩码,以便应用程序从
RTC
层接收事件通知。这些事件掩码是第一个示例应用程序中设置的事件掩码的补充。
#define RTCEF_REGISTRATION_STATE_CHANGE 0x00000002
#define RTCEF_BUDDY 0x00000100
#define RTCEF_WATCHER 0x00000200
#define RTCEF_PROFILE 0x00000400
注册所有事件的一种简单方法是使用
RTCEF_ALL
宏通知
RTC
层向应用程序发送所有事件。
long lEventMask = RTCEF_ALL;
处理
RTC
事件
以下代码段显示了使用用户配置文件注册到
SIP
服务器、发送和接收出席信息和好友列表事件时需要处理的其他事件。接收到每个事件后,事件过滤器方法将使用适当的
RTC
接口处理收到的事件。
HRESULT CAVDConfDlg::OnRTCEvent(UINT message, WPARAM wParam, LPARAM lParam)
{
IDispatch * pDisp = (IDispatch *)lParam;
RTC_EVENT enEvent = (RTC_EVENT)wParam;
HRESULT hr;
//
基于 RTC_EVENT 类型,查询
//
适当的事件接口并调用辅助
//
方法来处理事件
switch ( wParam )
{
... .
case RTCE_REGISTRATION_STATE_CHANGE:
{
IRTCRegistrationStateChangeEvent * pEvent = NULL;
//
获取与当前会话关联的事件句柄。
hr = pDisp->QueryInterface( IID_IRTCSessionStateChangeEvent,
(void **)&pEvent );
if (SUCCEEDED(hr))
{
OnRTCSessionStateChangeEvent(pEvent);
SAFE_RELEASE(pEvent);
}
}
break;
case RTCE_BUDDY:
{
IRTCBuddyEvent * pEvent = NULL;
hr = pDisp->QueryInterface( IID_IRTCBuddyEvent,
(void **)&pEvent );
if (SUCCEEDED(hr))
{
OnRTCBuddyEvent(pEvent);
SAFE_RELEASE(pEvent);
}
}
break;
case RTCE_WATCHER:
{
IRTCWatcherEvent * pEvent = NULL;
hr = pDisp->QueryInterface( IID_IRTCWatcherEvent,
(void **)&pEvent );
if (SUCCEEDED(hr))
{
OnRTCWatcherEvent(pEvent);
SAFE_RELEASE(pEvent);
} }
break;
}
... .
}
启用和检测出席信息
使用出席信息服务,用户可以跟踪联系人的出席状态、将此状态通知给联系人、通过注册服务器(维护联系人的当前位置信息)调用好友。位置可以是计算机或电话,将来还可以是移动电话、寻呼机或手持设备。
图
1
:包含好友列表和出席状态的示例用户界面
下图显示了在
SIP
服务器中注册客户端并启用出席信息服务的高级步骤。
图
2
:启用配置文件和出席信息服务的步骤
SIP
服务器注册
要启用出席信息服务,需要创建一个
“
配置文件
”
对象。配置文件对象是通过
IRTCClientProvisioning::CreateProfile
方法创建的。要创建配置文件对象,客户端应用程序需要创建一个符合装置架构的
XML
字符串。
XML
架构的属性包括:
·
装置设置
-
配置文件的唯一标识符。
·
用户设置
-
用户的
URI
、领域和登录帐户信息
·
客户端设置
-
有关与通信链路无关的客户端应用程序信息。此信息是可选的。
·
提供商设置
-
有关
Internet
电话服务提供商
(ITSP)
的信息。
·
SIP
服务器设置
-
指定可用的
SIP
服务器、
SIP
服务器角色和服务器支持的会话类型。
要在
SIP
服务器上注册用户,客户端需要创建一个
XML
配置文件字符串,指示
RTC
客户端
API
如何与
SIP
服务器进行通信。创建
XML
字符串后,即可调用
IRTCClientProvisioning::CreateProfile()
方法创建一个配置文件对象。下一步是调用
IRTCClientProvisioning::EnableProfile()
方法在
RTC
服务器上注册用户,并指定配置文件应该在该服务器上注册的会话类型。注册类型可以是:允许传入计算机到计算机的会话
(RTCRF_REGISTER_INVITE_SESSIONS)
、允许传入即时消息会话
(RTCRF_REGISTER_MESSAGE_SESSIONS)
、允许传入观察程序
(RTCRF_REGISTER_PRESENCE)
或允许所有的注册类型
(RTCRF_REGISTER_ALL)
。设置
RTCRF_REGISTER_PRESENCE
或
RTCRF_REGISTER_ALL
将通知注册服务器:客户端接受
SIP SUBSCRIBE
方法。这将允许用户通知其他用户他们的出席状态更改、获取其联系人出席状态更改的通知以及将出席信息添加到观察程序列表的其他人。
观察程序对象在好友列表中提供了用户状态。观察程序可以查询
Presentity
的状态,并得到好友列表中的用户状态更改的通知。观察程序还使用户能够通过
Presentity
的状态中断或允许其他人加入。
示例代码说明了注册用户以及启用出席信息和好友列表的步骤。
HRESULT CAVDConfDlg::DoSIPLogin(BSTR bXMLObj)
{
HRESULT hr;
... .
//
获取 RTC 客户端装置接口
IRTCClientProvisioning * pProv = NULL;
hr = m_pClient->QueryInterface(
IID_IRTCClientProvisioning,
(void **)&pProv);
if (FAILED(hr))
{
//
查询接口失败
return hr;
}
//
从 XML 装置文档创建 RTC
//
配置文件对象
hr = pProv->CreateProfile(bXMLObj, &m_pProfile);
SAFE_FREE_STRING(bXMLObj);
if (FAILED(hr))
{
// CreateProfile
失败
SAFE_RELEASE(pProv);
return hr;
}
//
启用 RTC 配置文件对象
hr = pProv->EnableProfile(m_pProfile, RTCRF_REGISTER_ALL);
SAFE_RELEASE(pProv);
if (FAILED(hr))
{
// EnableProfile
失败
return hr;
}
//
启用出席信息
//
最好在启用配置文件之后立即启用出席信息,
//
这样传入的观察程序就不会丢失。
//
有关函数定义,请参阅以下 EnablePresence() 方法
hr = EnablePresence(TRUE);
if (FAILED(hr))
{
// DoEnablePresence
失败
DoSIPLogoff(); //
有关函数示例,请参阅代码示例 AVDConf2.exe
return hr;
}
return S_OK;
}
启用配置文件后,还应启用出席信息以防传入的观察程序事件丢失。
EnablePresence()
方法还演示了创建好友列表的方法。
HRESULT CAVDConfDlg::EnablePresence(BOOL bEnable)
{
IRTCClientPresence * pPresence = NULL;
HRESULT hr;
//
清理好友列表
ClearBuddyList();
//
获取 RTC 客户端出席信息接口
hr = m_pClient->QueryInterface(
IID_IRTCClientPresence,
(void **)&pPresence);
if (FAILED(hr))
{
// QueryInterface
失败
return hr;
}
//
获取出席信息存储区的位置
VARIANT varStorage;
VariantInit(&varStorage);
varStorage.vt = VT_BSTR;
varStorage.bstrVal = SysAllocString(L"presence.xml");
//
如果禁用出席信息,则将最新的
//
出席信息数据副本保存到 presence.xml 文件。
if (!bEnable && m_bPresenceEnabled)
{
hr = pPresence->Export(varStorage);
if (FAILED(hr))
{
//
导出失败
SAFE_RELEASE(pPresence);
VariantClear(&varStorage);
return hr;
}
}
//
启用出席信息
hr = pPresence->EnablePresence(
bEnable ? VARIANT_TRUE : VARIANT_FALSE, varStorage);
VariantClear(&varStorage);
if (FAILED(hr))
{
// EnablePresence
失败
SAFE_RELEASE(pPresence);
return hr;
}
//
设置启用标志
m_bPresenceEnabled = bEnable;
//
如果禁用出席信息,则清理
//
出席信息数据
if (!bEnable)
{
//
清理好友
IRTCEnumBuddies * pEnumBuddy = NULL;
IRTCBuddy * pBuddy = NULL;
if (FAILED(hr))
{
//
枚举好友失败
SAFE_RELEASE(pPresence);
return hr;
}
//
枚举用户的好友列表。
hr = pPresence->EnumerateBuddies(&pEnumBuddy);
if (FAILED(hr))
{
//
枚举好友失败
SAFE_RELEASE(pPresence);
return hr;
}
while (pEnumBuddy->Next(1, &pBuddy, NULL) == S_OK)
{
pPresence->RemoveBuddy(pBuddy);
SAFE_RELEASE(pBuddy);
}
SAFE_RELEASE(pEnumBuddy);
//
清理观察程序
IRTCEnumWatchers * pEnumWatcher = NULL;
IRTCWatcher * pWatcher = NULL;
hr = pPresence->EnumerateWatchers(&pEnumWatcher);
if (FAILED(hr))
{
//
枚举观察程序失败
SAFE_RELEASE(pPresence);
return hr;
}
while (pEnumWatcher->Next(1, &pWatcher, NULL) == S_OK)
{
pPresence->RemoveWatcher(pWatcher);
SAFE_RELEASE(pWatcher);
}
SAFE_RELEASE(pEnumWatcher);
}
SAFE_RELEASE(pPresence);
return S_OK;
}
请注意
pPresence->EnablePresence()
函数的第二个参数,它需要一个
Variant
型的存储区来保留好友列表以及允许和禁止的观察程序列表。
Variant
存储区可采用文件名
(BSTR)
、
DOMDocument
对象,或者支持
Istream
、
IsequentialStream
或
IpersistStream
的任意对象的形式。
在
RTC
服务器中注册后,应用程序接收
RTCE_REGISTRATION_STATE_CHANGE
事件,指示注册是完成还是失败。
出席信息服务可以在服务器注册您的配置文件之前启用。下图显示了注册前启用出席信息服务的方法。
这可以使用户在注册到服务器之前设置他们的状态。现在,用户可以注册到
SIP
服务器,而对观察程序可以显示为
“
离线
”
,并查看联系人状态。当用户要在不受干扰的情况下监视组的出席状态时,此功能非常有用。
图
3
:注册前启用出席信息的步骤。
请注意,可以在设置出席状态后调用
CreateProfile()
方法。
下面是
XML
架构。
<?xml version="1.0" ?>
<Schema name="ProvisioningSchema.xml"
xmlns="urn:schemas-microsoft-com:xml-data"
xmlns:dt="urn:schemas-microsoft-com:datatypes">
<comments>Schema Version MajorRevisionNumber = 1
MinorRevisionNumber = 0
</comments>
<ElementType name="provision" model="closed">
<AttributeType name="key" required="yes" />
<attribute type="key" />
<AttributeType name="name" required="yes" />
<attribute type="name" />
<AttributeType name="expires" dt:type="dateTime.tz" />
<attribute type="expires" />
<element type="user" minOccurs="1" maxOccurs="1" />
<element type="sipsrv" minOccurs="1" maxOccurs="*" />
<element type="client" minOccurs="0" maxOccurs="1" />
<element type="provider" minOccurs="0" maxOccurs="1" />
</ElementType>
<ElementType name="user" model="closed">
<AttributeType name="uri" required="yes" />
<attribute type="uri" />
<AttributeType name="account" required="no" />
<attribute type="account" />
<AttributeType name="name" required="no" />
<attribute type="name" />
<AttributeType name="password" required="no" />
<attribute type="password" />
<AttributeType name="realm" required="no" />
<attribute type="realm" />
</ElementType>
<ElementType name="client" model="closed">
<AttributeType name="name" required="yes" />
<attribute type="name" />
<AttributeType name="banner"
dt:type="bool"
required="no" />
<attribute type="banner" />
<AttributeType name="updates"
dt:type="bool"
required="no" />
<attribute type="updates" />
<AttributeType name="minver"
dt:type="fixed.14.4"
required="no"/>
<attribute type="minver" />
<AttributeType name="curver"
dt:type="fixed.14.4"
required="no"/>
<attribute type="curver" />
<AttributeType name="updateuri"
dt:type="uri"
required="no" />
<attribute type="updateuri" />
<element type="data" minOccurs="0" maxOccurs="1" />
</ElementType>
<ElementType name="data" model="open" />
<ElementType name="provider" model="closed" content="mixed">
<AttributeType name="name" />
<attribute type="name" />
<AttributeType name="homepage" dt:type="uri" required="no" />
<attribute type="homepage" />
<AttributeType name="helpdesk" dt:type="uri" required="no" />
<attribute type="helpdesk" />
<AttributeType name="personal" dt:type="uri" required="no" />
<attribute type="personal" />
<AttributeType name="calldisplay" dt:type="uri" required="no" />
<attribute type="calldisplay" />
<AttributeType name="idledisplay" dt:type="uri" required="no" />
<attribute type="idledisplay" />
<element type="data" />
</ElementType>
<ElementType name="sipsrv" model="closed" content="mixed">
<AttributeType name="addr" required="yes" />
<attribute type="addr" />
<AttributeType name="protocol"
dt:type="enumeration"
dt:values="TCP UDP TLS"
required="yes" />
<attribute type="protocol" />
<AttributeType name="auth"
dt:type="enumeration"
dt:values="basic digest"
required="no" />
<attribute type="auth" />
<AttributeType name="role"
dt:type="enumeration"
dt:values="proxy registrar"
required="yes" />
<attribute type="role" />
<element type="session" minOccurs="0" maxOccurs="*" />
</ElementType>
<ElementType name="session" model="closed">
<AttributeType name="party"
dt:type="enumeration"
dt:values="first third" />
<attribute type="party" />
<AttributeType name="type"
dt:type="enumeration"
dt:values="pc2pc pc2ph ph2ph im" />
<attribute type="type" />
</ElementType>
</Schema>
创建
XML
配置文件
以下代码演示如何创建
XML
配置文件字符串。
HRESULT CSIPLogin::CreateXMLProvision(LPSTR szURI, LPSTR szSIPIP,
LPSTR szTransport, BSTR *bstrBuf)
{
... .
//
生成 XML 装置文档
wsprintf(szBuf, "<provision key=/"AVDConf_2/" name=/"AVDConf_2/">"
"<user uri=/"%s/" account=/"/" password=/"/" realm=/"%s/" />"
"<sipsrv addr=/"%s/" protocol=/"%s/" %s role=/"proxy/">"
"<session party=/"first/" type=/"pc2pc/" />"
"<session party=/"first/" type=/"pc2ph/" />"
"<session party=/"first/" type=/"im/" />"
"</sipsrv>"
"<sipsrv addr=/"%s/" protocol=/"%s/" %s role=/"registrar/"/>"
"</provision>",
szURIBuf, szRealm,
szSIPIP, szTransport, bBasicAuth ? "auth=/"basic/"" : "",
szSIPIP, szTransport, bBasicAuth ? "auth=/"basic/"" : ""
);
... .
return S_OK;
}
在本文前面显示的示例应用程序和代码片段中,无须包括帐户用户
ID
和密码,因为
SIP
服务器不需要它。但是,如果知道
SIP
服务器需要登录帐户,则可以在配置文件字符串中包括用户
ID
和密码,以便登录到
SIP
服务器。注册需要的信息包括用户的统一资源标识符
(URI)
、领域或域、
RTC
服务器
IP
、验证方法以及用于与服务器通信的传输协议。支持的传输协议包括
TCP
、
UDP
和
TLS
。使用的
SIP
服务器同时支持
“
基本
”
和
“
摘要
”
身份验证。如果是
“
基本
”
身份验证,则传输协议必须是
TLS
(出于安全考虑)。
创建好友列表和观察程序对象
注册配置文件并启用出席信息后,向好友列表添加新用户将非常简单。使用
IRTCClientPresence
接口(提供启用出席的方法)、添加好友、删除好友、枚举观察程序、设置本地出席状态、确定应用程序处理新观察程序中订阅的方法以及设置隐私模式。本示例代码未演示如何实现隐私模式,但值得注意的是,通过该功能用户可以创建一个允许呼叫的用户的离散列表。
//
获取 RTC 客户端出席信息接口
IRTCClientPresence * pPresence = NULL;
hr = m_pClient->QueryInterface(
IID_IRTCClientPresence,
(void **)&pPresence);
if (FAILED(hr))
{
// QueryInterface
失败
char szBuf[256];
wsprintf (szBuf, "Failed to Query Presence Interface/nErr = 0x%x", hr );
MessageBox ( szBuf );
return hr;
}
//
添加好友
IRTCBuddy * pBuddy = NULL;
hr = pPresence->AddBuddy(
bstrURI,
bstrName,
NULL,
VARIANT_TRUE,
NULL,
0,
&pBuddy);
SAFE_RELEASE(pPresence);
if (FAILED(hr))
{
// Addbuddy
失败
SAFE_RELEASE(pBuddy);
char szBuf[256];
wsprintf (szBuf, "Failed to Add Buddy to List./nErr = 0x%x", hr );
MessageBox ( szBuf );
return hr;
}
//
更新好友列表条目
UpdateBuddyList(pBuddy);
SAFE_RELEASE(pBuddy);
如果成功创建了新的好友,
AddBuddy()
方法将返回一个指针,该指针指向新创建的好友列表中的
IRTCBuddy
接口。使用
IRTCBuddy
接口,客户端应用程序可以获取好友的出席
URI
、好友名称、好友状态、永久类型以及与好友出席相关的私人数据。
取消注册和禁用配置文件
调用
IRTCClientProvisioning::DisableProfile()
方法取消注册
SIP
服务器中的用户。在调用
DisableProfile()
方法后,请务必释放配置文件对象。
优化性能
Windows XP SP1
将带有超线程技术的
Pentium 4
处理器看作两个逻辑处理器,因而与单个逻辑
CPU
相比,
Windows XP
可以承担两倍的工作。
图
4
:使用超线程技术(左)和未使用超线程技术(右)运行的示例用户界面
图
4
说明了后台运行磁盘清理实用程序时,使用和不使用超线程技术的
Pentium 4
处理器的
CPU
使用情况对比。
小结
通过实时通信
(RTC) API
,可以生成全功能的会议和协作工具,不管是从计算机到计算机、从计算机到电话还是从电话到电话的通信。在前一篇文章中,我们说明了如何使用即时消息和应用程序共享快速开发音频和视频会议应用程序。在本文中,我们进一步扩展到包括出席信息和好友列表功能,以创建社区和跟踪人员的可用性。结合大量的
RTC API
和
Microsoft
实时通信服务器,您可以生成复杂、有效的协作通信工具,此工具能够增加跨站点团队的工作效率。
使用
RTC API
开发并运行在
Pentium 4
处理器(带有超线程技术和
Windows XP SP1
)中的通信应用程序,在同时执行多项任务时可以实现较高的通信速度和改善的响应性能。