风蚀之月

XMPP客户端C++库Gloox简明概要

04 Aug 2014 c++ XMPP Gloox

Gloox是Xmpp协议客户端的Lib,使用起来非常简单。官方自带的sample也非常的实用,功能方面恰到好处的没有提供显示之类的。对于开发xmpp的bot而言是非常的合适。

Xmpp协议虽然不是很流行,但是基于Xmpp的聊天服务还是有很多优点的。使用Xmpp协议来制作bot可以实现很多功能,只要不是网络游戏那样实时大量数据包传递的话,Xmpp是一个很好的避开与网络交互底层接触的解决方案。

Xmpp的协议详细的可以参照RFC文档,不过既然选择使用库来做的话就是懒得和协议上的东西打交道。基本功能上就是个聊天软件,想要详细接触的童鞋可以下载Pidgin尝试一下,GTalk是可以使用Xmpp协议连接的。

Gloox的设计简单易懂,基于事件驱动。官方有个详细的说明页面,这位前辈的博客上贴出了翻译:http://blog.csdn.net/kkaxiao/article/details/6702298。有兴趣详细了解的童鞋可以去看看。

如果只是做简单的开发的话,基本上不会需要很深入的了解。等需要的时候再针对性的找找相关说明即可,gloox的文档里面可以找到大多数的答案。Gloox的使用可以简单的分为两个步骤:登陆账号、处理消息。

#include "gloox/client.h"
#include "gloox/messagesessionhandler.h"
#include "gloox/messageeventhandler.h"
#include "gloox/messageeventfilter.h"
#include "gloox/chatstatehandler.h"
#include "gloox/chatstatefilter.h"
#include "gloox/connectionlistener.h"
#include "gloox/disco.h"
#include "gloox/message.h"
#include "gloox/gloox.h"
#include "gloox/lastactivity.h"
#include "gloox/loghandler.h"
#include "gloox/logsink.h"
#include "gloox/connectiontcpclient.h"
#include "gloox/connectionsocks5proxy.h"
#include "gloox/connectionhttpproxy.h"
#include "gloox/messagehandler.h"
using namespace gloox;

#ifndef _WIN32
# include <unistd.h>
#endif

#include <stdio.h>
#include <string>

#include <cstdio> // [s]print[f]

#if defined( WIN32 ) || defined( _WIN32 )
# include <windows.h>
#endif

class MessageTest : public MessageSessionHandler, ConnectionListener, LogHandler,
	MessageEventHandler, MessageHandler, ChatStateHandler
{
public:
	MessageTest() : m_session( 0 ), m_messageEventFilter( 0 ), m_chatStateFilter( 0 ) {}

	virtual ~MessageTest() {}

	void start()
	{
		// <ID和密码 
		JID jid( "[email protected]/gloox" );	// < Id后面的/gloox是资源名称,并不是必须的,Xmpp支持多个客户端同时接入,可以用作区分。
		j = new Client( jid, "password" );
		
		// <注册事件处理
		j->registerConnectionListener( this );
		j->registerMessageSessionHandler( this, 0 );
		j->disco()->setVersion( "messageTest", GLOOX_VERSION, "Win32" );
		j->disco()->setIdentity( "client", "bot" );
		j->disco()->addFeature( XMLNS_CHAT_STATES );
		StringList ca;
		ca.push_back( "/path/to/cacert.crt" );
		j->setCACerts( ca );
		j->logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this );


		if( j->connect( false ) )
		{
			ConnectionError ce = ConnNoError;
			while( ce == ConnNoError )
			{
				ce = j->recv();
			}
			printf( "ce: %d\n", ce );
		}

		delete( j );
	}

	virtual void onConnect()
	{
		printf( "connected!!!\n" );
	}

	virtual void onDisconnect( ConnectionError e )
	{
		printf( "message_test: disconnected: %d\n", e );
		if( e == ConnAuthenticationFailed )
			printf( "auth failed. reason: %d\n", j->authError() );
	}

	virtual bool onTLSConnect( const CertInfo& info )
	{
		time_t from( info.date_from );
		time_t to( info.date_to );

		printf( "status: %d\nissuer: %s\npeer: %s\nprotocol: %s\nmac: %s\ncipher: %s\ncompression: %s\n"
			"from: %s\nto: %s\n",
			info.status, info.issuer.c_str(), info.server.c_str(),
			info.protocol.c_str(), info.mac.c_str(), info.cipher.c_str(),
			info.compression.c_str(), ctime( &from ), ctime( &to ) );
		return true;
	}

	virtual void handleMessage( const Message& msg, MessageSession * /*session*/ )
	{
		printf( "type: %d, subject: %s, message: %s, thread id: %s\n", msg.subtype(),
			msg.subject().c_str(), msg.body().c_str(), msg.thread().c_str() );

		std::string re = "You said:\n> " + msg.body() + "\nI like that statement.";
		std::string sub;
		if( !msg.subject().empty() )
			sub = "Re: " +  msg.subject();

		m_messageEventFilter->raiseMessageEvent( MessageEventDisplayed );
#if defined( WIN32 ) || defined( _WIN32 )
		Sleep( 1000 );
#else
		sleep( 1 );
#endif
		m_messageEventFilter->raiseMessageEvent( MessageEventComposing );
		m_chatStateFilter->setChatState( ChatStateComposing );
#if defined( WIN32 ) || defined( _WIN32 )
		Sleep( 2000 );
#else
		sleep( 2 );
#endif
		m_session->send( re, sub );

		if( msg.body() == "quit" )
			j->disconnect();
	}

	virtual void handleMessageEvent( const JID& from, MessageEventType event )
	{
		printf( "received event: %d from: %s\n", event, from.full().c_str() );
	}

	virtual void handleChatState( const JID& from, ChatStateType state )
	{
		printf( "received state: %d from: %s\n", state, from.full().c_str() );
	}

	virtual void handleMessageSession( MessageSession *session )
	{
		printf( "got new session\n");
		// <示例程序只处理一个Session,所以在这里释放掉前一个
		j->disposeMessageSession( m_session );
		m_session = session;
		m_session->registerMessageHandler( this );
		m_messageEventFilter = new MessageEventFilter( m_session );
		m_messageEventFilter->registerMessageEventHandler( this );
		m_chatStateFilter = new ChatStateFilter( m_session );
		m_chatStateFilter->registerChatStateHandler( this );
	}

	virtual void handleLog( LogLevel level, LogArea area, const std::string& message )
	{
		printf("log: level: %d, area: %d, %s\n", level, area, message.c_str() );
	}

private:
	Client *j;
	MessageSession *m_session;
	MessageEventFilter *m_messageEventFilter;
	ChatStateFilter *m_chatStateFilter;
};

int main( int /*argc*/, char** /*argv*/ )
{
	MessageTest *r = new MessageTest();
	r->start();
	delete( r );
	getchar();getchar();
	return 0;
}

以上代码来自官方的Sample,主要功能是对接到的文字进行复述。在这个基础上进行修改就可以实现常见的需求了。

在使用gloox的过程中有一点需要注意的是,MessageSession如果保存了其指针的话,不要自行进行Delete操作,而应该交由Client来释放。j->disposeMessageSession( m_session )这样的做法才是正确的。

由于不是做客户端,实际上做的工作还是比较少的。但是刚上手一个库的时候多少会有些不知所措,希望对有用到的童鞋起到帮助。