ACL库简介

ACL库简介

描述

概叙

acl 工程是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及服务器编程框架,同时提供更多的实用功能库。通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式、协程方式)的服务器程序,WEB 应用程序,数据库应用程序。此外,该库还提供了常见应用的客户端通信库(如:HTTP、SMTP、ICMP、redis、memcache、beanstalk、handler socket),常见流式编解码库:XML/JSON/MIME/BASE64/UUCODE/QPCODE/RFC2047 etc。

acl github: https://github.com/acl-dev/acl --(1.8K⭐) acl 库下载:https://gitee.com/zsxxsz/acl/tree/master --(51⭐) acl 文章主页:http://zsxxsz.iteye.com/ demo:https://github.com/acl-dev/demo qq 群:242722074

库组成

本工程主要包含 5 个库及大量示例。5 个库的说明如下:

lib_acl: 用 C 语言写的基础库,其它 4 个库均依赖于该库;lib_protocol: 用 C 语言写的一些网络应用协议库,主要实现了 http 协议及 icmp/ping 协议lib_acl_cpp: 该库用 C++ 语言封装了 lib_acl/lib_protocol 两个库,同时增加了一些其它有价值的功能应用。lib_fiber: 用C 语言编写的支持高性能、高并发的网络协程库lib_dict: 该库主要实现了 KEY-VALUE 的字典式存储库,该库另外还依赖于 BDB, CDB 以及 tokyocabinet 库。lib_tls: 该库封装了 openssl 库,使 lib_acl 的通信模式可以支持 ssl。

功能模块组成

Redis 客户端库

支持 redis 集群模式及非集群模式;支持连接池方式;按 redis 数据结构类型分成独立的 C++ 类;每个命令映射为 1 个至多个函数.

根据 redis 的数据结构类型,分成 12 个大类,每个大类提供不同的函数接口,这 12 个 C++ 类展示如下:

redis_key:redis 所有数据类型的统一键操作类;因为 redis 的数据结构类型都是基本的 KEY-VALUE 类型,其中 VALUE 分为不同的数据结构类型;redis_connectioin:与 redis-server 连接相关的类;redis_server:与 redis-server 服务管理相关的类;redis_string:redis 中用来表示字符串的数据类型;redis_hash:redis 中用来表示哈希表的数据类型;每一个数据对象由 “KEY-域值对集合” 组成,即一个 KEY 对应多个“域值对”,每个“域值对”由一个字段名与字段值组成;redis_list:redis 中用来表示列表的数据类型;redis_set:redis 中用来表示集合的数据类型;redis_zset:redis 中用来表示有序集合的数据类型;redis_pubsub:redis 中用来表示“发布-订阅”的数据类型;redis_hyperloglog:redis 中用来表示 hyperloglog 基数估值算法的数据类型;redis_script:redis 中用来与 lua 脚本进行转换交互的数据类型;redis_transaction:redis 中用以事务方式执行多条 redis 命令的数据类型(注:该事务处理方式与数据库的事务有很大不同,redis 中的事务处理过程没有数据库中的事务回滚机制,仅能保证其中的多条命令都被执行或都不被执行);

除了以上对应于官方 redis 命令的 12 个类别外,在 acl 库中还提供了另外几个类:

redis_command:以上 12 个类的基类;redis_client:redis 客户端网络连接类;redis_result:redis 命令结果类;redis_pool:针对以上所有命令支持连接池方式;redis_manager:针对以上所有命令允许与多个 redis-server 服务建立连接池集群(即与每个 redis-server 建立一个连接池);redis_cluster:支持 redis3.0 集群模式的类。

实践

搭建环境

我的环境: centos 7

Linux/UNIX: 编译器为 gcc,直接在终端命令行方式下分别进入 lib_acl/lib_protocol/lib_acl_cpp 目录下,运行 make 命令即可。

下载

$ git clone http://git.oschina.net/zsxxsz/acl.git

$ cd acl

编译静态库:

进入 lib_acl 目录,直接运行 make(使用 gcc 编译器), 正常情况下便可以在 lib 目录下生成libacl.a 静态库进入 lib_protocol 目录,直接运行 make(使用 gcc 编译器),正常情况下便可以在 lib 目录下生成 lib_protocol.a 静态库进入 lib_acl_cpp 目录,运行make ( 或者make static) 编译 libacl_cpp.a 静态库,便可 lib 目录下生成 libacl_cpp.a 编译动态库:

编译 libacl.so, libprotocol.so, libacl_cpp.so 的方式与编译静态库的方式有所不同,需要分别进入三个目录执行: make shared rpath=$ {lib_path},其中 shared 表示需要编译动态库,${lib_path} 需要用实际的目标路径替换,比如:make shared rpath=/opt/acl/lib,则会将动态库编译好后存放于 /opt/acl/lib 目录因为 lib_acl 是最基础的库,而 lib_protocol 依赖于 lib_acl,lib_acl_cpp 依赖于 lib_protocol 和 lib_acl,所在生成动态库时,需要注意生成顺序,编译顺序为::libacl.so --> libprotocol.so --> libacl_cpp.so。另外,在编译 libacl_cpp.so 时,还需要提前编译在 resource 目录下的 polarssl 库,编译完后再编译 libacl_cpp.so 同时需要指定 polarssl.lib 库所在的路径;如果不需要 SSL 通讯方式,则需要打开 lib_acl_cpp/Makefile 文件,去年编译选项:-DHAS_POLARSSL。

$ cd lib_acl

$ make

build ./lib/libacl.a ok!

creating libacl.so

skip build libacl.so; usage: make shared rpath=xxx

$ make shared rpath=/usr/lib

creating libacl.so

building for linux

build /usr/lib/libacl.so ok!

$ cd lib_protocol

$ make

build ./lib/libprotocol.a ok!

creating libprotocol.so

skip build libprotocol.so; usage: make shared rpath=xxx

$ make shared rpath=/usr/lib

creating libprotocol.so

building for linux

build /usr/lib/libprotocol.so ok!

$ cd lib_acl_cpp

$ make

create ./lib/libacl_cpp.a ok!

$ make shared rpath=/usr/lib

creating libacl_cpp.so

building for linux

build /usr/lib/libacl_cpp.so ok!

编译成单一库

在 acl 库目录下运行:make build_one,则会生成统一库:libacl_all.a 及 libacl_all.so,该库包含了 libacl,lib_protocol,lib_acl_cpp 三个库

$ cd acl

$ make build_one

rm -f libacl.a

ln -s libacl_all.a libacl.a

g++ -shared -o ./libacl_all.so release/acl_cpp/*.o \

release/protocol/*.o release/acl/*.o \

-lpthread -lz -lrt -ldl

rm -f libacl.so

ln -s libacl_all.so libacl.so

Over, libacl_all.a and libacl_all.so were built ok!

使用 cmake 编译

$mkdir build

$cd build

$cmake ..

$make

准备工程

必知

当前使用的是编译成单一库编辑器为clion,创建一个基于c++11的工程, 并且将lib_acl_cpp/include,/lib_acl/include,lib_protocol/include,以及liball_all.so复制到工程中。如下

编译cmakelists.txt

cmake_minimum_required(VERSION 3.16)

project(acl_redis)

set(CMAKE_CXX_STANDARD 11)

add_definitions("-Wall -g")

include_directories(${PROJECT_SOURCE_DIR}/include

${PROJECT_SOURCE_DIR}/include/lib_acl/include

${PROJECT_SOURCE_DIR}/include/lib_acl_cpp/include

${PROJECT_SOURCE_DIR}/include/lib_protocol)

link_directories(${PROJECT_SOURCE_DIR}/lib/acl)

aux_source_directory(. SRC_LIST)

add_executable(${PROJECT_NAME} ${SRC_LIST})

target_link_libraries (${PROJECT_NAME} libacl_all.so)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

使用 acl 库编写高效的 C redis 客户端应用

实践一

main.cpp内容

#include

#include"acl_cpp/lib_acl.hpp"

using namespace std;

int main()

{

acl::redis_string cmd_string;//redis中的string类型

acl::redis_client_cluster cluster;

const char *redis_addr="127.0.0.1:6379";//设置连接的数据库地址

int conn_timeout=10;//连接redis-server的超时时间(秒)

int rw_timeout=10;//与redis-server进行通信IO的超时时间(秒)

int max_threads=100;//最大线程线程数

cluster.set(redis_addr, max_threads, conn_timeout, rw_timeout);

acl::redis_client conn(redis_addr, conn_timeout, rw_timeout);//设置连接的客户端

cmd_string.set_client(&conn);//设置该string类型是连接那个客户端

acl::redis_string string_cmd(&conn);//使用初始化一个string

const char *key="name";

if(!string_cmd.set(key, "Shayne"))//设置一个string 类型的key value

{

const acl::redis_result *res=string_cmd.get_result();

cout<<"false "<

return false;

}

cout<<"connected"<

printf("set key: %s ok\r\n", key);

return true;

}

实践二

#include

#include

#include"acl_cpp/lib_acl.hpp"

using namespace std;

/*

* @param conn {acl::redis_client&} redis 连接对象

* @return {bool} 操作过程是否成功

*/

bool test_redis_string(acl::redis_client& conn, const char* key){

// 创建 redis string 类型的命令操作类对象,同时将连接类对象与操作类对象进行绑定

acl::redis_string string_operation(&conn);

const char* value = "test_value";

// 添加 K-V 值至 redis-server 中

if(!string_operation.set(key, value)){

const acl::redis_result* res = string_operation.get_result();

printf("set key : %s error: %s\r\n",

key, res ? res->get_error(): "unknown error");

return false;

}

printf("set key: %s ok !\r\n", key);

// 需要重置连接对象的状态

string_operation.clear();

// 从 redis-server 中取得对应 key 的值

acl::string buf;

if(!string_operation.get(key, buf)){

const acl::redis_result* res = string_operation.get_result();

printf("get key: %s error: %s\r\n",

key, res ? res->get_error() : "unknown error");

return false;

}

printf("get key: %s ok, value: %s\r\n", key, buf.c_str());

// 探测给定 key 是否存在于 redis-server 中,需要创建 redis 的 key类对象,同时将 redis 连接对象与之绑定

acl::redis_key key_operation;

key_operation.set_client(&conn); // 将连接对象与操作对象进行绑定

if (!key_operation.exists(key)){

if (conn.eof()){

printf("disconnected from redis-server\r\n");

return false;

}

printf("key: %s not exists\r\n", key);

}else{

printf("key: %s exists\r\n", key);

}

// 删除指定 key 的字符串类对象

if (key_operation.del(key) < 0){

printf("del key: %s error\r\n", key);

return false;

}else{

printf("del key: %s ok\r\n", key);

}

return true;

}

/**

* @param redis_addr {const char*} redis-server 服务器地址,

* 格式为:ip:port,如:127.0.0.1:6379

* @param conn_timeout {int} 连接 redis-server 的超时时间(秒)

* @param rw_timeout {int} 与 redis-server 进行通信的 IO 超时时间(秒)

*/

bool test_redis(const char *redis_addr, int conn_timeout, int rw_timeout){

acl::redis_client conn(redis_addr, conn_timeout, rw_timeout);

const char*key = "test_key";

return test_redis_string(conn, key);

}

int main()

{

test_redis("127.0.0.1:6379", 10, 10);

return 0;

}

mysql

参考例子:acl/lib_acl_cpp/samples/mysql 目录

简单使用

main.cpp

// mysql.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include "lib_acl.h"

#include "acl_cpp/lib_acl.hpp"

const char* CREATE_TBL =

"create table group_tbl\r\n"

"(\r\n"

"group_name varchar(128) not null,\r\n"

"uvip_tbl varchar(32) not null default 'uvip_tbl',\r\n"

"access_tbl varchar(32) not null default 'access_tbl',\r\n"

"access_week_tbl varchar(32) not null default 'access_week_tbl',\r\n"

"access_month_tbl varchar(32) not null default 'access_month_tbl',\r\n"

"update_date date not null default '1970-1-1',\r\n"

"disable integer not null default 0,\r\n"

"add_by_hand integer not null default 0,\r\n"

"class_level integer not null default 0,\r\n"

"primary key(group_name, class_level)\r\n"

")";

static bool tbl_create(acl::db_handle& db)

{

if (db.tbl_exists("group_tbl"))

{

printf("table exist\r\n");

return (true);

}

else if (!db.sql_update(CREATE_TBL))

{

printf("sql error\r\n");

return (false);

}

else

{

printf("create table ok\r\n");

return (true);

}

}

// 添加表数据

static bool tbl_insert(acl::db_handle& db, int n)

{

const char* sql_fmt = "insert into group_tbl(group_name, uvip_tbl)"

" values('中国人-%d', 'test')";

acl::string sql;

sql.format(sql_fmt, n);

if (!db.sql_update(sql.c_str()))

return (false);

const acl::db_rows* result = db.get_result();

if (result)

{

const std::vector& rows = result->get_rows();

for (size_t i = 0; i < rows.size(); i++)

{

const acl::db_row* row = rows[i];

for (size_t j = 0; j < row->length(); j++)

printf("%s, ", (*row)[j]);

printf("\r\n");

}

}

db.free_result();

return (true);

}

// 查询表数据

static int tbl_select(acl::db_handle& db, int n)

{

const char* sql_fmt = "select * from group_tbl where"

" group_name='中国人-%d' and uvip_tbl='test'";

acl::string sql;

sql.format(sql_fmt, n);

if (!db.sql_select(sql.c_str()))

{

printf("select sql error\r\n");

return (-1);

}

printf("\r\n---------------------------------------------------\r\n");

// 列出查询结果方法一

const acl::db_rows* result = db.get_result();

if (result)

{

const std::vector& rows = result->get_rows();

for (size_t i = 0; i < rows.size(); i++)

{

if (n > 100)

continue;

const acl::db_row* row = rows[i];

for (size_t j = 0; j < row->length(); j++)

printf("%s, ", (*row)[j]);

printf("\r\n");

}

}

// 列出查询结果方法二

for (size_t i = 0; i < db.length(); i++)

{

if (n > 100)

continue;

const acl::db_row* row = db[i];

// 取出该行记录中某个字段的值

const char* ptr = (*row)["group_name"];

if (ptr == NULL)

{

printf("error, no group name\r\n");

continue;

}

printf("group_name=%s: ", ptr);

for (size_t j = 0; j < row->length(); j++)

printf("%s, ", (*row)[j]);

printf("\r\n");

}

// 列出查询结果方法三

const std::vector* rows = db.get_rows();

if (rows)

{

std::vector::const_iterator cit = rows->begin();

for (; cit != rows->end(); cit++)

{

if (n > 100)

continue;

const acl::db_row* row = *cit;

for (size_t j = 0; j < row->length(); j++)

printf("%s, ", (*row)[j]);

printf("\r\n");

}

}

int ret = (int) db.length();

// 释放查询结果

db.free_result();

return (ret);

}

// 删除表数据

static bool tbl_delete(acl::db_handle& db, int n)

{

const char* sql_fmt = "delete from group_tbl where group_name='中国人-%d'";

acl::string sql;

sql.format(sql_fmt, n);

if (!db.sql_update(sql.c_str()))

{

printf("delete sql error\r\n");

return (false);

}

for (size_t i = 0; i < db.length(); i++)

{

const acl::db_row* row = db[i];

for (size_t j = 0; j < row->length(); j++)

printf("%s, ", (*row)[j]);

printf("\r\n");

}

// 释放查询结果

db.free_result();

return (true);

}

int main(void)

{

acl::log::stdout_open(true); // 允许将错误日志输出至屏幕

acl::string line;

acl::stdin_stream in;

acl::stdout_stream out;

acl::db_handle::set_loadpath("/usr/lib64/mysql/libmysqlclient_r.so"); // 设置动态库加载的全路径

const char* dbaddr = "192.168.0.21:3306";

const char* dbname = "city";

const char* dbuser = "root", *dbpass = "123456";

acl::db_mysql db(dbaddr, dbname, dbuser, dbpass);

int max = 100;

// 允许将错误日志输出至屏幕

acl_msg_stdout_enable(1);

if (!db.open())

{

printf("open db(%s) error\r\n", dbname);

getchar();

return 1;

}

printf("open db %s ok\r\n", dbname);

if (!tbl_create(db))

{

printf("create table error\r\n");

getchar();

return 1;

}

ACL_METER_TIME("---begin insert---");

for (int i = 0; i < max; i++)

{

bool ret = tbl_insert(db, i);

if (ret)

printf(">>insert ok: i=%d, affected: %d\r",i, db.affect_count());

else

printf(">>insert error: i = %d\r\n", i);

}

printf("\r\n");

ACL_METER_TIME("---end insert---");

ACL_METER_TIME("---begin select---");

int n = 0;

for (int i = 0; i < max; i++)

{

int ret = tbl_select(db, i);

if (ret >= 0)

{

n += ret;

printf(">>select ok: i=%d, ret=%d\r", i, ret);

}

else

printf(">>select error: i = %d\r\n", i);

}

printf("\r\n");

printf(">>select total: %d\r\n", n);

ACL_METER_TIME("---end select---");

ACL_METER_TIME("---begin delete---");

for (int i = 0; i < max; i++)

{

bool ret = tbl_delete(db, i);

if (ret)

printf(">>delete ok: %d, affected: %d\r",

i, (int) db.affect_count());

else

printf(">>delete error: i = %d\r\n", i);

}

printf("\r\n");

printf("mysqlclient lib's version: %ld, info: %s\r\n", db.mysql_libversion(), db.mysql_client_info());

ACL_METER_TIME("---end delete---");

printf("Enter any key to exit.\r\n");

getchar();

return 0;

}

数据库连接池

int main(void)

{

acl::db_handle::set_loadpath("/usr/lib64/mysql/libmysqlclient_r.so"); // 设置动态库加载的全路径

const char* dbaddr = "192.168.0.21:3306";

const char* dbname = "city";

const char* dbuser = "root", *dbpass = "123456";

acl::db_pool* dbpool = new acl::mysql_pool(dbaddr, dbname, dbuser, dbpass); // 创建 mysql 连接池

acl::db_handle* dbhandle = dbpool->peek_open(); // 从连接池中获取一个数据库连接

if (dbhandle == nullptr)

{

printf("peek db connection error\r\n");

delete dbhandle;

return 0;

}

tbl_create(*dbhandle);

tbl_insert(*dbhandle, 6);

tbl_select(*dbhandle, 6);

tbl_delete(*dbhandle, 6);

dbpool->put(dbhandle); // 归还数据库连接给连接池

delete dbpool; // 删除连接池对象

return 0;

}

源码分析

初始化lib_acl/sample/acl

#include

#include

struct ACL_VSTREAM {

};

static char *version = "3.5.1-5 20200627";

typedef struct ACL_VSTREAM ACL_VSTREAM;

static pthread_t acl_var_main_tid = (pthread_t) -1;

const char *acl_version();

unsigned long acl_main_thread_self();

int main()

{

printf("current acl version: %s\r\n", acl_version());

printf("ACL_VSTREAM's size: %d\r\n", (int) sizeof(ACL_VSTREAM));

printf("main tid: %lu\r\n", acl_main_thread_self());

printf("tid: %lu\r\n", (unsigned long) pthread_self());

return 0;

}

unsigned long acl_main_thread_self(){

return ((unsigned long) acl_var_main_tid);

}

const char *acl_version()

{

return version;

}

socket

服务端1

#include "acl_cpp/lib_acl.hpp"

#include

int main(int argc, char* argv[])

{

acl::server_socket server;

const char * addr = "127.0.0.1:9001";

acl::acl_cpp_init();

if (!server.open(addr))

{

printf("open %s error %s\r\n", addr, acl::last_serror());

return 1;

}else

printf("open %s ok\r\n", addr);

while (true)

{

acl::socket_stream* client = server.accept(); //阻塞,直到有连接到了(接收客户端连接并创建客户端连接流)

if (client == NULL)

{

printf("accept failed: %s\r\n", acl::last_serror());

break;

}

acl::string buf;

if (!client->gets(buf))

{

printf("gets error, status: %s\r\n", acl::last_serror());

delete client;

continue;

}

printf("gets: %s, length: %zu\r\n", buf.c_str(), buf.length());

delete client;

printf("close client ok\r\n");

}

return (0);

}

客户端1

#include "acl_cpp/lib_acl.hpp"

#include "lib_acl.h"

#include

#include

int main(int argc, char* argv[])

{

acl::socket_stream client;

const char * addr = "127.0.0.1:9001";

acl::acl_cpp_init();

// 连接远程服务器并打开网络连接流

if (!client.open(addr, 0, 0)){

printf("open %s error %s\n", addr, acl::last_serror());

return 1;

}else{

printf("open %s ok\r\n", addr);

}

const char * req = "echo1\r\n"

"echo2\r\0"

"echo3\r\n"

"read_delay1\r\n"

"read_delay2\r\n"

"read_delay3\r\n"

"read_delay4\r\n"

"read_delay5\r\n"

"quit\r\n";

// 探测连接是否正常

if (client.alive())

printf("check: ok, status: %s\r\n", acl::last_serror());

else

printf("check: disconnected, status: %s\r\n", acl::last_serror());

if (client.write(req, 100) == -1)

{

printf("1, write error\r\n");

return -1;

}

return (0);

}

相关推荐

合作伙伴