一、为什么需要流式编程
首先解释一下什么是流式编程,所谓流式编程是指编程接口可以接收部分输入数据,边接收边处理,不必等待完整数据。这好比 TCP 数据流,因为网络传输的因素,每次读操作并不能保证会获得完整的数据块,每次仅读取一部分数据,多次读取才可能会读到一块完整的数据,基于 TCP 的应用服务也比较多,象 HTTP/SMTP/POP 等服务都是基于 TCP 传输协议进行数据传输的,因为 TCP 流的流式特点,所以这些应用都定义了数据完整性的规则。正因为有了这些数据完整性规则,才使得编程变得简单,因此很多实现都是根据这些完整性原则在读取了完整数据后才进行处理。
流式编程因为允许每次输入的数据仅是一部分,甚至可能只有一个字节,所以就需要维护一个数据流的处理状态(内部可能会有很多标志位和缓冲区),一般会采用有限状态机的方法进行编程(象知名的 telnet 服务就是采用了有限状态机的编程思想)。
既然可以把基于 TCP 流的应用协议根据具体应用的数据完整性规则转换为非流式编程,那为什么还需要采用复杂的流式编程呢?原因就是为了适应数据流的多样性以及应用的复杂性。采用流式编程方式,既可以应用于非阻塞 IO,又可以应用于阻塞 IO,而如果采用非流式编程,则一般仅能采用阻塞 IO 编程了;同时,采用流式编程,还可以非常容易使数据流以管道的方式从一个流式编程接口输给下一个流式编程接口,实现流水线式的数据处理过程。
二、acl_cpp 中流式编程接口的设计
acl_cpp 中为了处理流式数据,设计了两种类:流式数据处理器(简称流式处理器)和流式处理器管理器(流式管理器)。流式处理器类中,定义了一个虚接口,规定了子类为了实现某一具体应用的流式处理功能而必须遵循的接口;流式管理器,负责管理流式处理器,将这些流式处理器组成一个数据流管道,接收用户的数据输入,将数据流从一个管道(即流式处理器)传向另一个管道。
1、流式处理器类
pipe_stream:流式处理器基类,是一个纯虚类。继承该类的子类必须实现三个虚方法中的两个纯虚接口:push_pop,pop_end,子类可根据需要实现另一个虚方法:clear。
1.1、push_pop 纯虚接口定义:
/** * 数据输入输出接口 * @param in {const char*} 输入数据的地址 * @param len {size_t} 输入数据长度 * @param out {string*} 存储输出结果缓冲区,不能为空 * @param max {size_t} 希望接收到输出结果的长度限制,如果为0则 * 表示没有限制,输出结果都存储在 out 缓冲区中 * @return {int} 输出数据的长度,如果 < 0 则表示出错 */ virtual int push_pop(const char* in, size_t len, string* out, size_t max = 0) = 0;
该接口接收外部数据流,同时将中间处理结果输出。
1.2、pop_end 纯虚接口定义:
/** * 最后处理的输出数据接口 * @param out {string*} 存储输出结果缓冲区,不能为空 * @param max {size_t} 希望接收到输出结果的长度限制,如果为0则 * 表示没有限制,输出结果都存储在 out 缓冲区中 * @return {int} 输出数据的长度,如果 < 0 则表示出错 */ virtual int pop_end(string* out, size_t max = 0) = 0;
当数据流结束时(即已经读到了完整的数据时),子类必须实现该接口,将自身缓冲区里的数据处理后输出给调用者。
2、流式管理器类
pipe_manager:流式处理器类的管理器类。该类主要定义并实现了四个方法:push_back,push_front,update,update_end。
2.1 push_back/push_front 方法:
/** * 以尾部添加的方式注册新的管道流处理器 * @param stream {pipe_stream*} 管道流处理器对象 * @return {bool} 如果该管道流处理器对象已经存在则返回 false */ bool push_back(pipe_stream* stream); /** * 以头部添加的方式注册新的管道流处理器 * @param stream {pipe_stream*} 管道流处理器对象 * @return {bool} 如果该管道流处理器对象已经存在则返回 false */ bool push_front(pipe_stream* stream);
这两个方法分别以尾部或头部添加的方式,将流式处理器加入到流式管道中,形成数据流处理的管道。需要注意的是:添加的流式处理器对象必须在 pipe_manager 的类实例作用域内依然有效,pipe_manager 流式管理器实例并不负责流式处理器对象的销毁,如果这些流式处理器是动态创建的,用户应该负责对象销毁。
2.2、update/update_end 方法:
/** * 应用向管道流管理器添加新数据,由该管理器依次传递给所有已注册管道流 * 处理器,同时从已注册管道流处理器接收处理结果,依次传递给下一个 * @param src {const char*} 待处理的数据地址 * @param len {size_t} src 数据长度 * @param out {pipe_stream*} 如果非空,则该管道处理器将是最后一个只接收 * 输入而不进行输出的管道处理器 * @return {bool} 是否有错误发生 */ bool update(const char* src, size_t len, pipe_stream* out = NULL); /** * 最后必须调用一次该函数,以使有些管道的缓冲区里的数据可以一次性地 * 刷新至最后的管道中 * @param out {pipe_stream*} 如果非空,则该管道处理器将是最后一个只接收 * 输入而不进行输出的管道处理器 * @return {bool} 是否有错误发生 */ bool update_end(pipe_stream* out = NULL);
这两个方法提供了数据输入及数据输出的方法,允许用户每次仅输入部分数据,流式管理器内部会自动将数据流在各个流式处理器之间进行传递;当用户确定数据完整时,应该调用 update_end 将管道流中可能存在的最后结果数据取出。
三、示例
以 HTTP 应用为例,客户端在接收服务器响应数据时,假设数据是采用 utf-8 字符集的 xml 数据格式,同时对数据进行了压缩处理。则客户端接收到数据后,如果将接收到数据转换为GBK字符集后再提取数据字段,处理顺序为:解压缩->字符集转换->xml解析,然后才提取出需要的数据字段。如果这三个处理过程都提供了流式接口,则要方便得多,我们只需将数据输入一个流式处理器,然后提取中间处理结果,再将中间处理结果输入到另一个流式处理器即可。
下面列出了实现上述功能的示例代码:
#include "lib_acl.hpp" bool http_get(acl::istream& in) { // 初始化解压库 acl::zlib_stream zlib; if (zlib.pipe_unzip_begin() = false) { printf("初始化解压库失败\r\n"); return false; } // 初始化字符集转码库 acl::charset_conv utf8ToGbk; if (utf8ToGbk.update_begin("utf-8", "gbk") == false) { printf("初始化字符集转码库失败\r\n"); return false; } acl::xml xml; // xml 数据解析器 acl::pipe_manager manager; // 流式管理器 // 以尾部添加方式分别添加:解压流式处理器、字符集转码处理器以及 xml 解析处理器, // 从而使处理管理流的处理方向为:解压处理->字符集转码处理->xml解析处理 manager.push_back(&zlib); manager.push_back(&utf8ToGbk); manager.push_back(&xml); // 循环读取数据流,进行处理 char buf[4096]; int ret; while (true) { ret = in.read(buf, sizeof(buf) - 1, false); // 读取部分数据 if (ret == -1) break; buf[ret] = 0; if (manager.update(buf, ret) == false) // 输入数据至流式管理器 { printf("流式处理器内部出错\r\n"); return false; } } if (manager.update_end() == false) // 处理最后一部分数据 { printf("流式处理器内部出错\r\n"); return false; } // 假设完整数据为: // <users><user name="zsx1" age="1" /><user name="zsx2" age="2" /></users> // 想要提取名字为 zsx2 的 age 字段,则可如下处理: // 提取符合 users/user 方式的 xml 结点对象集合 const std::vector<acl::xml_node*>& users = xml.getElementsByTags("users/user"); if (users.empty()) { printf("zsx2 未发现\r\n"); return false; } // 遍历查询结果集,找颞骨 zsx2 的 xml 结点 std::vector<acl::xml_node*>::const_iterator cit = users.begin(); for (; cit != users.end(); ++cit) { // 提取 name 属性值 const char* user = (*cit)->attr_value("name"); if (strcasecmp(user, "zsx2") != 0) continue; // 提取 age 属性值 const char* age = (*cit)->attr_value("age"); if (age == NULL) continue; printf("zsx2's age: %s\r\n", age); return true; } printf("zsx2's age not found\r\n"); return false; }
以上是一个简单的流式编程的示例,所有符合流式编程规则的处理器类不仅可以组合起来,由 pipe_manager 管理器统一管理,而且也可以单独使用。
四、acl_cpp 库中支持流式编程的流式处理器类
在 acl_cpp 库中能够支持流式处理功能的类有:
4.1、可以和 pipe_manager 流式管理器配合的流式处理器
pipe_string:字符串处理双向管理流;
xml:xml 数据格式流式解析处理器;
json:json 数据格式流式解析处理器;
mime_code/mime_base64/mime_uucode/mime_xxcode/mime_quoted_printable:邮件数据格式编码/解码的流式处理器;
charset_conv:字符集转码处理器;
zlib_stream:流式压缩/解压处理器;
ostream:IO 输出流处理器。
4.2、暂时不能与 pipe_manager 流式管理器配合的流式处理器
mime:邮件 mime 数据的流式解析处理器;
rfc2047:邮件 mime rfc2047 编码的流式解析处理器。
本文链接地址:http://zsxxsz.iteye.com/blog/1566188
github: https://github.com/acl-dev/aclQQ 群:242722074
相关推荐
acl_cpp(已包含在acl项目中:https://sourceforge.net/projects/acl/,请从acl项目url下载)是acl的c ++包装库,并且acl_cpp比...使用acl_cpp,您将获得更强大的acl功能,并且可以快速开发,进行模块编程,祝您好运!
acl_cpp(已包含在acl项目:https://sourceforge.net/projects/acl/,请从acl项目url下载)是acl的c++封装库,acl_cpp比acl有很多有用的功能...有了acl_cpp,你会得到更强大的acl功能,快速开发,模块编程,祝你好运!
redis-acl\lib_acl_cpp\samples\redis路径下,把lib_acl_vc2010d.lib、lib_acl_cpp_vc2010d.lib放到 \redis-acl\lib_acl_cpp\samples\redis\redisclient路径下: 依赖的库 需要下载开源 源码编出来 : svn://svn....
acl_cpp 是基于 acl 库的 C++ 库,包括 MIIME 解析、Handlersocket 客户端库、数据库连接池(支持mysql/sqlite)、WEB 编程、数据库编程、阻塞/非阻塞数据流等内容。
CISCO_ACL__思科_访问控制列表
ACL(Access Control List)框架库是一个专为C语言设计的开源库,它的核心目标是提供一个高效且灵活的服务器开发框架。这个库包含了多种关键组件,使得开发者在构建网络服务时能够快速、稳定地实现复杂的功能。以下...
今天我们要探讨的是"Acl_pong-master"项目,这是一款以乒乓球为主题的简单游戏,让我们一起深入了解其背后的编程技术和设计理念。 首先,"Acl_pong-master"这个名字暗示了这是一个与访问控制列表(Access Control ...
在这个“acl.zip_ACL_权限控制”的压缩包中,我们有四个PHP文件:p2.php、acl_1.php、p3.php和p1.php。这些文件很可能是用来演示如何通过HTTP GET请求参数实现简单的ACL权限控制。下面将详细解释ACL的概念以及如何在...
Python库的设计通常遵循模块化原则,因此`acl_iitbbs`可能包含多个子模块,每个子模块都有特定的功能,如登录认证、帖子检索、分类浏览、用户管理等。为了深入了解这个库,我们需要查看其文档或源代码,了解它的API...
code_for_ACL_2020_paper_FLAT_Chinese_NER_Using_F_Flat-Lattice-Transformer
在这个“ACL_Agent_jade_jade_agent_jade_java”主题中,我们将探讨如何在JADE上构建和配置agent,以及它们如何使用ACL进行通信。 首先,了解JADE的基本结构是至关重要的。JADE由几个核心组件组成,包括:Agent容器...
整个 acl 项目主要包含三个函数库:lib_acl(纯C开发的基础库,主要包含网络通信及服务器编程框架以及其它丰富的功能)、lib_protocol(包含 HTTP/PING/SMTP 通信协议的C语言实现)、lib_acl_cpp(基于 lib_acl 及 lib_...
acl_mysql_util是一个针对MySQL数据库和ACL(Access Control List,访问控制列表)的实用工具库,它为开发者提供了方便的接口,以便在应用中更高效、更安全地进行数据操作。这个库是基于MySQL的原生接口进行封装,...
通常,这样的代码会包含如`acl_get_file()`, `acl_set_file()`, `acl_init()`, `acl_delete_def_file()`等函数,用于获取、设置、初始化和删除默认ACL。此外,还可能有处理错误的函数,以及与系统调用交互的接口。 ...
本压缩包文件"luyouqi.rar_ACL_路由器"提供了关于标准ACL配置的详细资料,旨在帮助用户理解和实施路由器的ACL设置。 首先,我们来看"标准ACL"的概念。标准ACL是基于源IP地址进行过滤的规则集合,允许或拒绝特定网络...
综上所述,"ACL_MAIN"涉及的核心知识点包括JavaScript编程、访问控制列表的原理和实现、Web应用的中间件机制以及可能的数据结构和算法。具体的实现细节将依赖于`acl_main.js`的源码内容,包括如何定义用户、角色、...
在IT行业中,访问控制列表(Access Control List,简称ACL)是一种重要的安全机制,它用于管理对资源的访问权限。QEMU,全称Quick Emulator,是一个功能强大的开源虚拟化软件,能够模拟各种CPU架构和硬件环境。在...
2. `acl_get_entry()`: 此函数用于获取ACL中的特定条目,可能用于读取已有的权限设置。 3. `acl_set_entry()`: 这个函数用来修改ACL中的条目,比如更新特定用户的权限。 4. `acl_add_entry()`: 添加新的ACL条目,...
在`Zend_Acl`中,资源可以通过实现`Zend_Acl_Resource_Interface`接口或继承`Zend_Acl_Resource`类来创建。 - **实现`Zend_Acl_Resource_Interface`接口**:实现该接口需要定义一个`getResourceId()`方法,用于...