14.4 CD数据库应用程序

现在,我们可以用在本章中学到的IPC机制来修改CD数据库应用程序了。

我们可以使用这3种IPC机制的不同组合方式,但考虑到需要传递的消息非常小,所以直接使用消息队列来实现请求和响应的传递应该是比较合乎情理的。

如果需要传递的数据量很大,我们就可以考虑用共享内存来传递实际数据,再用信号量或消息来传递一个“令牌”去通知其他进程共享内存中的数据已可用。

消息队列的接口省去了我们在第11章中遇到的问题,那时我们需要在数据传递过程中两个进程都打开管道。使用消息队列允许一个进程往队列中放消息,即使这个进程是当前该队列的唯一用户。

唯一需要我们做出的重要决定是如何向客户返回查询结果。一种简单的做法是让服务器用一个队列,每个客户用一个队列。但如果并发客户数太大,这将引起问题,因为需要大量的消息队列。通过使用消息中的消息ID域,就可以允许所有客户只使用一个队列。通过在消息中使用客户进程ID,就可以把响应消息和特定的客户进程联系起来。然后,每个客户可以只获取那些发送给它的消息,而将发送给其他客户的消息留在队列中。

要想把我们的CD数据库应用程序转换为使用IPC机制,只需要更换第13章代码中的文件pipe_imp.c。在以下几页内容中,我们将介绍替换文件ipc_imp.c中的核心代码。

14.4.1 修改服务器函数

首先,需要更新服务器函数。

(1)首先,包括必要的头文件,声明一些消息队列的键,然后定义一个用来保存消息数据的结构:

14.4 CD数据库应用程序 - 图1

(2)两个文件范围的变量分别保存msgget函数返回的两个队列标识符:

14.4 CD数据库应用程序 - 图2

(3)我们让服务器负责创建两个消息队列:

14.4 CD数据库应用程序 - 图3

14.4 CD数据库应用程序 - 图4

(4)服务器还负责在退出时执行清理工作。服务器结束时,我们将文件范围的变量设置为无效值。当服务器在调用了server_ending后还试图发送消息时,这种做法可以捕获到这样的错误。

14.4 CD数据库应用程序 - 图5

(5)服务器读函数的作用是:从队列中读取一个任一类型(即来自任意客户)的消息,返回消息的数据部分(忽略消息的类型):

14.4 CD数据库应用程序 - 图6

(6)发送响应时,用客户进程ID来编址消息,客户进程ID存放在客户的请求中:

14.4 CD数据库应用程序 - 图7

14.4 CD数据库应用程序 - 图8

14.4.2 修改客户函数

接着,修改客户函数。

(1)当客户启动时,它需要找到服务器和客户队列标识符。客户本身并不创建队列。如果服务器没有运行,这个函数就会因消息队列不存在而失败。

14.4 CD数据库应用程序 - 图9

(2)与服务器一样,当客户结束时,我们将文件范围的变量设置为无效值。客户在调用了client_ending之后还试图发送消息时,这种做法就可以捕获到这样的错误。

14.4 CD数据库应用程序 - 图10

(3)为了发送消息给服务器,将数据存储到我们的结构中。注意,我们必须设置消息的键。因为0对键来说是个无效值,而如果不对这个键做定义就意味着它将取一个(显然的)随机值,如果碰巧这个值是0的话,这个函数就会调用失败。

14.4 CD数据库应用程序 - 图11

14.4 CD数据库应用程序 - 图12

(4)当客户从服务器获取一个消息时,它用自己的进程ID来只接收发送给它的消息,而忽略发送给其他客户的消息。

14.4 CD数据库应用程序 - 图13

(5)为了保持与pipe_imp.c的完全兼容,我们还需要定义4个函数。但在新程序中,这些函数是空的,因为现在已经不再需要它们在使用管道时实现的操作了。

14.4 CD数据库应用程序 - 图14

我们现在可以启动服务器,它在后台完成实际的数据存储和检索。然后运行客户程序,它通过消息连接服务器。

我们在这里所需要做的就是将第11章中的接口函数替换为使用消息队列的实现。将应用程序转换为使用消息队列展示了IPC消息队列的强大。因为与使用管道的程序相比,我们需要使用的函数更少了,即使那些仍然需要使用的函数也比它们以前的实现版本要简单得多。