改定履历:
2011-08-19-------------------新建文本文档
2011-08-25--------------------增加XMPP官网链接
2011-09-02-------------------新增对GLOOX1.0API文档Main Page页翻译
个人研究XMPP已经有大约一个星期的时间了,基本过程是先看了三天的RFC,包括RFC3290,RFC3291两个主要协议,然后就是研究GLOOX1.0了。XMPP除了这两个核心协议之外还有很多的XEP扩展协议。另外比较成熟的XMPP协议栈是GLOOX1.0版。前文已经贴出了RFC文档,从这篇文章开始详细介绍GLOOX,由于本人也是边学边记录,有高手飘过时请不吝赐教。
先贴出一下XMPP一些相关资源:
gloox官方网站---这里可以了解gloox的官方信息,开发文档等
gloox1.0API文档---个人将官方的API文档整站下载下来打包,方便本地查看,强烈建议大家详细看一下文档的Main Page页,以对整个gloox的设计有一个大的层面上的理解
RFC3920------XMPP核心协议
RFC3921------即时消息和出席信息
xmpp协议PPT----XMPP PPT文档
先这些吧,以后随时补充
---------------------------------------------------------------------------------------------------------------------
GLOOX协议栈是基于所谓“观察者”设计模式设计的,如果你熟悉“观察者”设计模式或者不熟悉但是在去深入了解GLOOX之前能去看一下“观察者”模式会对学习GLOOX很有好处。其主要思想就是将观察者“注册”到“被观察者”那里,“被观察者”有什么风吹草动,观察者都会知道,而进一步的处理就是在你的程序里实现“观察者”的若干虚函数。
环境搭建的话,本人用的是vs2008,下载下来gloox1.0,新建一个工程把gloox下src目录里所有的.h和.cpp全部加载,再添加一个工程用于对gloox进行测试,将两个工程的生成目录及依赖设置好即可。
----------------------------------------------------------------------------------------------------------------------
补充:博主近日学习GLOOX,重读了API Main Page页,觉得Main Page页对于新手从较高层面上把握GLOOX有很大帮助,且这里往往是新手容易忽略的最直接的一手资料信息来源,于是决定花时间翻译出来。限于本人英文水平,大家可参照原英文文档查看:
Introduction:
GLOOX的设计遵循了所谓的观察者设计模式,含义是一切都是基于事件驱动的。使用GLOOX不如Jabber/XMPP网络有两种方式------做为客户端或组件。C++ XMPP服务器库请参考<http://camaya.net/glooxd>.
Note:
XMPP详细规格说明书(RFC 3290)的11.5小节要求,线路上交换的数据只能是UTF-8编码方式,由于GLOOX不知道输入的数据是哪种编码,所以GLOOX要求输入数据必须是有效的UTF-8编码。
Event Handlers:
GLOOX里最重要的工具就是事件处理器(event hanbdlers),当前,除了有为RFC中定义的基本协议服务的4种事件处理器,还有为XEP实现里的事件和附加功能服务的其它许多事件处理器。另外,一个日志处理器、一个一般标签处理器和一个连接事件处理器都是有效的。
通常这些处理器都是虚接口,你可以继承它们派生出一个类,并且实现一些这些虚函数。然后你可以注册这样的一个子类对象到各自的协议实现里。下面是一个简单的例子:
- class MyClass : public PresenceHandler
- {
- public:
- // reimplemented from PresenceHandler
- virtual void handlePresence(const Presence& presence );
- [...]
- };
- void MyClass::handlePresence( const Presence& presence )
- {
- // extract further information from the Presence object
- }
- class MyClass : public PresenceHandler
- {
- public:
- // reimplemented from PresenceHandler
- virtual void handlePresence( const Presence& presence );
- [...]
- };
- void MyClass::handlePresence( const Presence& presence )
- {
- // extract further information from the Presence object
- }
在某个地方你可以像下面这样做:
- OtherClass::doSomething()
- {
- Client* client = new Client( ... );
- [...]
- MyClass* handler = new MyClass( ... );
- client->registerPresenceHandler( handler );
- }
- OtherClass::doSomething()
- {
- Client* client = new Client( ... );
- [...]
- MyClass* handler = new MyClass( ... );
- client->registerPresenceHandler( handler );
- }
现在,每次接收到一个presence节(非subscription节)时,handlePresence都会被调用,参数是当前节。然后你可以使用Stanza类提供的众多getters(一般来讲是get前缀的方法,此处理解为获取stanza各数据的方法)进一步提取节中的数据以进一步处理。
几乎所以的事件处理器的工作方式都是与这个例子相似的,接下来以使用连接事件处理器(class ConnectionListener)为例,再举一例:
- class MyClass : public ConnectionListener
- {
- public:
- virtual void onConnect();
- virtual bool onTLSConnect( ... );
- };
- void MyClass::onConnect()
- {
- // do something when the connection is established
- }
- bool MyClass::onTLSConnect( const CertInfo& info )
- {
- // decide whether you trust the certificate, examine the CertInfo structure
- return true; // if you trust it, otherwise return false
- }
- class MyClass : public ConnectionListener
- {
- public:
- virtual void onConnect();
- virtual bool onTLSConnect( ... );
- };
- void MyClass::onConnect()
- {
- // do something when the connection is established
- }
- bool MyClass::onTLSConnect( const CertInfo& info )
- {
- // decide whether you trust the certificate, examine the CertInfo structure
- return true; // if you trust it, otherwise return false
- }
Note:
ConnectionListener是一个比较奇特的接口。你必须重新实现ConnectionListener::onTLSConnect()这个接口,如果你想成功连接一个启用了TLS/SSL服务器的话。尽管GLOOX试图检查服务器的证书,但不会自动的信任该服务器。必须由客户端程序员或用户来决定是否信任此服务器。是否信任就由onTLSConnect()的返回值来表示,FLASE意味着你不信任该服务器,结果就是与服务器的连接中断。
先去吃饭了,回来待续……
Components
Jabber/XMPP网络中的一个组件是加载到服务器上的,它运行在实际的服务软件之外,但它有相似的权限,组件使用XEP-0114中描述的协议去连接和验证一个服务器.类Component 支持该协议并且可以用于创建一个新的Jabber component.简单的例子如下:
- Component* comp = new Component( ... );
- comp->connect();
- Component* comp = new Component( ... );
- comp->connect();
Clients
一个客户端可以是终端用户的聊天客户端,一个机器人或者是一个没有和特殊服务器绑定在一起的简单实体。类Client实现了连接一个XMPP 服务器所需要的功能。一个例子如下:
- class MyClass : public ConnectionListener, PresenceHandler
- {
- public:
- void doSomething();
- virtual void handlePresence( ... );
- virtual void onConnect();
- virtual bool onTLSConnect( const CertInfo& info );
- };
- void MyClass::doSomething()
- {
- JID jid( "jid@server/resource" );
- Client* client = new Client( jid, "password" );
- client->registerConnectionListener( this );
- client->registerPresenceHandler( this );
- client->connect();
- }
- void MyClass::onConnect()
- {
- // connection established, auth done (see API docs for exceptions)
- }
- bool MyClass::onTLSConnect( const CertInfo& info )
- {
- // examine certificate info
- }
- void MyClass::handlePresence( Presence* presence )
- {
- // presence info
- }
- class MyClass : public ConnectionListener, PresenceHandler
- {
- public:
- void doSomething();
- virtual void handlePresence( ... );
- virtual void onConnect();
- virtual bool onTLSConnect( const CertInfo& info );
- };
- void MyClass::doSomething()
- {
- JID jid( "jid@server/resource" );
- Client* client = new Client( jid, "password" );
- client->registerConnectionListener( this );
- client->registerPresenceHandler( this );
- client->connect();
- }
- void MyClass::onConnect()
- {
- // connection established, auth done (see API docs for exceptions)
- }
- bool MyClass::onTLSConnect( const CertInfo& info )
- {
- // examine certificate info
- }
- void MyClass::handlePresence( Presence* presence )
- {
- // presence info
- }
GLOOX官方并不支持5223上的连接方式。例如:在XML串先于SSL加密之前被发送,因为这是历史遗留的方法况且不符合标准的XMPP协议。然而,GLOOX提供了一个ConnectionTlS类,做为附加功能,允许你去绑定一个这样的连接。
默认情况下,Client::connect()是阻塞的,直到连接终止(Client::disconnect()被调用,或者服务器终止连接)。
阻塞和非阻塞连接
对于某些种类的机器人来说阻塞式的连接(缺省的行为)是非常理想的方式。几乎所有的机器人都是对来自于服务器的事件做出响应。然而,对某些终端用户或带有GUI应用的客户端来说就不那么完美了。
这些情况下就需要用到非阻塞式的连接了。如果调用ClinetBase::connect(false),函数将会在绑定连接后立即返回。接下来程序员就有责任初始化一些从SOCKET接收到的数据。
一种最简单的方法是以一个超时值(微秒)做为参数周期性的调用ClientBase::recv(),缺省值-1意味着调用是阻塞的,直到有数据到达,对数据的解析也是自动的。
作为周期性地轮询一种替换办法,你可以获取连接的原始文件描述符。你可以在该文件描述符上使用select()并且当select上的数据有效时使用ClientBase::recv()。你可能直接从文件描述符得不到任何数据,也没有办法为解析器提供数据。
为了得到文件描述符,你必须手动设置一个连接类(ConnectionTCPClient),就像这样:
- Client* client = new Client( ... );
- ConnectionTCPClient* conn = new ConnectionTCPClient( client, client->logInstance(), server, port );
- client->setConnectionImpl( conn );
- client->connect( false );
- int sock = conn->socket();
- [...]
- Client* client = new Client( ... );
- ConnectionTCPClient* conn = new ConnectionTCPClient( client, client->logInstance(), server, port );
- client->setConnectionImpl( conn );
- client->connect( false );
- int sock = conn->socket();
- [...]
有可能像下面这样获取文件描述符:
- Client* client = new Client( ... );
- client->connect( false );
- int sock = static_cast<ConnectionTCPClient*>( client->connectionImpl() )->socket();
- [...]
- Client* client = new Client( ... );
- client->connect( false );
- int sock = static_cast<ConnectionTCPClient*>( client->connectionImpl() )->socket();
- [...]
Note
在0.9以后的版本里这已经发生了改变,ClientBase::fileDescriptor()不在可用了。
Roster Management
RFC3921定义了如何管理一个人的联系列表(花名册)。在GLOOX中,类RosterManager实现了这个功能。一组简单函数有效地实现了订阅和取消订阅实体的出席。也可以在没有实际订阅此联系人出席的情况,把他加入到自己的花名册中。另外,接口RosterListener对应于各种花名册相关的事件提供了一组回调函数。
如果你依上所述创建了一个客户端对象,那么你可以获取一个RosterManager对象。Client::rosterManager()返回一个该类对象的指针。
Privacy Lists
同样地,“隐私列表”也定义在RFC3921中,一个隐秘列表可以明确地禁止或允许 从联系人接收或发给联系人数据节,区分不同的联系人。你可以自己定义基于JID,节类型等的规则。类PrivacyManager 和类PrivacyListHandler虚接口在隐私列表的处理上比较灵活。
- PrivacyManager* p = new PrivacyManager( ... );
- [...]
- PrivacyListHandler::PrivacyList list;
- PrivacyItem item( PrivacyItem::TypeJid, PrivacyItem::ActionDeny,
- PrivacyItem::PacketMessage, "me@there.com" );
- list.push_back( item );
- PrivacyItem item2( PrivacyItem::TypeJid, PrivacyItem::ActionAllow,
- PrivacyItem::PacketIq, "me@example.org" );
- list.push_back( item2 );
- p->store( "myList", list );
- PrivacyManager* p = new PrivacyManager( ... );
- [...]
- PrivacyListHandler::PrivacyList list;
- PrivacyItem item( PrivacyItem::TypeJid, PrivacyItem::ActionDeny,
- PrivacyItem::PacketMessage, "me@there.com" );
- list.push_back( item );
- PrivacyItem item2( PrivacyItem::TypeJid, PrivacyItem::ActionAllow,
- PrivacyItem::PacketIq, "me@example.org" );
- list.push_back( item2 );
- p->store( "myList", list );
GLOOX支持XEP-0078定义的旧样式的基于IQ节的鉴权机制,也支持若干SASL机制。可以参考Client类文档说明以获取更多信息。
Sending and Receiving of Chat Messages
对于消息传输推荐使用MessageSession接口。它处理消息的发送和接收,也处理包括消息事件和聊天状态(例如“打字中……”)等。查看MessageSession类文档以获取更多细节。
Protocol Extensions (XEPs)
XMPP标准基金会发布了许多核心协议的扩展,称为XMPP Extension Protocols(XEPs)(XMPP扩展协议)。一些扩展协议已经在GLOOX中得到实现:
XEP-0004 Data Forms
XEP-0012 Last Activity
XEP-0013 Flexible Offline Message Retrieval
XEP-0022 Message Events (see MessageSession for examples)
XEP-0027 Current Jabber OpenPGP Usage (see GPGSigned and GPGEncrypted )
XEP-0030 Service Discovery
XEP-0045 Multi-User Chat
XEP-0047 Used with File Transfer
XEP-0048 Bookmark Storage
XEP-0049 Private XML Storage
XEP-0050 Ad-hoc Commands
XEP-0054 vcard-temp
XEP-0060 Publish-Subscribe
XEP-0065 SOCKS5 Bytestreams , used with File Transfer and HTTP and SOCKS5 Proxy support
XEP-0066 Out of Band Data , also used with File Transfer
XEP-0077 In-Band Registration
XEP-0078 Non-SASL Authentication (automatically used if the server does not support SASL)
XEP-0079 Advanced Message Processing
XEP-0083 Nested Roster Groups (automatically used if supported by the server. see RosterManager )
XEP-0085 Chat State Notifications (see MessageSession for examples)
XEP-0091 Delayed Delivery (old spec)
XEP-0092 Software Version (integrated into Service Discovery )
XEP-0095 Stream Initiation , used with File Transfer
XEP-0096 File Transfer
XEP-0106 JID Escaping
XEP-0114 Jabber Component Protocol
XEP-0115 Entity Capabilities (used automatically internally)
XEP-0124 Bidirectional-streams Over Synchronous HTTP (BOSH)
XEP-0131 Stanza Headers and Internet Metadata
XEP-0138 Stream Compression (used automatically if gloox is compiled with zlib and if the server supports it)
XEP-0145 Annotations
XEP-0153 vCard-based Avatars
XEP-0172 User Nickname
XEP-0184 Message Receipts
XEP-0199 XMPP Ping
XEP-0203 Delayed Delivery (new spec)
XEP-0206 see BOSH
XEP-0224 Attention
XEP-0256 Last Activity in Presence
更进一步的扩展可以用类StanzaExtensions很容易的实现。
File Transfer
对于文件传输,GLOOX实现了XEP-0095(流初始化协议)和XEP-0096(文件传输)协议的信号机制;为传输实现了XEP-0065(SOCKS5 Bytestreams 流字节)和XEP-0047(带内字节流)。参考 SIProfileFT类
HTTP and SOCKS5 Proxy support
gloox 有能力穿过http及SOCKS5代理,即便是连锁代理。参考ConnectionHTTPProxy类 和 ConnectionSocks5Proxy类.