`
helpbs
  • 浏览: 1161803 次
文章分类
社区版块
存档分类
最新评论

进程间通信(八)

 
阅读更多

搜索数据库

在CD关键字上的搜索比较复杂。函数的用户希望一旦调用就启动一个搜索。我们在第7章通过将在第一次调用上的*first_call_ptr设置为true并且函数返回第一个匹配结果来满足这种需求。在接下来的搜索函数调用中,*first_call_ptr设置为false,从而会返回更多的匹配,每次调用返回一个匹配结果。

现在我们已经将程序分为两个进程,我们不能再允许搜索在服务器端一次处理一个实体,因为另一个不同的客户端也许会由服务器请求一个不同的搜索,而我们的搜索仍在处理之中。我们不能使得服务器端为每一个客户端单独存储搜索内容(搜索已经进行到哪里),因为当一个用户找到他们要找的CD关键值时或是客户端出现问题,客户端就可以简单的停止搜索。

我们或者可以改变搜索执行的方式,或者是,正如我们在这里所选择的,在接口例程中隐藏复杂性。我们所做的就是使得服务器返回所有可能的搜索匹配,然后将他们存储在一个临时文件中直到客户端请求他们。

试验--查找

1 这个函数看起更为复杂,因为他调用我们会在下一节讨论的三个管道函数:send_mess_to_server,start_resp_from_server与read_resp_from_server。

cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr)
{
message_db_t mess_send;
message_db_t mess_ret;
static FILE *work_file = (FILE *)0;
static int entries_matching = 0;
cdc_entry ret_val;
ret_val.catalog[0] = ‘/0’;
if (!work_file && (*first_call_ptr == 0)) return(ret_val);

2 下面是搜索的第一个调用,在这里*first_call_ptr设置为true。为了防止我们忘记,立即将其设置为false。在这个函数中创建一个work_file并且初始化客户端消息结构。

if (*first_call_ptr) {
*first_call_ptr = 0;
if (work_file) fclose(work_file);
work_file = tmpfile();
if (!work_file) return(ret_val);
mess_send.client_pid = mypid;
mess_send.request = s_find_cdc_entry;
strcpy(mess_send.cdc_entry_data.catalog, cd_catalog_ptr);

3 接下是一个三层的条件测试。如果消息成功的发送到服务器,客户端会等待服务器的响应。当服务器的read操作成功,搜索匹配就会被记入work_file中,而相应的entries_matching也会增加。

if (send_mess_to_server(mess_send)) {
if (start_resp_from_server()) {
while (read_resp_from_server(&mess_ret)) {
if (mess_ret.response == r_success) {
fwrite(&mess_ret.cdc_entry_data, sizeof(cdc_entry), 1, work_file);
entries_matching++;
} else {
break;
}
} /* while */
} else {
fprintf(stderr, “Server not responding/n”);
}
} else {
fprintf(stderr, “Server not accepting requests/n”);
}

4 下面的测试检测搜索是否成功。然后fseek调用会将work_file设置为数据将要写入的下一个位置。

if (entries_matching == 0) {
fclose(work_file);
work_file = (FILE *)0;
return(ret_val);
}
(void)fseek(work_file, 0L, SEEK_SET);

5 如果这并不是第一次使用特定的搜索模式调用搜索函数,下面的代码会检测是否还有余下的匹配。最后,下一个匹配项会被读入ret_val结构中。前面的检测认为存在一个匹配项。

} else {
/* not *first_call_ptr */
if (entries_matching == 0) {
fclose(work_file);
work_file = (FILE *)0;
return(ret_val);
}
}
fread(&ret_val, sizeof(cdc_entry), 1, work_file);
entries_matching—;
return(ret_val);
}

服务器接口

就如客户端有一个对app_ui.c程序的一个接口,服务器端也需要一个程序来控制(重命名的)cd_access.c,现在是cd_dbm.c。服务器的main函数如下。

试验--server.c

1 我们在程序的开始处声明了几个全局变量,一个process_command函数原型,以及一个信号捕获函数来保证一个干净的退出。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include “cd_data.h”
#include “cliserv.h”
int save_errno;
static int server_running = 1;
static void process_command(const message_db_t mess_command);
void catch_signals()
{
server_running = 0;
}

2 现在我们来了解一下main函数。在检测信号捕获例程正确之后,程序检测我们是否在命令行传递了一个-i选项。如果我们传递了,程序就会创建一个新的数据库。如果cd_dbm.c中的database_initialize例程失败,就会显示一个错误消息。如果一切正常而且服务器正在运行,由客户端来的请求就会被传递给process_command函数,这个函数我们将会稍后介绍。

int main(int argc, char *argv[]) {
struct sigaction new_action, old_action;
message_db_t mess_command;
int database_init_type = 0;
new_action.sa_handler = catch_signals;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
if ((sigaction(SIGINT, &new_action, &old_action) != 0) ||
(sigaction(SIGHUP, &new_action, &old_action) != 0) ||
(sigaction(SIGTERM, &new_action, &old_action) != 0)) {
fprintf(stderr, “Server startup error, signal catching failed/n”);
exit(EXIT_FAILURE);
}
if (argc > 1) {
argv++;
if (strncmp(“-i”, *argv, 2) == 0) database_init_type = 1;
}
if (!database_initialize(database_init_type)) {
fprintf(stderr, “Server error:-/
could not initialize database/n”);
exit(EXIT_FAILURE);
}
if (!server_starting()) exit(EXIT_FAILURE);
while(server_running) {
if (read_request_from_client(&mess_command)) {
process_command(mess_command);
} else {
if(server_running) fprintf(stderr, “Server ended - can not /
read pipe/n”);
server_running = 0;
}
} /* while */
server_ending();
exit(EXIT_SUCCESS);
}

3 所有的客户端消息都会被传递给process_command函数,在那里他们会被传递给一个case语句,从而执行cd_dbm.c中的正确调用。

static void process_command(const message_db_t comm)
{
message_db_t resp;
int first_time = 1;
resp = comm; /* copy command back, then change resp as required */
if (!start_resp_to_client(resp)) {
fprintf(stderr, “Server Warning:-/
start_resp_to_client %d failed/n”, resp.client_pid);
return;
}
resp.response = r_success;
memset(resp.error_text, ‘/0’, sizeof(resp.error_text));
save_errno = 0;
switch(resp.request) {
case s_create_new_database:
if (!database_initialize(1)) resp.response = r_failure;
break;
case s_get_cdc_entry:
resp.cdc_entry_data =
get_cdc_entry(comm.cdc_entry_data.catalog);
break;
case s_get_cdt_entry:
resp.cdt_entry_data =
get_cdt_entry(comm.cdt_entry_data.catalog,
comm.cdt_entry_data.track_no);
break;
case s_add_cdc_entry:
if (!add_cdc_entry(comm.cdc_entry_data)) resp.response =
r_failure;
break;
case s_add_cdt_entry:
if (!add_cdt_entry(comm.cdt_entry_data)) resp.response =
r_failure;
break;
case s_del_cdc_entry:
if (!del_cdc_entry(comm.cdc_entry_data.catalog)) resp.response
= r_failure;
break;
case s_del_cdt_entry:
if (!del_cdt_entry(comm.cdt_entry_data.catalog,
comm.cdt_entry_data.track_no)) resp.response = r_failure;
break;
case s_find_cdc_entry:
do {
resp.cdc_entry_data =
search_cdc_entry(comm.cdc_entry_data.catalog,
&first_time);
if (resp.cdc_entry_data.catalog[0] != 0) {
resp.response = r_success;
if (!send_resp_to_client(resp)) {
fprintf(stderr, “Server Warning:-/
failed to respond to %d/n”, resp.client_pid);
break;
}
} else {
resp.response = r_find_no_more;
}
} while (resp.response == r_success);
break;
default:
resp.response = r_failure;
break;
} /* switch */
sprintf(resp.error_text, “Command failed:/n/t%s/n”,
strerror(save_errno));
if (!send_resp_to_client(resp)) {
fprintf(stderr, “Server Warning:-/
failed to respond to %d/n”, resp.client_pid);
}
end_resp_to_client();
return;
}

在我们了解实际的管道实现之前,让我们来讨论一下在客户端与服务器进程之间传递数据需要发生的事件序列。图13-9显示所启动的客户端与服务器进程以及在处理命令与响应时双方是如何循环的。

在这个实现中,形势有一些困难,因为对于一个搜索请求,客户端向服务器传递一个单一命令,然后希望由服务器接收一个或多个响应。这会导致其他额外的一些复杂性,主要在客户端。

分享到:
评论

相关推荐

    LINUX高级程序设计(中文第二版)第八章 进程间通信-管道和信号

    俺花了N个大洋买来的,现在免费提供给大家

    《嵌入式Linux应用程序开发标准教程》(第2版)-第8章、进程间通信.pdf

    《嵌入式Linux应用程序开发标准教程》(第2版)-第8章、进程间通信.pdf

    unix环境高级编程

    第1章 UNIX基础知识 第2章 UNIX标准化及实现 ...第十四章 进程间通信 第十五章 高级进程间通信 第十六章 一个数据库函数库 第十七章 与PostScript打印机通信 第十八章 调制解调器拨号器 第十九章 伪终端 附录

    Unix系统编程(程序设计语言+系统调用)

    本课程主要讲述在Unix/Linux环境下高级编程的方法,其内容包括文件、信号、POSIX线程、UDP、WEB和客户机服务器等...第五章 进程间通信 第六章 管道 第七章 高级进程间通信 第八章 精灵进程 目前有八章PPT,之后持续更新

    linux网络编程

    23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信发展 进程间通信分类 进程间共享信息的三种方式 IPC对象的持续性 24进程间通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 ...

    Linux内核情景分析(二级标题).pdf

    第1章 预备知识 1.1 Linux内核简介 1.2 Intel X86 CPU 系列的寻址方式 1.3 i386的页式内存管理机制 ...第7章 基于socket的进程间通信 第八章 设备驱动 第9章 多处理器的SMP系统结构 第10章 系统的引导和初始化

    C++教程网《Linux网络编程》视频百度云地址

    Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...

    2018年C++教程网的linux网络编程视频共41集百度云下载链接.rar

    Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元...

    嵌入式Linux ARM开发课件第八讲

    ARM7~ARM9体系结构体系结构介绍 ARM7(9)TDMI处理器内核...进程间通信: 进程通信的基本概念,管道、信号、消息队列、信号量、共享内存。 网络通讯接口,socket通信编程。 串口通讯程序和编程实践 多线程程序设计 等。

    Linux网络编程 视频 教程

    Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...

    C++教程网视频:linux网络编程

    Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...

    c++教程网的linux网络编程视频下载

    Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...

    [免费]2018年C++教程网的linux网络编程视频百度云下载链接.rar

    Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...

    Linux操作系统分析实验指导书

    实验五 Linux系统的进程间通信:管道通信 实验六 Linux系统下利用链表实现动态内存分配 实验七 Linux系统下文件管理模拟实验 实验八 Linux系统下的字符设备驱动程序编程 补充: 一、 Linux系统下的进程创建与进程...

    Linux操作系统实验报告

    (3) 实验三 Linux下进程间管道通信; (4) 实验四 IPC进程间共享内存通信; (5) 实验五 IPC信号量使用 (6) 实验六 Linux内存基本原理 (7) 实验八 设备驱动程序 (8) 实验九 Linux 下socket网络通信 仅供...

    IPC消息队列

    该程序是我写的博客“一起talk C栗子吧(第九十八回:C语言实例--使用消息队列进行进程间通信二)”的配套程序,共享给大家使用

    linux环境下的网络编程

    第四章进程间通信; 第五章通信协议简介; 第六章 Berkeley 套接字; 第七章网络安全; 第八章 Ping 例; 第九章 tftp 例程; 第十章远程命令执行; 第十一章远程注; 第十二章远程过程调; 第十三章远程磁带的访问...

    Linux内核源代码情景分析 pdf

    第一章 预备知识 第二章 存储管理 第三章 中断、异常和系统调用 ...第六章 传统的UNIX进程间通信 第七章 基于socket的进程间通信 第八章 设备驱动 第九章 多处理器SMP系统结构 第十章 系统引导和初始化

    Android 使用AIDL进行两个APP之间通讯以及相互消息回调(一)

    作用:可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求。 1.AIDL支持的基本数据类型 八种基本数据类型:byte、char、short、int、long、float、double、boolean String,...

    linux 网络编程

    第四章 进程间通信....... 第五章 通信协议简介 第六章 Berkeley 套接字. 第七章 网络安全性.... 第八章 Ping 例程.. 第九章 tftp 例程.... 第十章 远程命令执行 第十一章 远程注册.... 第十二章 远程过程调用 第十...

Global site tag (gtag.js) - Google Analytics