之前写过几篇如何使用 acl 库来实现 HTTP 客户端的例子都是基于 C 语言(使用 acl 较为底层的 HTTP 协议库写 HTTP 下载客户端举例, 使用 acl 库开发一个 HTTP 下载客户端),其实在 acl 的 C++ 库(lib_acl_cpp) 中 HTTP 类功能更为强大,本节将介绍如何使用 acl::http_request 类来写一些简单的 HTTP 客户端示例。
一、 acl::http_request 类的一些常用接口
该 HTTP 请求类有两个构造函数,如下 :
/** * 构造函数:通过该构造函数传入的 socket_stream 流对象并 * 不会被关闭,需要调用者自己关闭 * @param client {socket_stream*} 数据连接流,非空, * 在本类对象被销毁时该流对象并不会被销毁,所以用户需自行释放 * @param conn_timeout {int} 如果传入的流关闭,则内部会 * 自动重试,此时需要该值表示连接服务器的超时时间(秒), * 至于重连流的 IO 读写超时时间是从 输入的流中继承的 * @param unzip {bool} 是否对服务器响应的数据自动进行解压 * 注:当该类实例被多次使用时,用户应该在每次调用前调用 * request_header::http_header::reset() */ http_request(socket_stream* client, int conn_timeout = 60, bool unzip = true); /** * 构造函数:该构造函数内部创建的 socket_stream 流会自行关闭 * @param addr {const char*} WEB 服务器地址 * @param conn_timeout {int} 远程连接服务器超时时间(秒) * @param rw_timeout {int} IO 读写超时时间(秒) * @param unzip {bool} 是否对服务器响应的数据自动进行解压 */ http_request(const char* addr, int conn_timeout = 60, int rw_timeout = 60, bool unzip = true);
第一个是以已经连接成功的套接字流为参数的构造函数,该构造函数把连接 HTTP 服务器的工作交给用户来完成;第二个是以 HTTP 服务器地址为参数的构造函数,使用该构造函数,则该类对象内部会自动连接 HTTP 服务器。
下面的几个函数接口与 HTTP 发送相关:
/** * 获得 HTTP 请求头对象,然后在返回的 HTTP 请求头对象中添加 * 自己的请求头字段或 http_header::reset()重置请求头状态, * 参考:http_header 类 * @return {http_header&} */ http_header& request_header(void); /** * 向 HTTP 服务器发送 HTTP 请求头及 HTTP 请求体,同时从 * HTTP 服务器读取 HTTP 响应头,对于长连接,当连接中断时 * 会再重试一次,在调用下面的几个 get_body 函数前必须先 * 调用本函数(或调用 write_head/write_body); * 正常情况下,该函数在发送完请求数据后会读 HTTP 响应头, * 所以用户在本函数返回 true 后可以调用:get_body() 或 * http_request::get_clinet()->read_body(char*, size_t) * 继续读 HTTP 响应的数据体 * @param data {const void*} 发送的数据体地址,非空时自动按 * POST 方法发送,否则按 GET 方法发送 * @param len {size_} data 非空时指定 data 数据长度 * @return {bool} 发送请求数据及读 HTTP 响应头数据是否成功 */ bool request(const void* data, size_t len); /** * 当采用流式写数据时,需要首先调用本函数发送 HTTP 请求头 * @return {bool} 是否成功,如果成功才可以继续调用 write_body */ bool write_head(); /** * 当采用流式写数据时,在调用 write_head 后,可以循环调用本函数 * 发送 HTTP 请求体数据;当输入的两个参数为空值时则表示数据写完; * 当发送完数据后,该函数内部会自动读取 HTTP 响应头数据,用户可 * 继续调用 get_body/read_body 获取 HTTP 响应体数据 * @param data {const void*} 数据地址指针,当该值为空指针时表示 * 数据发送完毕 * @param len {size_t} data 非空指针时表示数据长度 * @return {bool} 发送数据体是否成功 * 注:当应用发送完数据后,必须再调用一次本函数,同时将两个参数都赋空 */ bool write_body(const void* data, size_t len);
构建及发送 HTTP 请求的过程如下:
1、使用两个构造函数之一创建 acl::http_request 请求对象
2、调用 http_request::request_header 获得 HTTP 请求头对象的引用(http_header&),然后对该 HTTP 请求头设置 HTTP 请求的参数
3、http_request 类提供了两种 HTTP 请求调用 方式:
3.1、当 HTTP 请求方法为 HTTP GET 方法或为 HTTP POST 但数据体可以一次性写入时,可以使用 http_request::request 方法,在调用 http_request::request 时会将 HTTP 请求头及请求体一次性发给 HTTP 服务器;
3.2 如果为 HTTP POST 请求方法,且 HTTP 数据体内容是流式的(即每次只是要发送部分数据),则应该使用 http_request::write_head 和 http_request::write_body 两个函数,即使用流式方式发送数据时,应首先调用 http_request::write_head 发送 HTTP 请求头,当该函数返回成功后,可以循环调用 http_request::write_body 来发送 HTTP 请求数据体,为了表示 HTTP 请求体数据完毕,必须最后调用一次 http_request::write_body 且两个参数为 0 时以表示数据体发送完毕。
在调用以上 3.1 或 3.2 过程成功发送完 HTTP 请求数据后,这两个过程内部会自动读取 HTTP 服务器发来的 HTTP 响应头。
在上面的步骤 2 获得 HTTP 请求头对象(http_header)后,应该先调用下面的方法设置 HTTP 请求头中的参数:
/** * 设置请求的 URL,url 格式示例如下: * 1、http://www.test.com/ * 2、/cgi-bin/test.cgi * 3、http://www.test.com/cgi-bin/test.cgi * 3、http://www.test.com/cgi-bin/test.cgi?name=value * 4、/cgi-bin/test.cgi?name=value * 5、http://www.test.com * 如果该 url 中有主机字段,则内部自动添加主机; * 如果该 url 中有参数字段,则内部自动进行处理并调用 add_param 方法; * 调用该函数后用户仍可以调用 add_param 等函数添加其它参数; * 当参数字段只有参数名没有参数值时,该参数将会被忽略,所以如果想 * 单独添加参数名,应该调用 add_param 方法来添加 * @param url {const char*} 请求的 url,非空指针 * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& set_url(const char* url); /** * 设置 HTTP 请求头的 HOST 字段 * @param value {const char*} 请求头的 HOST 字段值 * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& set_host(const char* value); /** * 向请求的 URL 中添加参数对,当只有参数名没有参数值时则: * 1、参数名非空串,但参数值为空指针,则 URL 参数中只有:{name} * 2、参数名非空串,但参数值为空串,则 URL参数中为:{name}= * @param name {const char*} 参数名,不能为空指针 * @param value {const char*} 参数值,当为空指针时,仅添加参数名, * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& add_param(const char* name, const char* value); http_header& add_int(const char* name, short value); http_header& add_int(const char* name, int value); http_header& add_int(const char* name, long value); http_header& add_int(const char* name, unsigned short value); http_header& add_int(const char* name, unsigned int value); http_header& add_int(const char* name, unsigned long value); http_header& add_format(const char* name, const char* fmt, ...) ACL_CPP_PRINTF(3, 4); /** * 向 HTTP 头中添加 cookie * @param name {const char*} cookie 名 * @param value {const char*} cookie 值 * @param domain {const char*} 所属域 * @param path {const char*} 存储路径 * @param expires {time_t} 过期时间,当该值为 0 时表示不过期, * > 0 时,则从现在起再增加 expires 即为过期时间,单位为秒 * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& add_cookie(const char* name, const char* value, const char* domain = NULL, const char* path = NULL, time_t expires = 0); /** * 设置 HTTP 头中的 Connection 字段,是否保持长连接 * 不过,目前并未真正支持长连接,即使设置了该标志位, * 则得到响应数据后也会主动关闭连接 * @param on {bool} 是否保持长连接 * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& set_keep_alive(bool on); /** * 设置 HTTP 头中的 Content-Length 字段 * @param n {long long int} 设置值 * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& set_content_length(long long int n); /** * 设置 HTTP 头中的 Content-Type 字段 * @param value {const char*} 设置值 * @return {http_header&} 返回本对象的引用,便于用户连续操作 */ http_header& set_content_type(const char* value);
以上仅列出了 http_header 类设置 HTTP 请求参数的一些常用方法,其它的方法请参考 http_header.hpp 头文件中的说明。
二、acl::http_request 类获得 HTTP 服务器响应数据的常用方法
上面介绍了使用 acl::http_request 构建 HTTP 请求头及发送请求的接口方法,下面介绍使用 acl::http_request 类中的方法来接收 HTTP 服务器响应过程,在调用 http_request 类中的 request 或 write_body 成功发送完请求数据后,该类对象在这两个方法内部会首先自动接收 HTTP 服务器的响应头数据,若接收过程失败,这两个方法也会返回 false 表示失败,若返回成功,则可以调用 http_request 类对象的 http_status 方法获得 HTTP 服务器的响应状态码(2xx, 3xx, 4xx, 5xx),还可调用 body_length 方法获得 HTTP 响应数据体的长度(当 HTTP 服务器返回的数据格式为 HTTP 块传输时,该函数会返回 -1,所以一般不用显示调用该方法)。下面介绍了主要的与 HTTP 响应相关的方法:
首先是与 HTTP 响应头相关的接口函数,如下:
/** * 当发送完请求数据后,内部会自动调用读 HTTP 响应头过程,可以通过此函数获得服务端 * 响应的 HTTP 状态字(2xx, 3xx, 4xx, 5xx); * 其实该函数内部只是调用了 http_client::response_status 方法 * @return {int} */ int http_status() const; /** * 获得 HTTP 响应的数据体长度 * @return {int64) 返回值若为 -1 则表明 HTTP 头不存在或没有长度字段 */ #ifdef WIN32 __int64 body_length(void) const; #else long long int body_length(void) const; #endif /** * HTTP 数据流(响应流是否允许保持长连接) * @return {bool} */ bool keep_alive(void) const; /** * 获得 HTTP 响应头中某个字段名的字段值 * @param name {const char*} 字段名 * @return {const char*} 字段值,为空时表示不存在 */ const char* header_value(const char* name) const; /** * 获得服务器返回的 Set-Cookie 设置的某个 cookie 对象 * @param name {const char*} cookie 名 * @param case_insensitive {bool} 是否区分大小写,true 表示 * 不区分大小写 * @return {const HttpCookie*} 返回 NULL 表示不存在 */ const HttpCookie* get_cookie(const char* name, bool case_insensitive = true) const;
然后是与读 HTTP 响应数据体相关的接口函数:
/** * 是否读完了数据体 * @return {bool} */ bool body_finish() const; /** * 当调用 request 成功后调用本函数,读取服务器响应体数据 * 并将结果存储于规定的 xml 对象中 * @param out {xml&} HTTP 响应体数据存储于该 xml 对象中 * @param to_charset {const char*} 当该项非空,内部自动 * 将数据转成该字符集存储于 xml 对象中 * @return {bool} 读数据是否成功 * 注:当响应数据体特别大时不应用此函数,以免内存耗光 */ bool get_body(xml& out, const char* to_charset = NULL); /** * 当调用 request 成功后调用本函数,读取服务器响应体数据 * 并将结果存储于规定的 json 对象中 * @param out {json&} HTTP 响应体数据存储于该 json 对象中 * @param to_charset {const char*} 当该项非空,内部自动 * 将数据转成该字符集存储于 json 对象中 * @return {bool} 读数据是否成功 * 注:当响应数据体特别大时不应用此函数,以免内存耗光 */ bool get_body(json& out, const char* to_charset = NULL); /* * 当调用 request 成功后调用本函数,读取服务器全部响应数据 * 存储于输入的缓冲区中 * @param out {string&} 存储响应数据体 * @param to_charset {const char*} 当该项非空,内部自动 * 将数据转成该字符集存储于 out 对象中 * 注:当响应数据体特别大时不应用此函数,以免内存耗光 */ bool get_body(string& out, const char* to_charset = NULL); /* * 当调用 request 成功后调用本函数,读取服务器响应数据并 * 存储于输入的缓冲区中,可以循环调用本函数,直至数据读完了, * @param buf {char*} 存储部分响应数据体 * @param size {size_t} buf 缓冲区大小 * @return {int} 返回值 == 0 表示正常读完毕,< 0 表示服务器 * 关闭连接,> 0 表示已经读到的数据,用户应该一直读数据直到 * 返回值 <= 0 为止 * 注:该函数读到的是原始 HTTP 数据体数据,不做解压和字符集 * 解码,用户自己根据需要进行处理 */ int read_body(char* buf, size_t size); /** * 当调用 request 成功后调用本函数读 HTTP 响应数据体,可以循环调用 * 本函数,本函数内部自动对压缩数据进行解压,如果在调用本函数之前调用 * set_charset 设置了本地字符集,则还同时对数据进行字符集转码操作 * @param out {string&} 存储结果数据 * @param clean {bool} 每次调用本函数时,是否要求先自动将缓冲区 out * 的数据清空 * @param real_size {int*} 当该指针非空时,存储解压前读到的真正数据 * 长度,如果在构造函数中指定了非自动解压模式且读到的数据 > 0,则该 * 值存储的长度值应该与本函数返回值相同;当读出错或未读到任何数据时, * 该值存储的长度值为 0 * @return {int} == 0 表示读完毕,可能连接并未关闭;>0 表示本次读操作 * 读到的数据长度(当为解压后的数据时,则表示为解压之后的数据长度, * 与真实读到的数据不同,真实读到的数据长度应该通过参数 real_size 来 * 获得); < 0 表示数据流关闭,此时若 real_size 非空,则 real_size 存 * 储的值应该为 0 */ int read_body(string& out, bool clean = false, int* real_size = NULL); /** * 当调用 request 成功后调用本函数来从 HTTP 服务端读一行数据,可以循环调用 * 本函数,直到返回 false 或 body_finish() 返回 true 为止; * 本函数内部自动对压缩数据进行解压,如果在调用本函数之前调用 set_charset 设置了 * 本地字符集,则还同时对数据进行字符集转码操作 * @param out {string&} 存储结果数据 * @param nonl {bool} 读到的一行数据是否自动去掉尾部的 "\r\n" 或 "\n" * @param size {size_t*} 该指针非空时存放读到的数据长度 * @return {bool} 是否读到了一行数据:当返回 true 时表示读到了一行数据,可以 * 通过 body_finish() 是否为 true 来判断是否读数据体已经结束,当读到一个空行 * 且 nonl = true 时,则 *size = 0;当返回 false 时表示未读完整行且读完毕, * *size 中存放着读到的数据长度 */ bool body_gets(string& out, bool nonl = true, size_t* size = NULL);
虽然上面提供了多个读 HTTP 响应体数据的方法,但可以分为两大类:1、一次性读所有的数据体;2、以流式方式循环读数据体。 其中,对于“一次性读取所有数据体”的读方法,适合于响应数据体比较小的情形,当响应数据为 xml 或 json 格式时,还提供了直接将响应数据体转为 xml 或 json 对象的读方法;如果响应数据体非常大(如几兆甚至几十兆以上)则应该采用流式方法循环读数据体。
有一点需要注意,除了 " int read_body(char* buf, size_t size);" 可以直接读原生的响应数据体外,其它的读方法会将读到数据体自动进行解压、字符集转换操作后将最终结果返回调用者。
此外,为了方便一些文本类应用,在 http_request 类中还提供了 body_gets 方法,用来以行为单位读取 HTTP 响应数据体(当服务器也是以行为单位发送响应数据时才可使用 body_gets 方法)。
acl::http_request 类除了以上接口外,还提供了其它丰富的接口(如:支持 HTTP 断点续传的 Range 相关的方法),如果您觉得这些接口依然不能满足要求,不妨通过 "http_request::get_client" 获得 acl::http_client 类对象(该类对象是 acl 有关 http 协议处理中比较基础的 HTTP 通信类),然后再在 acl::http_client 类中查找您所希望的功能接口。
三、示例
下面用一个简单的例子来说明上面一些方法的使用过程:
// http_servlet.cpp : 定义控制台应用程序的入口点。 // #include <assert.h> #include <getopt.h> #include "acl_cpp/lib_acl.hpp" using namespace acl; ////////////////////////////////////////////////////////////////////////// class http_request_test { public: http_request_test(const char* server_addr, const char* file, const char* stype, const char* charset) { server_addr_= server_addr; file_ = file; stype_ = stype; charset_ = charset; to_charset_ = "gb2312"; } ~http_request_test() {} bool run(void) { string body; if (ifstream::load(file_, &body) == false) { logger_error("load %s error", file_.c_str()); return false; } http_request req(server_addr_); // 添加 HTTP 请求头字段 string ctype("text/"); ctype << stype_ << "; charset=" << charset_; http_header& hdr = req.request_header(); // 请求头对象的引用 hdr.set_url("/"); hdr.set_content_type(ctype); hdr.add_param("name1", "value1"); hdr.add_param("name2", "value2"); // 发送 HTTP 请求数据 if (req.request(body.c_str(), body.length()) == false) { logger_error("send http request to %s error", server_addr_.c_str()); return false; } // 取出 HTTP 响应头的 Content-Type 字段 const char* p = req.header_value("Content-Type"); if (p == NULL || *p == 0) { logger_error("no Content-Type"); return false; } // 分析 HTTP 响应头的数据类型 http_ctype content_type; content_type.parse(p); // 响应头数据类型的子类型 const char* stype = content_type.get_stype(); bool ret; if (stype == NULL) ret = do_plain(req); else if (strcasecmp(stype, "xml") == 0) ret = do_xml(req); else if (strcasecmp(stype, "json") == 0) ret = do_json(req); else ret = do_plain(req); if (ret == true) logger("read ok!\r\n"); return ret; } private: // 处理 text/plain 类型数据 bool do_plain(http_request& req) { string body; if (req.get_body(body, to_charset_) == false) { logger_error("get http body error"); return false; } printf("body:\r\n(%s)\r\n", body.c_str()); return true; } // 处理 text/xml 类型数据 bool do_xml(http_request& req) { xml body; if (req.get_body(body, to_charset_) == false) { logger_error("get http body error"); return false; } xml_node* node = body.first_node(); while (node) { const char* tag = node->tag_name(); const char* name = node->attr_value("name"); const char* pass = node->attr_value("pass"); printf(">>tag: %s, name: %s, pass: %s\r\n", tag ? tag : "null", name ? name : "null", pass ? pass : "null"); node = body.next_node(); } return true; } // 处理 text/json 类型数据 bool do_json(http_request& req) { json body; if (req.get_body(body, to_charset_) == false) { logger_error("get http body error"); return false; } json_node* node = body.first_node(); while (node) { if (node->tag_name()) { printf("tag: %s", node->tag_name()); if (node->get_text()) printf(", value: %s\r\n", node->get_text()); else printf("\r\n"); } node = body.next_node(); } return true; } private: string server_addr_; // web 服务器地址 string file_; // 本地请求的数据文件 string stype_; // 请求数据的子数据类型 string charset_; // 本地请求数据文件的字符集 string to_charset_; // 将服务器响应数据转为本地字符集 }; ////////////////////////////////////////////////////////////////////////// static void usage(const char* procname) { printf("usage: %s -h[help]\r\n", procname); printf("options:\r\n"); printf("\t-f request file\r\n"); printf("\t-t request stype[xml/json/plain]\r\n"); printf("\t-c request file's charset[gb2312/utf-8]\r\n"); } int main(int argc, char* argv[]) { int ch; string server_addr("127.0.0.1:8888"), file("./xml.txt"); string stype("xml"), charset("gb2312"); while ((ch = getopt(argc, argv, "hs:f:t:c:")) > 0) { switch (ch) { case 'h': usage(argv[0]); return 0; case 'f': file = optarg; break; case 't': stype = optarg; break; case 'c': charset = optarg; break; default: usage(argv[0]); return 0; } } log::stdout_open(true); // 允许日志输出至屏幕上 http_request_test test(server_addr, file, stype, charset); test.run(); // 开始运行 return 0; }
上面的例子来自于 lib_acl_cpp/samples/http_request。
如果查看 http_request::request 源码实现,会发现 try_open()、reuse_conn、need_retry_ 等方法或变量来表示 HTTP 客户端连接的重试过程,这是因为 http_request 类的设计是支持长连接及可重用的,对于 HTTP 客户端连接池来说这些功能非常重要,在下一节介绍使用 acl 的 http 客户端连接池功能类时将会用到 http 请求客户端连接的重连及重试机制。
四、参考
http_request 类的头文件位置:lib_acl_cpp/include/acl_cpp/http/http_request.hpp
HTTP 协议简介:http://zsxxsz.iteye.com/blog/568250
acl 库下载:http://sourceforge.net/projects/acl/
svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code
github:https://github.com/acl-dev/acl
acl 的编译与使用:http://zsxxsz.iteye.com/blog/1506554
qq 群:242722074
微博:http://weibo.com/zsxxsz/
相关推荐
acl_cpp(已包含在acl项目中:https://sourceforge.net/projects/acl/,请从acl项目url下载)是acl的c ++包装库,并且acl_cpp比acl具有更多有用的功能,例如例如流式mime解析,处理程序套接字支持以及db(mysql和...
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 编程、数据库编程、阻塞/非阻塞数据流等内容。
ACL(Access Control List)框架库是一个专为C语言设计的开源库,它的核心目标是提供一个高效且灵活的服务器开发框架。这个库包含了多种关键组件,使得开发者在构建网络服务时能够快速、稳定地实现复杂的功能。以下...
lib_acl_cpp 库的 db 模块依赖于 mysql 客户端库、sqlite 库,stream 流模块依赖于 polarssl 库(该库源码附在 acl/resource 目录下),另外,在 UNIX/LINUX 平台下还需要压缩库 --- libz 库(一般 LINUX 会自带该...
1. **数据结构设计**:首先,需要定义一个数据结构来存储权限信息,这可能是一个数组、关联数组或者自定义类。例如,可以创建一个包含用户ID、资源ID和权限位的数组。 2. **权限验证**:当用户请求访问资源时,...
以下示例展示了如何使用`UTL_HTTP`发送一个包含POST参数的HTTP请求: ```plsql DECLARE l_url VARCHAR2(4000) := 'http://example.com/submit'; l_request UTL_HTTP.req; l_response UTL_HTTP.resp; l_body ...
在本例中,我们关注的是一个名为`acl_iitbbs`的Python库,版本为0.1,适用于Python 3环境。这个库以`.whl`格式提供,这是一种预编译的Python包分发格式,旨在简化安装过程。 `.whl`文件是Python的Wheel格式,它是...
在本主题中,“acl.rar”可能是一个包含关于如何在JADE(Java Agent Development Framework)平台上使用ACL的详细资料的压缩文件。 JADE是一个开源的、跨平台的框架,用于开发遵循FIPA(Foundation for Intelligent...
lib_acl(纯C开发的基础库,主要包含网络通信及服务器编程框架以及其它丰富的功能)、lib_protocol(包含 HTTP/PING/SMTP 通信协议的C语言实现)、lib_acl_cpp(基于 lib_acl 及 lib_protocol 两个C库,提供了更为强大的...
acl_mysql_util是一个针对MySQL数据库和ACL(Access Control List,访问控制列表)的实用工具库,它为开发者提供了方便的接口,以便在应用中更高效、更安全地进行数据操作。这个库是基于MySQL的原生接口进行封装,...
- **实现`Zend_Acl_Resource_Interface`接口**:实现该接口需要定义一个`getResourceId()`方法,用于标识资源的唯一ID。 - **继承`Zend_Acl_Resource`类**:此类已经实现了资源接口,并提供了额外的方法来简化资源...
在Posix ACL中,`acl_t`是表示ACL的主要数据类型,而`acl_entry_t`则用来表示ACL中的一个条目,包含了一个用户或用户组的权限信息。头文件中还会定义如`ACL_TYPE_ACCESS`和`ACL_TYPE_DEFAULT`常量,分别对应于访问...
CISCO_ACL__思科_访问控制列表
首先,"Acl_pong-master"这个名字暗示了这是一个与访问控制列表(Access Control List,简称ACL)相关的项目,可能是为了实现特定的游戏权限管理或者安全机制。然而,"pong"通常指的是经典的乒乓游戏,这种游戏在...
在提供的文件名列表中,`acl.c`是一个C语言源代码文件,很可能包含了QEMU中ACL管理的实现细节。这个文件通常包含函数定义、数据结构和处理规则的实现。而`acl.h`则是一个头文件,它可能定义了ACL相关的数据结构、...
code_for_ACL_2020_paper_FLAT_Chinese_NER_Using_F_Flat-Lattice-Transformer
1. `acl_init()`: 这个函数通常用于创建一个新的空ACL,是处理ACL操作的基础。 2. `acl_get_entry()`: 此函数用于获取ACL中的特定条目,可能用于读取已有的权限设置。 3. `acl_set_entry()`: 这个函数用来修改ACL...