免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1010 | 回复: 0
打印 上一主题 下一主题

进程间通信(七) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-08-09 10:13 |只看该作者 |倒序浏览
CD数据库程序
现在我们已经了解了我们可以如何使用有名管道来实现一个简单的客户端/服务器系统,我们可以重新查看我们的CD数据库程序并且进行相应的修改。我们同时组
合了一些信号处理从而允许我们在进程被中断时进行一些清理动作。我们会使用我们前面的具有一个命令行接口的dbm版本,从而进可能直接的查看代码。
在我们更为详细的讨论新版本的代码之前,让我们先编译这个程序。如果我们有由Web站点所获得的源代码,就可以使用makefile来编译生成server与client程序。
输入server -i使得程序初始化一个新的CD数据库。
无需多言,客户端直到服务器启动并运行时才运行。下面是makefile文件,显示程序是如何组合在一起的。
all:     server client
CC=cc
CFLAGS= -pedantic -Wall
# For debugging un-comment the next line
# DFLAGS=-DDEBUG_TRACE=1 -g
# Where, and which version, of dbm are we using.
# This assumes gdbm is pre-installed in a standard place, but we are
# going to use the gdbm compatibility routines, that make it emulate ndbm.
# We do this because ndbm is the ‘most standard’ of the dbm versions.
# Depending on your distribution, these may need changing.
DBM_INC_PATH=/usr/include/gdbm
DBM_LIB_PATH=/usr/lib
DBM_LIB_FILE=gdbm
.c.o:
    $(CC) $(CFLAGS) -I$(DBM_INC_PATH) $(DFLAGS) -c $
#include
#include
#include
#include
#include
#include
2 然后我们定义有名管道。我们使用一个管道用于服务器,另一个管道用于所有的客户端。因为也许会有多个客户端,客户端在其名字中组合一个进程ID来保证他的管道是唯一的:
#define SERVER_PIPE “/tmp/server_pipe”
#define CLIENT_PIPE “/tmp/client_%d_pipe”
#define ERR_TEXT_LEN 80
3 我们将命令实现为枚举类型,而不是#define定义。
这是一个好办法,允许编译器进行更多的类型检测工作,同时有助于程序的调试,因为许多调试器可以显示枚举常量的名字,但是不能显示由#define指令所定义的名字。
第一个typdef指定了将要发送给服务器的请求类型;第二个指定了服务器向客户端的响应类型。
typedef enum {
    s_create_new_database = 0,
    s_get_cdc_entry,
    s_get_cdt_entry,
    s_add_cdc_entry,
    s_add_cdt_entry,
    s_del_cdc_entry,
    s_del_cdt_entry,
    s_find_cdc_entry
} client_request_e;
typedef enum {
    r_success = 0,
    r_failure,
    r_find_no_more
} server_response_e;
4 接下来我们声明了一个构成可以在两个进程之间双向传递的消息的结构。
typedef struct {
    pid_t             client_pid;
    client_request_e  request;
    server_response_e response;
    cdc_entry         cdc_entry_data;
    cdt_entry         cdt_entry_data;
    char              error_text[ERR_TEXT_LEN + 1];
} message_db_t;
5 最后,我们来了解执行数据传输的管道接口函数,实现在pipe_imp.c中。这些函数分为服务器端与客户端函数,分别在第一个与第二个块中:
int server_starting(void);
void server_ending(void);
int read_request_from_client(message_db_t *rec_ptr);
int start_resp_to_client(const message_db_t mess_to_send);
int send_resp_to_client(const message_db_t mess_to_send);
void end_resp_to_client(void);
int client_starting(void);
void client_ending(void);
int send_mess_to_server(message_db_t mess_to_send);
int start_resp_from_server(void);
int read_resp_from_server(message_db_t *rec_ptr);
void end_resp_from_server(void);
我们将讨论的其余部分分为客户端接口函数与定义在pipe_imp.c中的服务器端与客户端函数的详细讨论,而且我们会讨论必须的源代码。
客户端接口函数
现在我们来探讨client_if.c。他为数据库访问例程提供了一个虚拟版本,他将请求编码进入message_db_t结构中,然后使用pipe_imp.c中的例程将这些请求传递给服务器。这会使得我们在原始的app_ui.c上进行最小的修改。
试验--客户解释器
1 这个文件实现了在cd_data.h中定义的9个数据库函数。他通过向服务器传递请求并且由函数中返回服务器响应来实现操作,类似于一个中间人。这个文件由#include与常量定义开始:
#define _POSIX_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include "cd_data.h"
#include "cliserv.h"
2 静态变量mypid减少了所需要的getpid函数的调用次数。我们使用一个局部函数,read_one_response,来减少重复的代码:
static pid_t mypid;
static int read_one_response(message_db_t *rec_ptr);
3 database_initialize与close例程仍然被调用,但是现在分别用来初始化管道接口的客户端和当客户端退出时移除多余的有名管道。
int database_initialize(const int new_database)
{
    if (!client_starting()) return(0);
    mypid = getpid();
    return(1);
} /* database_initialize */
void database_close(void) {
    client_ending();
}
4
get_cdc_entry例被调用用来在指定一个CD类别标题的情况下由数据中获取一个类别实体。在这里我们将请求编码进message_db_t结构
中,并且将其传递给服务器。然后我们读取返回的响应放入另一个不同的message_db_t结构中。如果找到一个实体,他就会被作为一个
cdc_entry结构被包含进入message_db_t结构中,所以我们需要介绍结构的相关部分:
cdc_entry get_cdc_entry(const char *cd_catalog_ptr)
{
    cdc_entry ret_val;
    message_db_t mess_send;
    message_db_t mess_ret;
    ret_val.catalog[0] = ‘\0’;
    mess_send.client_pid = mypid;
    mess_send.request = s_get_cdc_entry;
    strcpy(mess_send.cdc_entry_data.catalog, cd_catalog_ptr);
    if (send_mess_to_server(mess_send)) {
        if (read_one_response(&mess_ret)) {
            if (mess_ret.response == r_success) {
                ret_val = mess_ret.cdc_entry_data;
            } else {
                fprintf(stderr, “%s”, mess_ret.error_text);
            }
        } else {
            fprintf(stderr, “Server failed to respond\n”);
        }
    } else {
        fprintf(stderr, “Server not accepting requests\n”);
    }
    return(ret_val);
}
5 下面是我们用来避免重复代码的read_one_response函数源码:
static int read_one_response(message_db_t *rec_ptr) {
    int return_code = 0;
    if (!rec_ptr) return(0);
  if (start_resp_from_server()) {
      if (read_resp_from_server(rec_ptr)) {
          return_code = 1;
      }
      end_resp_from_server();
  }
  return(return_code);
}
6 其他的get_xxx,del_xxx与add_xxx例程的实现方式与get_cdc_entry函数类似,为了完整,我们在这里进行介绍。首先是用来读取CD音轨的函数源码:
cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no)
{
    cdt_entry ret_val;
    message_db_t mess_send;
    message_db_t mess_ret;
    ret_val.catalog[0] = ‘\0’;
    mess_send.client_pid = mypid;
    mess_send.request = s_get_cdt_entry;
    strcpy(mess_send.cdt_entry_data.catalog, cd_catalog_ptr);
    mess_send.cdt_entry_data.track_no = track_no;
    if (send_mess_to_server(mess_send)) {
        if (read_one_response(&mess_ret)) {
            if (mess_ret.response == r_success) {
                ret_val = mess_ret.cdt_entry_data;
            } else {
                fprintf(stderr, “%s”, mess_ret.error_text);
            }
        } else {
            fprintf(stderr, “Server failed to respond\n”);
        }
    } else {
        fprintf(stderr, “Server not accepting requests\n”);
    }
    return(ret_val);
}
7 下面是两个是用来添加数据的函数,第一个向类别中添加,而第二个向音轨数据中添加:
int add_cdc_entry(const cdc_entry entry_to_add)
{
    message_db_t mess_send;
    message_db_t mess_ret;
    mess_send.client_pid = mypid;
    mess_send.request = s_add_cdc_entry;
    mess_send.cdc_entry_data = entry_to_add;
    if (send_mess_to_server(mess_send)) {
        if (read_one_response(&mess_ret)) {
            if (mess_ret.response == r_success) {
                return(1);
            } else {
                fprintf(stderr, “%s”, mess_ret.error_text);
            }
        } else {
            fprintf(stderr, “Server failed to respond\n”);
        }
    } else {
        fprintf(stderr, “Server not accepting requests\n”);
    }
    return(0);
}
int add_cdt_entry(const cdt_entry entry_to_add)
{
    message_db_t mess_send;
    message_db_t mess_ret;
    mess_send.client_pid = mypid;
    mess_send.request = s_add_cdt_entry;
    mess_send.cdt_entry_data = entry_to_add;
    if (send_mess_to_server(mess_send)) {
        if (read_one_response(&mess_ret)) {
            if (mess_ret.response == r_success) {
                return(1);
            } else {
                fprintf(stderr, “%s”, mess_ret.error_text);
            }
        } else {
            fprintf(stderr, “Server failed to respond\n”);
        }
    } else {
        fprintf(stderr, “Server not accepting requests\n”);
    }
    return(0);
}
8 最后是两个用来数据删除的函数:
int del_cdc_entry(const char *cd_catalog_ptr)
{
    message_db_t mess_send;
    message_db_t mess_ret;
    mess_send.client_pid = mypid;
    mess_send.request = s_del_cdc_entry;
    strcpy(mess_send.cdc_entry_data.catalog, cd_catalog_ptr);
    if (send_mess_to_server(mess_send)) {
        if (read_one_response(&mess_ret)) {
            if (mess_ret.response == r_success) {
                return(1);
            } else {
                fprintf(stderr, “%s”, mess_ret.error_text);
            }
        } else {
            fprintf(stderr, “Server failed to respond\n”);
        }
    } else {
        fprintf(stderr, “Server not accepting requests\n”);
    }
    return(0);
}
int del_cdt_entry(const char *cd_catalog_ptr, const int track_no)
{
    message_db_t mess_send;
    message_db_t mess_ret;
    mess_send.client_pid = mypid;
    mess_send.request = s_del_cdt_entry;
    strcpy(mess_send.cdt_entry_data.catalog, cd_catalog_ptr);
    mess_send.cdt_entry_data.track_no = track_no;
    if (send_mess_to_server(mess_send)) {
        if (read_one_response(&mess_ret)) {
            if (mess_ret.response == r_success) {
                return(1);
            } else {
                fprintf(stderr, “%s”, mess_ret.error_text);
            }
        } else {
            fprintf(stderr, “Server failed to respond\n”);
        }
    } else {
        fprintf(stderr, “Server not accepting requests\n”);
    }
    return(0);
}
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/19185/showart_2022423.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP