Qt 的内部进程通信机制

续欣 (xxin76@hotmail.com), 博士、大学讲师

2004 年 4 月 01 日

Qt 作为一种跨平台的基于 C++ 的 GUI 系统,能够提供给用户构造图形用户界面的强大功能。自从 1996 年 Qt 被 Trolltech 公司发布以来,该系统成为世界上很多成功的图形用户应用所使用的主要系统。更为重要的是,Linux 操作系统的桌面环境系统 KDE 也是基于 Qt 构造的。目前,Qt 已经提供了对包括 MS/Windows、Unix/X11 和嵌入式平台的支持,得到了越来越广泛的应用。
在 Qt 系统中,不仅有着构造完善的系统结构,而且为了满足用户对编写图形用户界面应用的种种需求,它还创建了许多新的系统机制,其中 Qt 所特有的内部进程通信机制尤其值得一提。 本文分析了基于 QT 的应用进程之间通信常用的三种机制:QCOP 协议,Signal-Slot 机制和 FIFO 机制。给出了各自的使用方法,并指出了各自的使用场合。

1、 QCOP协议

QCOP 是 Qt 内部的一种通信协议,这种协议用于不同的客户之间在同一地址空间内部或者不同的进程之间的通信。目前,这种机制还只在 Qt 的嵌入式版本中提供。

为实现这种通信机制,Qt 中包括了由 QObject 类继承而来的 QCopChannel 类,该类提供了诸如 send()、isRegistered() 等静态函数,它们可以在脱离对象的情况下使用。为了在 channel 中接收通信数据,用户需要构造一个 QCopChannel 的子类并提供 receive() 函数的重载函数,或者利用 connect() 函数与接收到的信号相联系。

值得一提的是,在 Qt 系统中,只提供了 QCOP 协议机制和用于接收消息的类,而如何发送消息则没有提供相应的类供用户使用。

在基于 Qt 的桌面系统 Qtopia(QPE)中,则提供了相应的发送类:QCopEnvelope。用户可以通过该类利用 channel 向其他进程发送消息。该类将通过 QCopChannel 发送 QCop 消息的过程进行了封装,用户只需要调用该类中的相关函数就可以方便地实现进程之间的通信过程。一方面,QCop 消息的发送要利用 QCopEnvelope 类,另一方面,接收消息则是通过与一个 QCopChannel 相关联。

在发送消息时,将利用如下的协议机制:

QCopEnvelope e(channelname, messagename);

对于需要携带参数的消息,必须使用"<<()"运算符将参数添加到envelope中。

e << parameter1 << parameter2 << ...;

对于不带参数的消息,只需要利用:

QCopEnvelope e(channelname, messagename);

在Qtopia中,所有的channels名都以"QPE/"开始,而messagename则是一个函数的标识符。

在接收消息时,通常只需要利用在应用程序中预先定义好的QPE/Application/{appname}管道,当然,也可以根据需要自己定义管道,并将其与一个slot函数相关联:

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 &)));

同时,需要在该类中定义相应的消息处理函数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)都是用户自己定义的消息,该函数中分别对这些消息进行了相应的处理。在该例中当收到带有参数的Message1消息时,将该字符串参数stream显示在按钮button上;当收到Message2消息时,将执行关闭Window1窗口的动作,当然用户可以根据需要自行编写相应的处理过程。

另一方面,在类Class2中需要发出消息的函数function中利用QCopEnvelope发送消息:

void Class2::function()
{ QCopEnvelope e("QPE/Do", "Message1(QString)");
e << param; }

这里发出了Message1消息,并将需要携带的参数param发送到管道中。

通过这样的过程,用户可以很方便地实现不同对象、不同进程之间通信过程,而且可以根据需要在通信过程中任意传递参数。

2、 信号-槽(Signal-Slot)机制

在Qt中,有一种用于对象之间的通信:信号-槽机制,这种机制是Qt的核心机制,也是它区别于其他GUI工具的最主要的特征。在大多数GUI工具中,通常为可能触发的每种行为定义一个回调函数,这个回调函数是一个指向函数的指针。在Qt中,信号-槽机制取代了这种繁杂的函数指针,能够实现同样的功能。信号-槽机制可以携带任意类型、任意数量的参数,而且完全是安全的,不会引起系统的崩溃。

所有由QObject类继承而来的类,或者是它的一个子类,都可以包括信号-槽机制。信号通常是当对象改变他们的状态时发出的,这就是一个对象在需要与其他对象通信时所需要做的一切,它并不知道是否有其他对象在另一端接收该信号。从这个意义上来说,这种机制实现了真正的信息封装,确保了对象可以被当作一个独立的软件构件来使用。

而槽可以被用于接收信号,它们通常是类中的成员函数。一个槽并不知晓是否有一个信号与自己相联系,同样,包含有槽函数的对象也对通信机制一无所知,它们也可以作为一个独立的软件构件。

用户可以按照需要将许多信号与一个单独的槽函数相联系,一个信号也可以按需要被联系到很多不同的槽函数。甚至还可以将一个信号直接与另一个信号相联系,这样当第一个信号被发出时立刻发出第二个信号。

这样,信号-槽相结合就产生了一种功能强大的编程机制。

例如:

button = new QAction(tr("button"), QIconSet(QPixmap("button.png")), 0, 0, this);
connect(button, SIGNAL(activated()), this, SLOT(slotButton()));

程序中定义了一个按钮,并利用connect()函数将该按钮button的activated()信号与slotButton()函数相关联,当用户触发按钮时,就会执行相应的槽函数。当然,这里的信号是QAction类中预先定义好的信号,用户在使用该机制时,可以根据需要自行定义信号,同时在适当的时候利用emit语句发出该信号。另外,在信号和相应的槽函数之间还可以传递任意参数,如:

emit signal(parameter);

3、 FIFO机制

当然,除了 Qt 内部所特有的通信机制之外,一般操作系统中常用的进程间通信机制同样可以用于 Qt 系统内部不同进程之间的通信。如消息队列、共享内存、信号量、管道等机制,其中有些机制,如信号量,在 Qt 中重新进行了封装;有些机制则可以直接调用操作系统的系统调用来实现。这里,有名管道是一种简单实用的通信机制,用户在对Qt内部机制

不甚了解的情况下,同样可以使用这种方法实现对象进程之间的通信。下面就对利用这种机制实现Qt内部进程之间的通信过程进行介绍。

首先,需要创建 FIFO,这个过程类似于创建文件,在系统中可以利用 mkfifo 命令来创建,这样就可以用 open 函数打开它,同时,一般的文件 I/O函数(close、read、write)都可以用于 FIFO。

在基于 Qt 的应用中,有很多应用采用了一种客户机-服务器模式,这时就可以利用 FIFO 在客户机和服务器之间传递数据。例如,有一个服务器,它负责接收底层程序发来的消息,同时,它与很多客户机有关,服务器需要将收到的不同消息发送到不同的客户机,而每个客户机也有请求需要发给服务器,进而发给底层程序。

下面是服务器端的程序示例:(架设已有客户端进程为读而打开/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();
}
……

在该程序中,服务器接收底层发来的信息(这里假设也是由 FIFO 管道传来),然后根据收到的信息内容,如第一个字节的内容,将信息发到不同客户端的管道中,实现对信息的正确分发。

客户端程序示例如下:(假设服务器端已经为读而打开 /dev/fifo 管道)

QFile out_file("/dev/fifo");
if(out_file.open(IO_WriteOnly|IO_Append)) {
QTextStream t(&out_file);
t << text << "/n"; }

当任意一个客户端需要向服务器发送消息时,就可以通过 /dev/fifo 这个公共的管道发出。

通过这种方式,同样可以实现GUI内部不同进程或应用之间的通信过程,但是,当客户端数量较多时,这种方法就显示出了一定的局限性,整个通信过程布局变得过于繁杂,管道越来越多使得出错的可能性也越来越大。因此,利用 FIFO 实现 Qt 中上述客户端和服务器端的通信过程,更适用于客户端应用较少时。

http://blog.csdn.net/hufengvip/article/details/5798206

Qt 的内部进程通信机制的更多相关文章

  1. Android 进程通信机制之 AIDL

    什么是 AIDL AIDL 全称 Android Interface Definition Language,即 安卓接口描述语言.听起来很深奥,其实它的本质就是生成进程间通信接口的辅助工具.它的存在 ...

  2. 图文详解 Android Binder跨进程通信机制 原理

    图文详解 Android Binder跨进程通信机制 原理 目录 目录 1. Binder到底是什么? 中文即 粘合剂,意思为粘合了两个不同的进程 网上有很多对Binder的定义,但都说不清楚:Bin ...

  3. Storm进程通信机制

    storm的worker进程之间消息传递机制图: 每个worker都有一个独立的监听进程,监听配置文件中配置过的端口列表supervisor.slots.ports,topology.receiver ...

  4. Socket进程通信机制及应用

    Socket通常称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄.应用程序通过套接字向网络发出请求或者应答网络请求.Socket即不是一个程序,也不是一个协议,其只是操作系统提供的通信层的一 ...

  5. Socket进程通信机制

    1.Socket通常称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄. 2.应用程序通过套接字向网络发出请求或者应答网络请求. 3.Socket既不是一个程序,也不是一种协议,其只是操作系统 ...

  6. AIDL/IPC Android AIDL/IPC 进程通信机制——超具体解说及使用方法案例剖析(播放器)

    首先引申下AIDL.什么是AIDL呢?IPC? ------ Designing a Remote Interface Using AIDL 通常情况下,我们在同一进程内会使用Binder.Broad ...

  7. Aidl跨进程通信机制-android学习之旅(87)

    Aidl简介 AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信的代码. 如果在 ...

  8. IPC进程通信机制

    select.poll.epoll之间的区别总结[整理] 进程间通信---共享内存 信号量和互斥锁的区别 http://www.2cto.com/os/201510/445553.html http: ...

  9. MINIX3 进程通信分析

    MINIX3 进程通信分析 6.1MINIX3 进程通信概要 MINIX3 的进程通信是 MINIX3 内核部分最重要的一个部件,我个人认为其实这 是内核中的“内核”,怎么来理解这个概念呢?其实 MI ...

随机推荐

  1. JAVA序列化在IO中读写对象的使用

    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间.序列化是为了解决在对对象流进行读写操作时所引发的问题. 序列 ...

  2. 四大流行的jdbc连接池之C3P0篇

    C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSourc ...

  3. Makefile学习(二)[第二版]

    复杂实例 #演示样例1:在上一个演示样例的基础上再添加一个可运行文件03test[改动之处已标红] .PHONY: clean all CC = gcc CFLAGS = -Wall -g BIN = ...

  4. while和do while习题

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 练习 { ...

  5. 数据备份--dump(此作者有许多有用的博客文章)

    数据中 心操作大量的数据.当遭到破坏时,这就是一场灾难.这时候需要备份来恢复,及时你又大量的备份数据都没用,备份也肯定不是在浪费时间.你也许很幸运从 来没有经历过数据丢失.但是, 由于这种事情极少发生 ...

  6. protected的一些功能

    java的访问限制有private.protected.public.这里只想进一步认识一下protected. 我对protected有4个疑问: 1,在相同包中,是否可以调用其它类的protect ...

  7. [整理]MongoDB 经常使用命令总结

    MongoDB 经常使用命令总结 简单的的增删改查数据 在查询结果中指定显示或者不显示某个字段 比如,我们希望在 lessons 集合中查找全部数据,可是不希望在返回结果中包括 slides 字段:由 ...

  8. JQuery - 去除所有空格

    $('#submit').click(function () { //去除所有空格 String.prototype.NoSpace = function () { return this.repla ...

  9. C#面试-总结

    1.override,overload的区别(鲁班联盟面试题) 笔试场景: 当时写反了区别 正确答案: override(重写,覆盖) 1.方法名.参数.返回值相同. 2.子类方法不能缩小父类方法的访 ...

  10. 安装m2eclipse

    Help->Eclipse Marketplace- 搜索 maven 安装 Maven Integration for Eclipse