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對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章