深度解析 Qt 內(nèi)部進程通信機制
Qt 內(nèi)部進程通信機制是本文要介紹的內(nèi)容,Qt 作為一種跨平臺的基于 C++ 的 GUI 系統(tǒng),能夠提供給用戶構(gòu)造圖形用戶界面的強大功能。自從 1996 年 Qt 被 Trolltech 公司發(fā)布以來,該系統(tǒng)成為世界上很多成功的圖形用戶應(yīng)用所使用的主要系統(tǒng)。更為重要的是,Linux 操作系統(tǒng)的桌面環(huán)境系統(tǒng) KDE 也是基于 Qt 構(gòu)造的。目前,Qt 已經(jīng)提供了對包括 MS/Windows、Unix/X11 和嵌入式平臺的支持,得到了越來越廣泛的應(yīng)用。
在 Qt 系統(tǒng)中,不僅有著構(gòu)造完善的系統(tǒng)結(jié)構(gòu),而且為了滿足用戶對編寫圖形用戶界面應(yīng)用的種種需求,它還創(chuàng)建了許多新的系統(tǒng)機制,其中 Qt 所特有的內(nèi)部進程通信機制尤其值得一提。 本文分析了基于 QT 的應(yīng)用進程之間通信常用的三種機制:QCOP 協(xié)議,Signal-Slot 機制和 FIFO 機制。給出了各自的使用方法,并指出了各自的使用場合。
1、 QCOP協(xié)議
QCOP 是 Qt 內(nèi)部的一種通信協(xié)議,這種協(xié)議用于不同的客戶之間在同一地址空間內(nèi)部或者不同的進程之間的通信。目前,這種機制還只在 Qt 的嵌入式版本中提供。
為實現(xiàn)這種通信機制,Qt 中包括了由 QObject 類繼承而來的 QCopChannel 類,該類提供了諸如 send()、isRegistered() 等靜態(tài)函數(shù),它們可以在脫離對象的情況下使用。為了在 channel 中接收通信數(shù)據(jù),用戶需要構(gòu)造一個 QCopChannel 的子類并提供 receive() 函數(shù)的重載函數(shù),或者利用 connect() 函數(shù)與接收到的信號相聯(lián)系。
值得一提的是,在 Qt 系統(tǒng)中,只提供了 QCOP 協(xié)議機制和用于接收消息的類,而如何發(fā)送消息則沒有提供相應(yīng)的類供用戶使用。
在基于 Qt 的桌面系統(tǒng) Qtopia(QPE)中,則提供了相應(yīng)的發(fā)送類:QCopEnvelope。用戶可以通過該類利用 channel 向其他進程發(fā)送消息。該類將通過 QCopChannel 發(fā)送 QCop 消息的過程進行了封裝,用戶只需要調(diào)用該類中的相關(guān)函數(shù)就可以方便地實現(xiàn)進程之間的通信過程。一方面,QCop 消息的發(fā)送要利用 QCopEnvelope 類,另一方面,接收消息則是通過與一個 QCopChannel 相關(guān)聯(lián)。
在發(fā)送消息時,將利用如下的協(xié)議機制:
- QCopEnvelope e(channelname, messagename);
對于需要攜帶參數(shù)的消息,必須使用"<<()"運算符將參數(shù)添加到envelope中。
- e << parameter1 << parameter2 << ...;
對于不帶參數(shù)的消息,只需要利用:
- QCopEnvelope e(channelname, messagename);
在Qtopia中,所有的channels名都以"QPE/"開始,而messagename則是一個函數(shù)的標(biāo)識符。
在接收消息時,通常只需要利用在應(yīng)用程序中預(yù)先定義好的QPE/Application/{appname}管道,當(dāng)然,也可以根據(jù)需要自己定義管道,并將其與一個slot函數(shù)相關(guān)聯(lián):
- myChannel = new QCopChannel( "QPE/FooBar", this );
- connect( myChannel, SIGNAL(received(const QCString &, const QByteArray &)),
- this, SLOT(fooBarMessage( const QCString &, const QByteArray &)) );
下面將具體的通信過程舉例如下:
在需要接收消息的類(如Window1)中定義管道:
- QCopChannel *doChannel = new QCopChannel("QPE/Do", this);
- connect(doChannel, SIGNAL(received(const QCString &, const QByteArray &)),
- this, SLOT(doMessage(const QCString &, const QByteArray &)));
同時,需要在該類中定義相應(yīng)的消息處理函數(shù)doMessage,
- void Window1::doMessage(const QCString &msg, const QByteArray &args)
- {
- QDataStream stream(args, IO_ReadOnly);
- if(msg == "Message1(QString)")
- {
- QString text;
- stream >> text;
- button->setText(text);
- }
- else if(msg == "Message2()")
- {
- close();
- }
- }
其中的Message1(QString)和Message2(QString)都是用戶自己定義的消息,該函數(shù)中分別對這些消息進行了相應(yīng)的處理。在該例中當(dāng)收到帶有參數(shù)的Message1消息時,將該字符串參數(shù)stream顯示在按鈕button上;當(dāng)收到Message2消息時,將執(zhí)行關(guān)閉Window1窗口的動作,當(dāng)然用戶可以根據(jù)需要自行編寫相應(yīng)的處理過程。
另一方面,在類Class2中需要發(fā)出消息的函數(shù)function中利用QCopEnvelope發(fā)送消息:
- void Class2::function()
- { QCopEnvelope e("QPE/Do", "Message1(QString)");
- e << param; }
這里發(fā)出了Message1消息,并將需要攜帶的參數(shù)param發(fā)送到管道中。
通過這樣的過程,用戶可以很方便地實現(xiàn)不同對象、不同進程之間通信過程,而且可以根據(jù)需要在通信過程中任意傳遞參數(shù)。
#p#
2、 信號-槽(Signal-Slot)機制
在Qt中,有一種用于對象之間的通信:信號-槽機制,這種機制是Qt的核心機制,也是它區(qū)別于其他GUI工具的最主要的特征。在大多數(shù)GUI工具中,通常為可能觸發(fā)的每種行為定義一個回調(diào)函數(shù),這個回調(diào)函數(shù)是一個指向函數(shù)的指針。在Qt中,信號-槽機制取代了這種繁雜的函數(shù)指針,能夠?qū)崿F(xiàn)同樣的功能。信號-槽機制可以攜帶任意類型、任意數(shù)量的參數(shù),而且完全是安全的,不會引起系統(tǒng)的崩潰。
所有由QObject類繼承而來的類,或者是它的一個子類,都可以包括信號-槽機制。信號通常是當(dāng)對象改變他們的狀態(tài)時發(fā)出的,這就是一個對象在需要與其他對象通信時所需要做的一切,它并不知道是否有其他對象在另一端接收該信號。從這個意義上來說,這種機制實現(xiàn)了真正的信息封裝,確保了對象可以被當(dāng)作一個獨立的軟件構(gòu)件來使用。
而槽可以被用于接收信號,它們通常是類中的成員函數(shù)。一個槽并不知曉是否有一個信號與自己相聯(lián)系,同樣,包含有槽函數(shù)的對象也對通信機制一無所知,它們也可以作為一個獨立的軟件構(gòu)件。
用戶可以按照需要將許多信號與一個單獨的槽函數(shù)相聯(lián)系,一個信號也可以按需要被聯(lián)系到很多不同的槽函數(shù)。甚至還可以將一個信號直接與另一個信號相聯(lián)系,這樣當(dāng)***個信號被發(fā)出時立刻發(fā)出第二個信號。
這樣,信號-槽相結(jié)合就產(chǎn)生了一種功能強大的編程機制。
例如:
- button = new QAction(tr("button"), QIconSet(QPixmap("button.png")), 0, 0, this);
- connect(button, SIGNAL(activated()), this, SLOT(slotButton()));
程序中定義了一個按鈕,并利用connect()函數(shù)將該按鈕button的activated()信號與slotButton()函數(shù)相關(guān)聯(lián),當(dāng)用戶觸發(fā)按鈕時,就會執(zhí)行相應(yīng)的槽函數(shù)。當(dāng)然,這里的信號是QAction類中預(yù)先定義好的信號,用戶在使用該機制時,可以根據(jù)需要自行定義信號,同時在適當(dāng)?shù)臅r候利用emit語句發(fā)出該信號。另外,在信號和相應(yīng)的槽函數(shù)之間還可以傳遞任意參數(shù),如:
- emit signal(parameter);
3、 FIFO機制
當(dāng)然,除了 Qt 內(nèi)部所特有的通信機制之外,一般操作系統(tǒng)中常用的進程間通信機制同樣可以用于 Qt 系統(tǒng)內(nèi)部不同進程之間的通信。如消息隊列、共享內(nèi)存、信號量、管道等機制,其中有些機制,如信號量,在 Qt 中重新進行了封裝;有些機制則可以直接調(diào)用操作系統(tǒng)的系統(tǒng)調(diào)用來實現(xiàn)。這里,有名管道是一種簡單實用的通信機制,用戶在對Qt內(nèi)部機制
不甚了解的情況下,同樣可以使用這種方法實現(xiàn)對象進程之間的通信。下面就對利用這種機制實現(xiàn)Qt內(nèi)部進程之間的通信過程進行介紹。
首先,需要創(chuàng)建 FIFO,這個過程類似于創(chuàng)建文件,在系統(tǒng)中可以利用 mkfifo 命令來創(chuàng)建,這樣就可以用 open 函數(shù)打開它,同時,一般的文件 I/O函數(shù)(close、read、write)都可以用于 FIFO。
在基于 Qt 的應(yīng)用中,有很多應(yīng)用采用了一種客戶機-服務(wù)器模式,這時就可以利用 FIFO 在客戶機和服務(wù)器之間傳遞數(shù)據(jù)。例如,有一個服務(wù)器,它負(fù)責(zé)接收底層程序發(fā)來的消息,同時,它與很多客戶機有關(guān),服務(wù)器需要將收到的不同消息發(fā)送到不同的客戶機,而每個客戶機也有請求需要發(fā)給服務(wù)器,進而發(fā)給底層程序。
下面是服務(wù)器端的程序示例:(架設(shè)已有客戶端進程為讀而打開/dev/fifoclient1和/dev/fifoclient1)
- fd = open("/dev/fifoserver", O_NONBLOCK|O_RDONLY);
- file = fdopen(fd, "r");
- ret = fgets(buf, MAX_LINE, file );
- if(buf[0] == '0')
- {
- QFile fd_file("/dev/fifoclient1");
- QString temp(buf);
- if(fd_file.open(IO_WriteOnly|IO_Append)) {
- QTextStream t(&fd_file);
- t<< temp;
- fd_file.close();
- }
- else if(buf[0] == '1')
- {
- QFile fd_file("/dev/fifoclient2");
- QString temp(buf);
- if(fd_file.open(IO_WriteOnly|IO_Append)) {
- QTextStream t(&fd_file);
- t<< temp;
- fd_file.close();
- }
在該程序中,服務(wù)器接收底層發(fā)來的信息(這里假設(shè)也是由 FIFO 管道傳來),然后根據(jù)收到的信息內(nèi)容,如***個字節(jié)的內(nèi)容,將信息發(fā)到不同客戶端的管道中,實現(xiàn)對信息的正確分發(fā)。
客戶端程序示例如下:(假設(shè)服務(wù)器端已經(jīng)為讀而打開 /dev/fifo 管道)
- QFile out_file("/dev/fifo");
- if(out_file.open(IO_WriteOnly|IO_Append)) {
- QTextStream t(&out_file);
- t << text << "\n"; }
當(dāng)任意一個客戶端需要向服務(wù)器發(fā)送消息時,就可以通過 /dev/fifo 這個公共的管道發(fā)出。
通過這種方式,同樣可以實現(xiàn)GUI內(nèi)部不同進程或應(yīng)用之間的通信過程,但是,當(dāng)客戶端數(shù)量較多時,這種方法就顯示出了一定的局限性,整個通信過程布局變得過于繁雜,管道越來越多使得出錯的可能性也越來越大。因此,利用 FIFO 實現(xiàn) Qt 中上述客戶端和服務(wù)器端的通信過程,更適用于客戶端應(yīng)用較少時。
小結(jié):Qt 內(nèi)部進程通信機制的內(nèi)容介紹完了,希望本文對你有所幫助,更多資料請參考編輯推薦。