改定履历:

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一些相关资源:

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实现里的事件和附加功能服务的其它许多事件处理器。另外,一个日志处理器、一个一般标签处理器和一个连接事件处理器都是有效的。

通常这些处理器都是虚接口,你可以继承它们派生出一个类,并且实现一些这些虚函数。然后你可以注册这样的一个子类对象到各自的协议实现里。下面是一个简单的例子:

[cpp]  view plain copy
  1. class MyClass : public PresenceHandler 
  2.    public
  3.      // reimplemented from PresenceHandler 
  4.      virtual void handlePresence(const Presence& presence ); 
  5.  
  6.    [...] 
  7. }; 
  8.  
  9. void MyClass::handlePresence( const Presence& presence ) 
  10.    // extract further information from the Presence object 
[cpp]  view plain copy
  1. class MyClass : public PresenceHandler  
  2.  {  
  3.    public:  
  4.      // reimplemented from PresenceHandler  
  5.      virtual void handlePresence( const Presence& presence );  
  6.   
  7.    [...]  
  8.  };  
  9.   
  10.  void MyClass::handlePresence( const Presence& presence )  
  11.  {  
  12.    // extract further information from the Presence object  
  13.  }  

在某个地方你可以像下面这样做:
[cpp]  view plain copy
  1. OtherClass::doSomething() 
  2.   Client* client = new Client( ... ); 
  3.   [...] 
  4.   MyClass* handler = new MyClass( ... ); 
  5.   client->registerPresenceHandler( handler ); 
[cpp]  view plain copy
  1. OtherClass::doSomething()  
  2. {  
  3.   Client* client = new Client( ... );  
  4.   [...]  
  5.   MyClass* handler = new MyClass( ... );  
  6.   client->registerPresenceHandler( handler );  
  7. }  
然后你可以使用Stanza类提供的众多getters(即get前缀的成员函数)进一步提取节中的数据
现在,每次接收到一个presence节(非subscription节)时,handlePresence都会被调用,参数是当前节。然后你可以使用Stanza类提供的众多getters(一般来讲是get前缀的方法,此处理解为获取stanza各数据的方法)进一步提取节中的数据以进一步处理。

几乎所以的事件处理器的工作方式都是与这个例子相似的,接下来以使用连接事件处理器(class ConnectionListener)为例,再举一例:

[cpp]  view plain copy
  1. class MyClass : public ConnectionListener 
  2.    public
  3.      virtual void onConnect(); 
  4.  
  5.      virtual bool onTLSConnect( ... ); 
  6. }; 
  7.  
  8. void MyClass::onConnect() 
  9.    // do something when the connection is established 
  10.  
  11. bool MyClass::onTLSConnect( const CertInfo& info ) 
  12.    // decide whether you trust the certificate, examine the CertInfo structure 
  13.    return true// if you trust it, otherwise return false 
[cpp]  view plain copy
  1. class MyClass : public ConnectionListener  
  2.  {  
  3.    public:  
  4.      virtual void onConnect();  
  5.   
  6.      virtual bool onTLSConnect( ... );  
  7.  };  
  8.   
  9.  void MyClass::onConnect()  
  10.  {  
  11.    // do something when the connection is established  
  12.  }  
  13.   
  14.  bool MyClass::onTLSConnect( const CertInfo& info )  
  15.  {  
  16.    // decide whether you trust the certificate, examine the CertInfo structure  
  17.    return true// if you trust it, otherwise return false  
  18.  }  

Note:

ConnectionListener是一个比较奇特的接口。你必须重新实现ConnectionListener::onTLSConnect()这个接口,如果你想成功连接一个启用了TLS/SSL服务器的话。尽管GLOOX试图检查服务器的证书,但不会自动的信任该服务器。必须由客户端程序员或用户来决定是否信任此服务器。是否信任就由onTLSConnect()的返回值来表示,FLASE意味着你不信任该服务器,结果就是与服务器的连接中断。

先去吃饭了,回来待续……

Components

Jabber/XMPP网络中的一个组件是加载到服务器上的,它运行在实际的服务软件之外,但它有相似的权限,组件使用XEP-0114中描述的协议去连接和验证一个服务器.类Component 支持该协议并且可以用于创建一个新的Jabber component.简单的例子如下:

[cpp]  view plain copy
  1. Component* comp = new Component( ... ); 
  2. comp->connect(); 
[cpp]  view plain copy
  1. Component* comp = new Component( ... );  
  2. comp->connect();  

Clients

一个客户端可以是终端用户的聊天客户端,一个机器人或者是一个没有和特殊服务器绑定在一起的简单实体。类Client实现了连接一个XMPP 服务器所需要的功能。一个例子如下:

[cpp]  view plain copy
  1. class MyClass : public ConnectionListener, PresenceHandler 
  2. public
  3.      void doSomething(); 
  4.  
  5.      virtual void handlePresence( ... ); 
  6.  
  7.      virtual void onConnect(); 
  8.  
  9.      virtual bool onTLSConnect( const CertInfo& info ); 
  10. }; 
  11.  
  12. void MyClass::doSomething() 
  13.    JID jid( "jid@server/resource" ); 
  14.    Client* client = new Client( jid, "password" ); 
  15.    client->registerConnectionListener( this ); 
  16.    client->registerPresenceHandler( this ); 
  17.    client->connect(); 
  18.  
  19. void MyClass::onConnect() 
  20.    // connection established, auth done (see API docs for exceptions) 
  21.  
  22. bool MyClass::onTLSConnect( const CertInfo& info ) 
  23.    // examine certificate info 
  24.  
  25. void MyClass::handlePresence( Presence* presence ) 
  26.    // presence info 
[cpp]  view plain copy
  1. class MyClass : public ConnectionListener, PresenceHandler  
  2. {  
  3. public:  
  4.      void doSomething();  
  5.   
  6.      virtual void handlePresence( ... );  
  7.   
  8.      virtual void onConnect();  
  9.   
  10.      virtual bool onTLSConnect( const CertInfo& info );  
  11. };  
  12.   
  13. void MyClass::doSomething()  
  14. {  
  15.    JID jid( "jid@server/resource" );  
  16.    Client* client = new Client( jid, "password" );  
  17.    client->registerConnectionListener( this );  
  18.    client->registerPresenceHandler( this );  
  19.    client->connect();  
  20. }  
  21.   
  22. void MyClass::onConnect()  
  23. {  
  24.    // connection established, auth done (see API docs for exceptions)  
  25. }  
  26.   
  27. bool MyClass::onTLSConnect( const CertInfo& info )  
  28. {  
  29.    // examine certificate info  
  30. }  
  31.   
  32. void MyClass::handlePresence( Presence* presence )  
  33. {  
  34.    // presence info  
  35. }  
Note

GLOOX官方并不支持5223上的连接方式。例如:在XML串先于SSL加密之前被发送,因为这是历史遗留的方法况且不符合标准的XMPP协议。然而,GLOOX提供了一个ConnectionTlS类,做为附加功能,允许你去绑定一个这样的连接。

默认情况下,Client::connect()是阻塞的,直到连接终止(Client::disconnect()被调用,或者服务器终止连接)。


阻塞和非阻塞连接

对于某些种类的机器人来说阻塞式的连接(缺省的行为)是非常理想的方式。几乎所有的机器人都是对来自于服务器的事件做出响应。然而,对某些终端用户或带有GUI应用的客户端来说就不那么完美了。

这些情况下就需要用到非阻塞式的连接了。如果调用ClinetBase::connect(false),函数将会在绑定连接后立即返回。接下来程序员就有责任初始化一些从SOCKET接收到的数据。

一种最简单的方法是以一个超时值(微秒)做为参数周期性的调用ClientBase::recv(),缺省值-1意味着调用是阻塞的,直到有数据到达,对数据的解析也是自动的。

作为周期性地轮询一种替换办法,你可以获取连接的原始文件描述符。你可以在该文件描述符上使用select()并且当select上的数据有效时使用ClientBase::recv()。你可能直接从文件描述符得不到任何数据,也没有办法为解析器提供数据。

为了得到文件描述符,你必须手动设置一个连接类(ConnectionTCPClient),就像这样:

[cpp]  view plain copy
  1. Client* client = new Client( ... ); 
  2. ConnectionTCPClient* conn = new ConnectionTCPClient( client, client->logInstance(), server, port ); 
  3. client->setConnectionImpl( conn ); 
  4.  
  5. client->connect( false ); 
  6. int sock = conn->socket(); 
  7.  
  8. [...] 
[cpp]  view plain copy
  1. Client* client = new Client( ... );  
  2. ConnectionTCPClient* conn = new ConnectionTCPClient( client, client->logInstance(), server, port );  
  3. client->setConnectionImpl( conn );  
  4.   
  5. client->connect( false );  
  6. int sock = conn->socket();  
  7.   
  8. [...]  

有可能像下面这样获取文件描述符:
[cpp]  view plain copy
  1. Client* client = new Client( ... ); 
  2. client->connect( false ); 
  3. int sock = static_cast<ConnectionTCPClient*>( client->connectionImpl() )->socket(); 
  4.  
  5. [...] 
[cpp]  view plain copy
  1. Client* client = new Client( ... );  
  2. client->connect( false );  
  3. int sock = static_cast<ConnectionTCPClient*>( client->connectionImpl() )->socket();  
  4.   
  5. [...]  
显然,只要你没有使用setConnectionImpl()设置一个不同的连接类型就会工作。

Note

在0.9以后的版本里这已经发生了改变,ClientBase::fileDescriptor()不在可用了。

Roster Management

RFC3921定义了如何管理一个人的联系列表(花名册)。在GLOOX中,类RosterManager实现了这个功能。一组简单函数有效地实现了订阅和取消订阅实体的出席。也可以在没有实际订阅此联系人出席的情况,把他加入到自己的花名册中。另外,接口RosterListener对应于各种花名册相关的事件提供了一组回调函数。

如果你依上所述创建了一个客户端对象,那么你可以获取一个RosterManager对象。Client::rosterManager()返回一个该类对象的指针。

Privacy Lists

同样地,“隐私列表”也定义在RFC3921中,一个隐秘列表可以明确地禁止或允许 从联系人接收或发给联系人数据节,区分不同的联系人。你可以自己定义基于JID,节类型等的规则。类PrivacyManager 和类PrivacyListHandler虚接口在隐私列表的处理上比较灵活。

[cpp]  view plain copy
  1. PrivacyManager* p = new PrivacyManager( ... ); 
  2. [...] 
  3. PrivacyListHandler::PrivacyList list; 
  4. PrivacyItem item( PrivacyItem::TypeJid, PrivacyItem::ActionDeny, 
  5.                   PrivacyItem::PacketMessage, "me@there.com" ); 
  6. list.push_back( item ); 
  7.  
  8. PrivacyItem item2( PrivacyItem::TypeJid, PrivacyItem::ActionAllow, 
  9.                    PrivacyItem::PacketIq, "me@example.org" ); 
  10. list.push_back( item2 ); 
  11.  
  12. p->store( "myList", list ); 
[cpp]  view plain copy
  1. PrivacyManager* p = new PrivacyManager( ... );  
  2. [...]  
  3. PrivacyListHandler::PrivacyList list;  
  4. PrivacyItem item( PrivacyItem::TypeJid, PrivacyItem::ActionDeny,  
  5.                   PrivacyItem::PacketMessage, "me@there.com" );  
  6. list.push_back( item );  
  7.   
  8. PrivacyItem item2( PrivacyItem::TypeJid, PrivacyItem::ActionAllow,  
  9.                    PrivacyItem::PacketIq, "me@example.org" );  
  10. list.push_back( item2 );  
  11.   
  12. p->store( "myList", list );  
Authentication

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类.