在“协作半驻留式服务器程序开发框架 --- 基于 Postfix 服务器框架改造 “文章中,介绍了ACL库中协作式半驻留服务器程序框架,本文将以其中第4)种(多线程进程池)开发框架为基础编写一个简单的 demo 程序,使大家熟悉这类服务器程序的开发方式。
该 demo 一个简单的 echo 服务器程序,主要由 main.c, service_main.c, service_var.c, service_main.h, service_var.h, Makefile 六个文件组成。下面分别介绍一下各个文件的主要功能。
1) main.c 主程序
#include "lib_acl.h" /* ACL库的头文件 */ #include <assert.h> #include "service_main.h" #include "service_var.h" /* 测试函数入口 */ static void service_test(void) { const char *addr = "127.0.0.1:8885"; ACL_VSTREAM *sstream, *client; int ret; sstream = acl_vstream_listen(addr, 32); /* 创建服务端口的监听套接口并转化流 */ assert(sstream != NULL); acl_xinetd_params_int_table(NULL, var_conf_int_tab); /* 设置整数类型的配置项 */ acl_xinetd_params_str_table(NULL, var_conf_str_tab); /* 设置字符串类型的配置项 */ acl_xinetd_params_bool_table(NULL, var_conf_bool_tab); /* 设置 bool 类型的配置项 */ printf("listen %s ok\n", addr); while (1) { client = acl_vstream_accept(sstream, NULL, 0); /* 等待客户端连接 */ if (client == NULL) { printf("accept error: %s\n", acl_last_serror()); break; } /* 获得一个客户端连接流 */ while (1) { /* 处理该客户端的请求 */ ret = service_main(client, NULL); if (ret < 0) { /* 关闭客户端连接流 */ acl_vstream_close(client); break; } if (ret > 0) { /* service_main() 内部关闭了客户端流 */ break; } } } /* 测试结束,关闭监听套接口 */ acl_vstream_close(sstream); } /* 程序入口 */ int main(int argc, char *argv[]) { if (argc == 2 && strcasecmp(argv[1], "test") == 0) { /* 测试入口 */ service_test(); } else { /* 服务器框架入口 */ acl_ioctl_app_main(argc, argv, service_main, NULL, ACL_APP_CTL_INIT_FN, service_init, ACL_APP_CTL_EXIT_FN, service_exit, ACL_APP_CTL_CFG_BOOL, var_conf_bool_tab, ACL_APP_CTL_CFG_INT, var_conf_int_tab, ACL_APP_CTL_CFG_STR, var_conf_str_tab, ACL_APP_CTL_END); } return (0); }
该文件中,可以看到两个分支,一个是测试用入口 service_test(),另一个是服务器框架入口 acl_ioctl_app_main()。其中的 service_test() 主要是为了开发者调试自己的程序用,此时程序运行是独立运行的,不需要 acl_master 主进程参与;而 acl_ioctl_app_main() 则为服务器框架入口,需要 acl_master 主进程进行控制,这主要是用在生产环境中。
2) service_main.c 任务处理函数
#include "lib_acl.h" #include "service_var.h" #include "service_main.h" /* 初始化函数 */ void service_init(void *init_ctx acl_unused) { const char *myname = "service_init"; acl_msg_info("%s: init ok ...", myname); } /* 进程退出前调用的函数 */ void service_exit(void *arg acl_unused) { const char *myname = "service_exit"; acl_msg_info("%s: exit now ...", myname); } /* 协议处理函数入口 */ int service_main(ACL_VSTREAM *client, void *run_ctx acl_unused) { const char *myname = "service_main"; char buf[1024]; int ret; ret = acl_vstream_gets(client, buf, sizeof(buf)); if (ret == ACL_VSTREAM_EOF) { if (var_cfg_debug_enable) acl_msg_info("%s: close client now, (%s)", myname, var_cfg_debug_msg); return (-1); /* 返回负值以使框架内部关闭 client 数据流 */ } if (acl_vstream_writen(client, buf, ret) == ACL_VSTREAM_EOF) { if (var_cfg_debug_enable) acl_msg_info("%s: write to client error, close now(%s)", myname, var_cfg_debug_msg); return (-1); /* 返回负值以使框架内部关闭 client 数据流 */ } if (var_cfg_keep_alive) { if (var_cfg_debug_enable) acl_msg_info("%s: keep alive, wait client...", myname); return (0); /* 返回 0 以使框架内部自动监听该数据流从而保持长连接 */ } else { /* 可以在此处返回 =1, 使框架内部自动关闭 client 数据流, * 也可以在此处直接关闭 client 数据流,同时返回 1 告诉框架 * 该流已经被用户关闭了不必再关心该 client 数据流. */ acl_vstream_close(client); return (1); } }
该文件中主要有三个函数(这三个函数都是在 main.c 中的 acl_ioctl_app_main() 设置的,这样服务器框架就以回调的方式分别调用它们): service_init(), 进程初始化回调函数,用户可以在些函数中做一些全局化的初始化,如数据库的连接建立 ;service_main(), 任务调用入口,每当有一个客户端连接建立时,服务器框架便会调用此函数由应用来处理与客户端的信息交流,其中的 client 参数为由服务器框架已经与客户端之间建立起的数据流,参数 run_ctx 可以在 acl_ioctl_app_main() 中进行设置;service_exit(), 当进程退出前便会回调此函数,用户可以在此函数里做一些清理工作。
3) service_var.c 配置参数源码
#include "lib_acl.h" #include "service_var.h" char *var_cfg_debug_msg; ACL_CFG_STR_TABLE var_conf_str_tab[] = { { "debug_msg", "test_msg", &var_cfg_debug_msg }, { 0, 0, 0 } }; int var_cfg_debug_enable; int var_cfg_keep_alive; ACL_CFG_BOOL_TABLE var_conf_bool_tab[] = { { "debug_enable", 1, &var_cfg_debug_enable }, { "keep_alive", 1, &var_cfg_keep_alive }, { 0, 0, 0 } }; int var_cfg_io_timeout; ACL_CFG_INT_TABLE var_conf_int_tab[] = { { "io_timeout", 120, &var_cfg_io_timeout, 0, 0 }, { 0, 0 , 0 , 0, 0 } };
该文件主要是一些全局化的配置项参数,主要有三类:int 类型的配置项,bool 类型的配置项,字符串类型的配置项,分别被设置在 var_conf_int_tab,var_conf_bool_tab,var_conf_str_tab 可,而这三个变量也是通过 acl_ioctl_app_main() 以参数方式传递给服务器框架,由框架读取配置文件后将配置内容分别设置在这三个变量中的具体配置变量中。
4) service_main.h 为 service_main.c 的头文件
#ifndef __SERVICE_MAIN_INCLUDE_H__ #define __SERVICE_MAIN_INCLUDE_H__ #include "lib_acl.h" #ifdef __cplusplus extern "C" { #endif /** * 初始化函数,服务器模板框架启动后仅调用该函数一次 * @param init_ctx {void*} 用户自定义类型指针 */ extern void service_init(void *init_ctx); /** * 进程退出时的回调函数 * @param exist_ctx {void*} 用户自定义类型指针 */ extern void service_exit(void *exit_ctx); /** * 协议处理函数入口 * @param stream {ACL_VSTREAM*} 客户端数据连接流 * @param run_ctx {void*} 用户自定义类型指针 */ extern int service_main(ACL_VSTREAM *stream, void *run_ctx); #ifdef __cplusplus } #endif #endif
5) service_var.h 为 service_var.c 的头文件
#ifndef __SERVICE_VAR_INCLUDE_H__ #define __SERVICE_VAR_INCLUDE_H__ #include "lib_acl.h" /*------------- 字符串配置项 ----------------*/ extern ACL_CFG_STR_TABLE var_conf_str_tab[]; /* 日志调试输出信息 */ extern char *var_cfg_debug_msg; /*-------------- 布尔值配置项 ---------------*/ extern ACL_CFG_BOOL_TABLE var_conf_bool_tab[]; /* 是否输出日志调试信息 */ extern int var_cfg_debug_enable; /* 是否与客户端保持长连接 */ extern int var_cfg_keep_alive; /*-------------- 整数配置项 -----------------*/ extern ACL_CFG_INT_TABLE var_conf_int_tab[]; /* 每次与客户端通信时,读超时时间(秒) */ extern int var_cfg_io_timeout; #endif
6) Makefile 文件中记录着怎样与 ACL 库 lib_acl.a 及 ACL 的头文件进行编译连接,以及在不同UNIX平台下需要哪些系统库。
7) demo.cf 为配置文件
service server {
# 进程是否禁止运行
master_disable = yes
# 服务地址及端口号
master_service = 127.0.0.1:5001
# 服务监听为域套接口
# master_service = aio_echo.sock
# 服务类型
master_type = inet
# master_type = unix
# 当子进程异常退出时,如果该值非空,则将子进程异常退出的消息通知该服务
# master_notify_addr = 127.0.0.1:5801
# 是否允许延迟接受客户端连接,如果为0则表示关闭该功能,如果大于0则表示打开此功能
# 并且此值代表延迟接受连接的超时值,超过此值时如果客户端依然没有发来数据,则操作
# 系统会在系统层直接关闭该连接
# master_defer_accept = 0
# 是否只允许私有访问, 如果为 y, 则域套接口创建在 {install_path}/var/log/private/ 目录下,
# 如果为 n, 则域套接口创建在 {install_path}/var/log/public/ 目录下,
master_private = n
master_unpriv = n
# 是否需要 chroot: n -- no, y -- yes
master_chroot = n
# 每隔多长时间触发一次,单位为秒(仅对 trigger 模式有效)
master_wakeup = -
# 最大进程数
master_maxproc = 1
# 进程程序名
master_command = ioctl_echo
# 进程日志记录文件
master_log = {install_path}/var/log/ioctl_echo.log
# 进程启动参数,只能为: -u [是否允许以某普通用户的身份运行]
# master_args =
# 传递给服务子进程的环境变量, 可以通过 getenv("SERVICE_ENV") 获得此值
# master_env = logme:FALSE, priority:E_LOG_INFO, action:E_LOG_PER_DAY, flush:sync_flush, imit_size:512,\
# sync_action:E_LOG_SEM, sem_name:/tmp/ioctl_echo.sem
# 每个进程实例处理连接数的最大次数,超过此值后进程实例主动退出
ioctl_use_limit = 100
# 每个进程实例的空闲超时时间,超过此值后进程实例主动退出
ioctl_idle_limit = 120
# 记录进程PID的位置(对于多进程实例来说没有意义)
ioctl_pid_dir = {install_path}/var/pid
# 进程运行时所在的路径
ioctl_queue_dir = {install_path}/var
# 读写超时时间, 单位为秒
ioctl_rw_timeout = 120
# 读缓冲区的缓冲区大小
ioctl_buf_size = 8192
# 每次 accept 时的循环接收的最大次数
ioctl_max_accept = 25
# 在并发访问量非常低的情况下,如访问量在 10 次/秒 以下时,可以找开此值(即赋为1),以加速事件循环过程,
# 从而防止服务进程阻塞在 select 上的时间过长而影响访问速度
# ioctl_enable_dog = 0
# 进程运行时的用户身份
ioctl_owner = root
# 用 select 进行循环时的时间间隔
# 单位为秒
ioctl_delay_sec = 0
# 单位为微秒
ioctl_delay_usec = 500
# 采用事件循环的方式: select(default), poll, kernel(epoll/devpoll/kqueue)
ioctl_event_mode = select
# 线程池的最大线程数
ioctl_max_threads = 250
# 线程的堆栈空间大小,单位为字节,0表示使用系统缺省值
ioctl_stacksize = 0
# 允许访问 udserver 的客户端IP地址范围
ioctl_access_allow = 127.0.0.1:255.255.255.255, 127.0.0.1:127.0.0.1
############################################################################
# 应用自己的配置选项
debug_msg = test msg
debug_enable = 1
keep_alive = 1
}
小结,可以看出开发一个多线程的进程池服务程序是如此之简单,我们无需写一些复杂的服务器控制代码,这个过程完全由ACL服务器框架内部自动处理。这个例子在 acl库的 samples/master/ioctl_echo3 中可以看到,要想使其运行在生产环境下,需要将编译后的可执行程序及配置文件按 "协作半驻留式服务器程序开发框架 --- 基于 Postfix 服务器框架改造 "文章所介绍的 acl_master 框架的运行位置及配置位置中即可,然后运行 acl_master 中的 reload.sh 即可以将这个新的服务加载了。
参考:
协作半驻留式服务器程序开发框架 --- 基于 Postfix 服务器框架改造
利用ACL库快速创建你的网络程序
个人微博:http://weibo.com/zsxxsz
QQ 群:242722074
相关推荐
服务开发的纪律 08-05-06 Win2000服务器端应用程序开发设计指南-设备I/O及线程间通讯 08-05-06 Windows2000 服务器端应用程序开发设计指南-服务应用程序(2) 08-05-06 Windows2000 服务器端应用程序开发设计指南-...
4. **进程池线程池**:ACL框架提供了进程池和线程池的管理工具,这有助于减少进程和线程的创建销毁开销,改善系统的响应速度和资源利用率。通过合理调度和复用,可以避免频繁的上下文切换,提升整体系统的运行效率。...
为了满足高并发访问的需求,开发人员常采用多线程技术来构建高效、响应迅速的服务端应用程序。本文将深入探讨多线程服务器的基本原理、线程的基础知识以及如何利用多线程技术来构建并发服务器。 #### 二、线程基础...
"Linux下的多线程服务器程序设计" ...基于Linux下的多线程服务器程序设计方法可以提高服务器的并发能力和响应速度,广泛应用于服务器领域。该方法可以解决传统服务器工作方式的三个缺点,提高服务器的可靠性和稳定性。
多线程是指在一个进程中同时执行多个线程,每个线程都有自己的程序计数器、栈和局部变量,共享全局变量和主内存资源。通过多线程,程序可以同时执行不同的任务,提高系统资源利用率和程序响应速度。在服务器端,多...
### 嵌入式Linux应用程序开发详解:多线程编程 #### 9.1 多线程编程概述 ##### 9.1.1 学习目标 - 掌握Linux中线程的基本概念。 - 掌握Linux中线程的创建及使用。 - 掌握Linux中线程属性的设置。 - 能够独立编写多...
总的来说,基于Qt的多线程并发服务器设计能够充分利用系统资源,为大量并发用户提供服务,是现代网络应用程序开发中的重要技术。理解并掌握这种技术,对于提升服务器性能和用户体验具有重要意义。
在Android应用开发中,框架和多线程下载技术是至关重要的组成部分,特别是在毕业设计或复杂的移动应用程序中。本文将深入探讨这两个主题,并基于提供的压缩包文件"Android应用源码之Android快速框架+多线程下载框架...
在IT领域,多线程服务器和客户端程序是网络编程中的重要组成部分,特别是在使用MFC(Microsoft Foundation Classes)框架的VC++环境中。MFC是微软提供的一套C++库,用于简化Windows应用程序的开发,它提供了丰富的类...
在IT领域,多线程和多客户端编程是构建高性能服务器应用程序的关键技术。在这个场景中,我们探讨的是一个基于C#语言实现的服务器程序,它能够处理来自多个客户端的并发请求,同时利用了C#中的`Dictionary`类进行数据...
在IT领域,多线程和Socket网络编程是两个至关重要的概念,它们对于开发高效、可靠的分布式系统至关重要。这里我们将深入探讨这两个主题。 1. **进程** - **进程的定义**:进程是操作系统中资源分配的基本单位,它...
在多线程编程方面,AMC框架允许用户在同一应用程序中执行多个任务,提高系统的并行处理能力,从而优化资源利用率和整体性能。 1. **AMC框架基础**: - **模块化设计**:AMC框架的核心理念是模块化,这使得代码组织...
在Web开发中,多线程技术能够有效地利用系统资源,特别是在高并发场景下,能够更好地响应用户请求,提供更高效的性能。 【描述】"支持多线程编码"意味着phpstudy不仅是一个运行环境,还提供了对多线程编程的支持。...
总结来说,这个实验涵盖了HTTP基础、多线程Web服务器的实现以及Java RMI的应用,这些都是构建复杂网络应用程序的基础。每个部分都至关重要,理解和掌握它们对于成为一名合格的IT专业人士至关重要。通过实践,你可以...
描述中提到的“libevent 多线程 HTTP post服务器”表明这是一个利用libevent库开发的服务端程序,该程序能够接收并处理来自客户端的HTTP POST请求,并且采用了多线程技术以实现并发处理,提升系统性能。这通常意味着...
服务器客户端-socket(进程线程),包括套接字,多线程,多进程,单进程,并发,互斥锁,tcp/ip,udp等
C#语言开发多线程Socket服务器端程序,实现一个服务器同时与多个客户端连接对话。这里,我们将详细讲解如何使用C#语言开发多线程Socket服务器端程序,实现一个服务器同时与多个客户端连接对话。 多线程Socket服务器...
基于Linux的多线程池并发Web服务器设计 Linux操作系统作为服务器操作系统的主要选择之一,Web服务器是其主要应用领域之一。传统的Web服务器设计基于进程或线程机制,但是面对大量的并发请求时,延迟现象较为明显。...
该文介绍了一个基于进程池的简单HTTP服务器程序,该服务器仅支持GET请求。服务器的主要流程如下: 1. 父进程首先监听指定端口,并创建一个pipe用于父子进程间的通信。 2. 预先创建`PRECHILD`(默认为5)个子进程。 ...
3. JAVA:Java是一种广泛使用的编程语言,尤其适合开发跨平台的应用程序。在本项目中,Java被选为实现多线程OCR服务器的语言,因为它提供了丰富的多线程API,如`java.util.concurrent`包,可以方便地创建和管理并发...