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. php实现加好友功能

    思路: 1用户发送好友申请之后 把申请储存到申请数据表中,状态为 未验证 2 当用户登录时,查询申请表中是否有uid和被申请人id相同的,如果同意,更改状态,并把数据插入到对应的好友数据表否则,删除申 ...

  2. 【Cocos2d-x游戏引擎开发笔记(25)】XML解析

    原创文章,转载请注明出处:http://blog.csdn.net/zhy_cheng/article/details/9128819 XML是一种非常重要的文件格式,由于C++对XML的支持非常完善 ...

  3. 如何捕获Wince下form程序的全局异常

    前言 上两篇文章我们总结了在winform程序下如何捕获全局的异常.那么同样的问题,在wince下我们如何来处理呢?用相同的代码来处理可以吗? 答案是否定的,上面的方案1完全不能解决wince下的情况 ...

  4. 基于visual Studio2013解决C语言竞赛题之1041反向打印

          题目 解决代码及点评 /* 功能:将一个整数(最多是10位数)从低位到高位打印出来, 如该数是12345时,输出应是54321(只占一行) 时间:19:18 201 ...

  5. Spring Tool Suite(简称STS)针对SimpleDateFormat.pase函数的实参值不做检验,异常直接默认值之

    Spring Tool Suite(简称STS)是 Spring 团队开发的一款基于Eclipse的IDE,旨在简化开发Spring MVC 应用的流程.可以自动生成spring相关的配置文件.比如a ...

  6. Endnote从头开始五:修改output style(转载)

    Endnote从头开始五:修改output style Endnote中虽然有大量的期刊格式,但是并不能囊括所有我们需要的style,所以学会自己制作或编辑已有的期刊格式是很重要的,本节内容是Endn ...

  7. Qt入门-字符串类QString

    原地址:http://blog.csdn.net/xgbing/article/details/7770854 QString是Unicode字符的集合,它是Qt API中使用的字符串类. QStri ...

  8. html,JavaScript调用winfrom方法

    ---恢复内容开始--- 目的: 在动画上面添加点击事件,通过JavaScript调用winfrom方法 1.创建一个页面 using System; using System.Collections ...

  9. cocos2d-x 3.0来做一个简单的游戏教程 win32平台 vs2012 详解献给刚開始学习的人们!

    原代码来自于网络,因为cocos2d-x 3.0的资料,的确不多,与曾经版本号的接口非常难对上, 所以网上非常多样例都无法调试,对于新学习cocos2d-x 的同学,难度添加了,所以出一个超具体的样例 ...

  10. android ListView和GridView拖拽移位实现代码

    关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:             首先 ...