天天看點

gloox接收消息

通過前面的描述,當和伺服器建立了連接配接之後,就可以和伺服器進行通訊了,今天先說一下接收遠端發送來的文本消息的方式吧。

在接收消息之前,需要明确一件事情,就是一旦你登陸至伺服器之後,随時有可能有人人給你發來消息,是以應該考慮的是你需要做一個死循環,不斷的監聽消息,如果有消息之後,根據消息的不同形式,處理它。當然不用擔心,覺得用個死循環很影響效率,實際上做過socket的就明白了,當你調用接收消息的函數時,是會阻塞的,如果沒有消息,就阻塞,這樣就不會浪費資源了。另外由于阻塞的緣故,是以我們不應該在你的主程序中來做接收消息這樣的事情,而是通過一個線程,來專門做消息的接收。當你連接配接至伺服器成功之後,就可以啟動一個新的線程,在這個線程中利用前面建立好的用戶端執行個體進行消息的接收了。當然,你可以不用阻塞的方式進行接收消息,而是用非阻塞的方式,但是,我覺得做為用戶端而言,用阻塞方式更好一些,對于使用非阻塞,在做伺服器端時很有用,但是對于用戶端而言就顯得沒有必要了,在我的使用中,用戶端都是使用的是阻塞的方式進行接收遠端文本資料的。

前面已經說明,當你連接配接至伺服器之後,你會獲得一個用戶端執行個體對象(Client),如果一直要保持連接配接的話,該執行個體對象應該一直存在。Client這個對象提供了一個recv()的該方法,通過該方法,就可以接收來自遠端的消息了(recv文法預設是阻塞的方式的,如果不需要阻塞,可以通過其參數設定就可以了)。

Gloox在接收遠端發來的消息時,會調用一個接口的實作類,該接口為:MessageSessionHandler,這個接口裡面提供了一個handleMessageSession( MessageSession *session ) = 0方法,當遠端有文本消息發來時,Socket接收資料中會建立一個MessageSession執行個體對象,表明有一個會話産生(一個無端與本地的會話就有一個對應的MessageSession對象),這時如果你注冊了實作MessageSessionHandler接口的實作類,則用戶端Client會自動調用你的實作類中的handleMessageSession(處理消息會話)接口。HandMessageSession的函數表現形式為:

virtual void handleMessageSession( MessageSession *session )

傳入的參數MessageSession裡面含有會話的相關資訊,此時,如果你希望能獲得裡面的相關資訊,則應該注冊一個接口實作類,則程式将會自動調用你的接口實作類,這個接口類名為:MessageHandler,從字面意思可以看出,注冊的這個接口類應該是關于收到的消息的處理接口,也就是說如果你實作了這個接口,并注冊至MessageSession對象中,則你将會得到你所需要的消息,注冊MessageHandler接口實作類的方法是調用MessageSession參數傳入的session->registerMessageHandler( this );方式,示例代碼中的this指的是接收消息類,該類實作了MessageHandler中的handleMessage方法。MessageHandler中的virtual void handleMessage( Stanza *stanza, MessageSession *session = 0 )方法就是你需要實作的,同時可以在該方法内得到你所需要的文本資訊。下面是得到這樣的文本資訊的示例代碼:

std::string msg =  stanza->body();//也即通過stanza參數傳入的變量,通過得到其中的//body就可以得到無端給你發來的文本消息了。

而stanza->from()可以獲得發送給你的遠端的一個完整的JID号,這個很有用,因為你得區分是誰給你發來的消息。當然你可以獲得一些其他的可能對你有用的資訊,我這裡暫且隻舉這兩個通常都要用到的。

如果你得到了所想要的資訊,并且也知道了是誰發給你的,這時你就可以在你的表現層裡面進行進一步的處理了,當然應該是在你實作的MessageHandler中的handleMessage方法裡面,比如你用MFC來做顯示的話,這裡就可以先通過獲得MFC中相關的顯示文本内容的地方的句柄,然後再将獲得的文本資訊寫入即可。

說到接收的文本資訊,通常還有一個問題,就是關于文本字型,顔色等相關的屬性問題,這在XMPP标準協定中并沒有這方面的文檔,主要原因我覺得是因為在文本樣式顯示上的表現标準不相同,比如在WEB的文本顯示和C/S模式下的文本顯示等。不過關于XMPP協定有一個擴充的協定提到了這個問題,這個擴充協定擴充了一個叫做XHTML的擴充,從名字上看,應該是和WEB方面的文本樣式表現差不多。由于這個不是标準使用的,而是建議使用的,在我的了解中,很多都是根據自己的需求,做了自己的擴充,而并沒有參考XHTML進行擴充。

那麼如何進行文本樣式的擴充呢,其實從接收文本消息的那個接口實作類的方法中可以看出,當我們通過stanza->body()時,就可以獲得一個std::string的文本内容,試想一下,如果這個字元串型的文本内容是這樣的表現,它應該是什麼意思呢?

<style size=’12px’ color=’red’>您好,世界</style>

從上面的示例來看,它應試表達的是傳輸的文本内容是“您好,世界”,而這句文本内容大小應該是12象素,顯示的顔色應該是紅色顯示。可是我們通過stanza->body()之後,得到的是除了文本内容之外,還含有其對應的樣式表現。那麼應該怎麼來分離内容與表現呢?

你可以通過字元串關鍵字來截取上面收到的文本資訊,這樣可以獲得文本内容和樣式,不過這個方法并不是很好的方式,雖然可以做得到。gloox内部提供了一個方法來處理這樣的表現,這也是為了考慮自行擴充的原因出現的。使用Parser類就可以實作将字元串内容轉換為面向對象的方式來操作,你就可以通過獲得屬性和值的方式來獲得你想要的表現與内容了。Parser類的構造函數要求是一個繼承了接口實作類的函數,該接口類是:TagHandler,從字面意思可以看出,是要求使用者實作這個處理Tag值的接口,這個接口裡面含有一個抽象方法,是void handleTag( Tag *tag ) ;這樣就可以解析你所收到的那樣的字元串了,Tag辨別的使用,可以參考gloox示例中的test裡面的相關使用方法,這裡隻做一個使用的示例:

Parser pp(this);    //我這裡的this是指的接收消息的類,其中實作了TagHandler接口中的//handleTag方法。

       pp.feed(stanza->body());//當調用這個語句時,就會調用handleTag方法了。

下面是我的handleTag的處理方法實作

void MyRecvMessage::handleTag( Tag *tag )

{

       std::cout<<tag->xml()<<std::endl;

       string fontSize = tag->findAttribute("size");//得到文本大小

       std::cout<<fontSize<<std::endl;    

       string msgtt = tag->cdata();//得到文本内容

}

另外還有一個地方需要注意,就是關于handleMessageSession( MessageSession *session )方法的實作類中,session對象是在gloox内部建立的,需要你将其删除掉,如果你不需要了,删除方法是調用Client對象中的disposeMessageSession方法,即可删除掉session對象了,示例代碼:client->disposeMessageSession( m_session );如果不删除這個,則有記憶體洩露的危險。

那麼這個session是個什麼東東呢?它的意思是當你和遠端,或者遠端和你有消息發送或者接收時,就會建立一個Session對象,用這個對象來存儲互動的資訊,是以當你處理這個對象裡面的資訊之後,就可以将其删除掉了。具體的使用情況可以參考gloox中的示例程式中的相應地方。至于示例程式中的處理消息事件和聊天狀态的句柄的使用,則可以通常檢視相關的源檔案的注釋,可以看出來大概是如何使用和在什麼情況下使用了。

如果遠端繼續給你發消息的話,因為你已經删除了對應的Session對象,是以gloox背景又會建立一個MessageSession對象的,是以對于MessageSession對象的管理是一個值得好好考慮的問題了,同樣對于向遠端發送文本消息也一樣存在着這個MessageSession對象的管理問題。因為一個很明顯的現象,就是你和遠端的某一個JID進行通話,至于是你結束你們之間的通話,還是遠端結束你們之間的通話這個是并不明确的,也是不定時的,比如遠端在某一個時刻給你發了消息,他有可能就隻發送了這一個消息,就不再發送了,這時你可以将MessageSession删除掉(調用Client對象中的disposeMessageSession方法),但是你完全也有可能還會和你繼續通信,是以你可以不用删除這個MessageSession對象。這樣隻要這個MessageSession對象一直保留着,你就可以随時和遠端進行通訊了。但是如果有很多遠端都要和你通訊,而你不删除這個MessageSession對象的話,則記憶體中将會有很多的這樣的對象,而實際中,可能有些對象應該已經過期了,是以應該删除之。

删還是不删,是個問題,因為删的話,可能這個會話還在繼續,而頻繁的建立删除對象,是會帶來一定的開銷的,如果不删的話,則可能這個會話已經不存在了。是以這裡有個政策的東西存在。你得根據你的需求和所考慮的情況來做決策,以更好的管理這些MessageSession對象。