文章《快速建立你的服務器程序》講述了基於 C 語言版本的 acl 服務器框架下如何開發多進程服務器應用程序。本文則講述了基於 C++ 語言的 acl_cpp 服務器框架下如何開發多進程服務器應用程序,固然 acl_cpp 下的服務器框架內部也是基於 acl 的服務器框架的。關於基於acl_master 的服務器程序設計原理,請參考 《協做半駐留式服務器程序開發框架》。html
1、類成員函數說明服務器
master_proc 是一個純虛類,其中定義的接口須要子類實現,以下:併發
/** * 純虛函數:當接收到一個客戶端鏈接時調用此函數 * @param stream {aio_socket_stream*} 新接收到的客戶端異步流對象 * 注:該函數返回後,流鏈接將會被關閉,用戶不該主動關閉該流 */ virtual void on_accept(socket_stream* stream) = 0;
master_proc 類提供了兩個函數:框架
/** * 開始運行,調用該函數是指該服務進程是在 acl_master 服務框架 * 控制之下運行,通常用於生產機狀態 * @param argc {int} 從 main 中傳遞的第一個參數,表示參數個數 * @param argv {char**} 從 main 中傳遞的第二個參數 */ void run_daemon(int argc, char** argv); /** * 在單獨運行時的處理函數,用戶能夠調用此函數進行一些必要的調試工做 * @param addr {const char*} 服務監聽地址 * @param conf {const char*} 配置文件全路徑 * @param count {int} 當該值 > 0 時,則接收的鏈接次數達到此值且完成 * 後,該函數將返回,不然一直循環接收遠程鏈接 * @return {bool} 監聽是否成功 */ bool run_alone(const char* addr, const char* conf = NULL, int count = 1);
master_proc 類實例當在生產環境下(由 acl_master 進程統一控制調度),用戶須要調用 run_daemon 函數;若是用戶在開發過程當中須要手工進行調試,則能夠調用 run_alone 函數。異步
master_proc 的基類 master_base 的幾個虛接口以下:socket
/** * 當進程切換用戶身份前調用的回調函數,能夠在此函數中作一些 * 用戶身份爲 root 的權限操做 */ virtual void proc_pre_jail() {} /** * 當進程切換用戶身份後調用的回調函數,此函數被調用時,進程 * 的權限爲普通受限級別 */ virtual void proc_on_init() {} /** * 當進程退出前調用的回調函數 */ virtual void proc_on_exit() {} // 在 run_alone 狀態下運行前,調用此函數初始化一些配置
2、示例函數
// master_proc.cpp : 定義控制檯應用程序的入口點。 // #include "stdafx.h" #include "lib_acl.hpp" // 字符串類型的配置項 static char *var_cfg_debug_msg; static acl::master_str_tbl var_conf_str_tab[] = { { "debug_msg", "test_msg", &var_cfg_debug_msg }, { 0, 0, 0 } }; // 布爾類型的配置項 static int var_cfg_debug_enable; static acl::master_bool_tbl var_conf_bool_tab[] = { { "debug_enable", 1, &var_cfg_debug_enable }, { 0, 0, 0 } }; // 整數類型的配置項 static int var_cfg_io_timeout; static acl::master_int_tbl var_conf_int_tab[] = { { "io_timeout", 120, &var_cfg_io_timeout, 0, 0 }, { 0, 0 , 0 , 0, 0 } }; static void (*format)(const char*, ...) = acl::log::msg1; ////////////////////////////////////////////////////////////////////////// using namespace acl; class master_proc_test : public master_proc { public: master_proc_test() {} ~master_proc_test() {} protected: /** * 基類純虛函數:當接收到一個客戶端鏈接時調用此函數 * @param stream {aio_socket_stream*} 新接收到的客戶端異步流對象 * 注:該函數返回後,流鏈接將會被關閉,用戶不該主動關閉該流 */ virtual void on_accept(socket_stream* stream) { if (stream->format("hello, you're welcome!\r\n") == -1) return; while (true) { if (on_read(stream) == false) break; } } bool on_read(socket_stream* stream) { string buf; if (stream->gets(buf) == false) // 讀一行數據 { format("gets error: %s", acl::last_serror()); return false; } if (buf == "quit") { stream->puts("bye!"); return false; } if (buf.empty()) { if (stream->write("\r\n") == -1) { format("write 1 error: %s", acl::last_serror()); return false; } } else if (stream->write(buf) == -1) { format("write 2 error: %s, buf(%s), len: %d", acl::last_serror(), buf.c_str(), (int) buf.length()); return false; } else if (stream->write("\r\n") == -1) { format("write 3 client error: %s", acl::last_serror()); return false; } return true; } // 基類虛函數:服務進程切換用戶身份前調用此函數 virtual void proc_pre_jail() { format("proc_pre_jail\r\n"); } // 基類虛函數:服務進程切換用戶身份後調用此函數 virtual void proc_on_init() { format("proc init\r\n"); } // 基類虛函數:服務進程退出前調用此函數 virtual void proc_on_exit() { format("proc exit\r\n"); } private: }; ////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { master_proc_test mp; // 設置配置參數表 mp.set_cfg_int(var_conf_int_tab); mp.set_cfg_int64(NULL); mp.set_cfg_str(var_conf_str_tab); mp.set_cfg_bool(var_conf_bool_tab); // 開始運行 if (argc >= 2 && strcmp(argv[1], "alone") == 0) { format = (void (*)(const char*, ...)) printf; mp.run_alone("127.0.0.1:8888", NULL, 5); // 單獨運行方式 } else mp.run_daemon(argc, argv); // acl_master 控制模式運行 return 0; }
這是一個簡單的提供 echo 行服務的服務器程序,能夠支持多個併發鏈接,並且能夠經過配置文件控制所啓動的最大進程數、進程空閒時間等控制參數,由於 acl 中的服務器框架都是半駐留的,因此既能夠保證運行效率,又可以在空閒釋放系統資源。該例子所在目錄:acl_cpp/samples/master_proc。測試
須要注意的是,master_proc 內部是單例的,即要求該類的對象只能有一個,不然內部自動產生斷言。只因此沒有采用單例模板來設計單例,主要是爲了避免對外暴露 acl 庫中的接口,使使用 acl_cpp 庫的用戶沒必要關心 acl 庫的頭文件在哪兒。ui
3、配置文件及程序安裝spa
打開 acl_cpp/samples/master_proc/single_echo.cf 配置文件,就其中幾個配置參數說明一下:
## 由 acl_master 用來控制服務進程池的配置項 # 爲 no 表示啓用該進程服務,爲 yes 表示禁止該服務進程 master_disable = no # 表示本服務器進程監聽 127.0.0.1 的 5002 端口 master_service = 127.0.0.1:5002 # 表示是 TCP 套接口服務類型 master_type = inet # 進程程序名 master_command = master_proc # 表示該服務進程池的最大進程數爲 2 master_maxproc = 2 # 進程日誌記錄文件,其中 {install_path} 須要用實際的安裝路徑代替 master_log = {install_path}/var/log/single_echo.log
# 每一個進程實例處理鏈接數的最大次數,超過此值後進程實例主動退出 single_use_limit = 250 # 每一個進程實例的空閒超時時間,超過此值後進程實例主動退出 single_idle_limit = 180
例如當 acl_master 服務器框架程序的安裝目錄爲:/opt/acl,則:
/opt/acl/libexec: 該目錄存儲服務器程序(acl_master 程序也存放在該目錄下);
/opt/acl/conf:該目錄存放 acl_master 程序配置文件 main.cf;
/opt/acl/conf/service:該目錄存放服務子進程的程序配置文件,該路徑由 main.cf 文件指定;
/opt/acl/var/log:該目錄存放日誌文件;
/opt/acl/var/pid:該目錄存放進程號文件。
該程序編譯經過後,須要把可執行程序放在 /opt/acl/libexec 目錄下,把配置文件放在 /opt/acl/conf/service 目錄下。
在 /opt/acl/sh 下有啓動/中止 acl_master 服務進程的控制腳本;運行腳本:./start.sh,而後請用下面方法檢查服務是否已經啓動:
ps -ef|grep acl_master # 查看服務器控制進程是否已經啓動
netstat -nap|grep LISTEN|grep 5002 # 查看服務端口號是否已經被監聽
固然,您也能夠查看 /opt/acl/var/log/acl_master 日誌文件,查看服務進程的啓動過程及監聽服務是否正常監聽。
能夠命令行以下測試:telnet 127.0.0.1 5002
QQ 羣:242722074