搜索数据库
在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操作系统中,进程间通信(IPC,Inter-Process Communication)是多个进程之间共享数据、交换信息的关键技术。本文将深入探讨Linux进程间通信的多种方法、原理以及实际应用。 一、管道(Pipe) 管道是一种...
在Windows操作系统中,进程间通信(IPC,Interprocess Communication)是一种技术,允许不同的进程之间共享数据、协调工作或交换信息。这种技术对于多线程和多进程应用的开发至关重要,尤其是在分布式系统和并发编程...
在Windows操作系统中,进程间通信(Inter-Process Communication, IPC)是一种允许不同进程之间交换数据和协调工作的技术。它是多任务环境下程序设计的关键部分,使得应用程序可以共享资源、协同工作,甚至实现...
《嵌入式Linux应用程序开发详解》的第八章深入探讨了这一主题,提供了关于Linux中进程间通信的各种方法的全面指导。 #### Linux进程间通信概述 Linux下的进程间通信机制丰富多样,主要分为四类:管道(包括有名...
### Linux共享内存实现进程间通信详解 #### 一、引言 在Linux系统中,进程间的通信(Inter-Process Communication, IPC)是一项重要的技术,它允许不同进程之间交换数据和同步执行。其中一种高效的进程间通信方法是...
在《嵌入式Linux应用程序开发详解》一书中,第八章深入探讨了Linux下的进程间通信,涵盖了从基础概念到具体实现的各个层面。 #### Linux进程间通信方式 Linux提供了多种进程间通信的方式,每种方式都有其适用场景...
在嵌入式Linux系统中,进程间通信(IPC,Inter-Process Communication)是多个独立运行的进程之间交换数据的关键机制。这一章节详细探讨了在Linux环境下如何实现有效的进程间通信,这对于开发高效、稳定的嵌入式应用...
### 基于有名管道的进程间通信实验报告知识点解析 #### 一、有名管道概述 有名管道(Named Pipe或FIFO),作为一种进程间通信(IPC)方式,它允许无亲缘关系的进程进行通信。相比传统的无名管道,有名管道克服了仅...
共享内存+互斥量实现Linux进程间通信 一、共享内存简介 共享内存是一种高效方便的进程间通信方式,它允许两个或更多进程访问同一块内存。共享内存并未提供进程同步机制,使用共享内存完成进程间通信时,需要借助...
### 全面深入的学习进程间通信机制之一:信号量 #### 一、信号量概述 **信号量**是一种用于管理进程间对共享资源访问的重要机制,尤其在操作系统中扮演着核心角色。它能够确保资源的一致性和完整性,避免了并发...
套接字是最通用的进程间通信机制,不仅适用于同一台机器上的进程,还可用于跨网络的进程通信。套接字有多种类型,如流式套接字(TCP)、数据报套接字(UDP)等。通过`socket()`、`bind()`、`listen()`、`accept()`...
俺花了N个大洋买来的,现在免费提供给大家
### 实验八 进程通信 #### 一、实验引言 进程间的通信是指不同进程之间进行信息交换的过程。...通过实际编程操作,学生能够更好地理解进程间通信的基本概念和技术细节,为进一步探索复杂的分布式系统打下坚实的基础。
### 嵌入式Linux应用程序开发详解:第八章 进程间通信 #### 8.1 Linux下进程间通信概述 在嵌入式系统开发中,进程间通信(IPC, Inter-Process Communication)是非常重要的一个环节。随着多任务操作系统的广泛应用...
"Android 进程间通信AIDL使用详解" Android 进程间通信(IPC)是一种复杂的技术,AIDL(Android Interface Definition Language)是 Android 系统中的一种进程间通信机制。AIDL 是一种描述语言,用于定义服务器和...
本篇文章将深入探讨“奇妙的进程——多进程间的通信篇”,特别关注如何通过socket UDP(用户数据报协议)进行通信。我们将从以下几个方面进行详细阐述: 一、进程与多进程的概念 在计算机系统中,进程是执行中的...
进程间通信可以使用管道、信号、共享内存、套接字等方法。在本实验中,我们使用管道通信来实现进程间的通信。管道是一种特殊的文件,它可以实现进程间的通信。使用 pipe() 系统调用可以创建一个管道,pipe() 系统...
总之,AIDL是Android系统中实现进程间通信的重要工具,理解并熟练运用AIDL,可以帮助开发者构建更高效、稳定的应用程序。在实际开发中,根据项目需求合理利用AIDL,可以提高系统的可扩展性和可维护性。