# FTP ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 概述 本章节介绍FTP协议的基本概念、基本要素、会话示例、流程概述以及消息结构,帮助用户理解FTP在嵌入式/物联网场景中的应用。 ## FTP简介 FTP(File Transfer Protocol,文件传输协议)是一种用于在网络上进行文件传输的标准网络应用层协议,基于客户机-服务器模型工作。它建立在TCP协议之上,允许用户通过客户端软件连接到FTP服务器,进行文件的上传、下载、删除、重命名和移动等操作,提供可靠高效的文件传送服务。 FTP协议的主要应用场景包括网站文件管理、软件更新部署、企业内部文件共享、设备数据备份等。 在嵌入式/物联网场景中,FTP常见用途: - **PUT**:上传文件至FTP(S)服务器。 - **GET**:从FTP(S)服务器下载文件。 - **SIZE**:获取FTP(S)服务器文件大小。 - **DELETE**:删除FTP(S)服务器上的文件。 - **MKDIR**:在FTP(S)服务器上创建文件夹。 - **RMDIR**:删除FTP(S)服务器上的文件夹。 - **LIST**:获取FTP(S)服务器目录内容。 - **RENAME**:重命名FTP(S)服务器上的文件或文件夹。 ### 基本要素 1. **工作模型**:基于客户端-服务器模型工作。客户端首先发起请求,服务器再回应客户端的请求。 2. **底层协议**:FTP默认使用TCP协议。默认情况下,控制连接通过端口21建立,数据连接在主动模式下使用端口20,被动模式下使用服务器动态分配的端口。 3. **工作模式**: - 主动模式(PORT):建立数据连接时,服务器主动连接客户端指定的数据端口。 - 被动模式(PASV):建立数据连接时,服务器在指定的数据端口被动等待客户端的连接,更适合防火墙环境和NAT环境。 4. **IPv6扩展模式**: - EPRT(Extended PORT):是FTP主动模式(PORT)的扩展版本,主要用于解决IPv6环境下的数据连接问题,同时也适用于IPv4环境。 - EPSV(Extended PASV):是FTP被动模式(PASV)的扩展版本,主要用于解决IPv6环境下的数据连接问题,同时也适用于IPv4环境。 5. **双连接设计**: - 控制连接:用于传输命令和响应,保持整个会话期间开放。 - 数据连接:用于实际文件传输,每次传输后关闭。 6. **认证方式**: - 支持用户名/密码认证。 - 支持匿名访问(用户名anonymous)。 7. **常见的文件操作能力**: - 上传(STOR/PUT)。 - 下载(RETR/GET)。 - 删除(DELE)。 - 重命名(RNFR/RNTO)。 - 目录操作(LIST、MKD、RMD)。 8. **传输的文件类型**: - ASCII模式:用于文本文件传输。 - 二进制模式:用于非文本文件传输。 9. **安全扩展**: - FTPS隐式加密:通过SSL/TLS加密的FTP。SSL/TLS加密会立即在连接建立后启动,无需额外的命令来启动加密会话。 - FTPS显式加密:通过SSL/TLS加密的FTP。客户端首先以明文方式连接到FTP服务器,然后再通过发送特定的命令(如AUTH TLS或AUTH SSL)来请求启动SSL/TLS加密会话。 - SFTP:基于SSH的文件传输协议。 ### 会话示例 一个典型的FTP会话流程如下: 1. 客户端建立控制连接到服务器21端口。 2. 服务器响应220(服务就绪)。 3. 客户端发送USER命令。 4. 服务器响应331(需要密码)。 5. 客户端发送PASS命令。 6. 服务器响应230(登录成功)。 7. 客户端发送PASV命令(被动模式)。 8. 服务器响应227(进入被动模式,告知端口)。 9. 客户端建立数据连接到指定端口。 10. 客户端发送RETR或STOR命令传输文件。 11. 传输完成后关闭数据连接。 12. 客户端发送QUIT命令。 13. 服务器响应221(再见)并关闭控制连接。 这种双连接设计使得FTP能够在传输文件的同时继续接收命令,提高了协议的灵活性。 ```{figure} images/board_NHaEwXII0hhXFTbSYnIc5dQln5c.jpg :align: center :alt: image ``` ### 流程概述 FTP流程是指网络设备通过蜂窝网络发送FTP请求并接收服务器响应的端到端过程。这个过程的核心在于FTP请求包的组包以及对FTP回应包的接收解析,确保数据交互的准确。 FTP的流程通常包含以下几个步骤: 1. qurl初始化: - `qurl_global_init()`:qurl库全局初始化。 - `qurl_core_create()`:创建一个新的qurl实例。 2. 配置qurl相关参数: - 解析输入的URL以及其他相关参数。 - `qurl_core_setopt()`:为给定的qurl实例设置选项。 3. 配置TLS相关参数: - `qurl_tls_cfg_init()`:qurl相关的TLS参数初始化。 - 处理外部输入的TLS相关参数。 - 通过 `qurl_core_setopt()` 和QURL_OPT_TLS_CFG配置TLS相关参数。 4. 发起FTP请求: - `qurl_core_perform()`:开始执行阻塞式的网络传输。 5. 激活网络: - 等待注网成功。 - PDP通道激活。 6. DNS域名解析。 7. 发起TCP连接(控制连接): - 创建socket。 - 绑定local地址。 - 发起socket连接进行三次握手。 8. 发起控制连接: - 服务器响应220(服务就绪)。 - 客户端发送USER命令。 - 服务器响应331(需要密码)。 - 客户端发送PASS命令。 - 服务器响应230(登录成功)。 9. 被动模式建立数据连接: - 客户端在控制连接上发送PASV命令,通知服务器方以被动方式进行连接。 - 服务器向客户端返回数据连接的IP地址和监听的端口号信息。 - 客户端创建数据连接使用的新socket。 - 新socket绑定local地址。 - 客户端选择一个本地的临时端口号,并在该端口上发起到服务器监听端口的数据连接。 - 数据连接的三次握手完成之后,服务器通过控制连接给客户端返回一个应答,数据连接完成。 10. 主动模式建立数据连接: - 主动模式下客户端首先创建socket、bind、listen监听等待服务器的数据连接请求。 - 使用PORT命令从控制连接上把临时选择的端口号以及本地IP地址发送到服务器端。 - 服务器在控制连接上接收到客户端发来的临时端口号,然后给客户端的控制端口返回一个ACK确认。 - 服务器发起一个从自己的数据端口(一般是20端口)到客户端先前指定的数据端口之间的数据连接。 - 数据连接的三次握手完成之后,客户端通过控制连接给服务端返回一个应答,数据连接完成。 11. 以GET请求为例,内部自动发送GET请求到服务器。 12. 读取并解析FTP回应header: - 通过QURL_OPT_WRITE_HEAD_CB配置处理响应header的回调函数,在回调内部可以读取到服务器的返回码并做相对应的处理。 13. 读取FTP回应body: - 通过QURL_OPT_WRITE_CB配置处理响应body的回调函数,客户可以自行在回调函数中对接收到的body数据进行处理。通常会不断调用FTP写body回调函数将收到的body保存到文件系统或者内存。 14. FTP请求完成,客户端主动断开TCP连接: - 默认数据传输完成之后只关闭数据连接,是否在数据传输完成后立即自动断开控制连接与QURL_OPT_REUSE_HOLD的配置相关。 15. qurl去初始化: - `qurl_slist_del_all()`:释放之前申请的单向链表。 - `qurl_core_delete()`:删除之前创建的qurl实例。 - `qurl_global_deinit()`:qurl库全局去初始化。 ```{figure} images/board_Nm15w4fbiha8XObFiPGcWv1Pn4e.jpg :align: center :alt: image ``` ### 消息结构 #### 控制连接报文结构 控制连接用于传输FTP命令和服务器响应,使用TCP端口21。控制连接报文结构如下: ```{figure} images/board_OHy5wAaLah8lzmb0KpvcFmMPncd.jpg :align: center :alt: image ``` FTP命令和响应采用ASCII格式,每行以CRLF(回车换行)结束。常见的FTP命令包括: - USER:用户名。 - PASS:密码。 - LIST:列出目录内容。 - RETR:下载文件。 - STOR:上传文件。 - QUIT:退出连接。 服务器响应以3位数字开头,后跟描述文本,例如: - 220:服务就绪。 - 230:登录成功。 - 530:未登录。 #### 数据连接报文结构 数据连接用于传输实际文件内容或目录列表,使用TCP端口20(主动模式)或动态端口(被动模式)。数据连接报文结构如下: ```{figure} images/board_Xlw8w2UbLhOTMZbPX1acSLWNn30.jpg :align: center :alt: image ``` 数据传输可以是二进制模式或ASCII模式: - 二进制模式:直接传输文件的字节内容,适用于非文本文件。 - ASCII模式:在传输时进行文本格式转换,适用于文本文件。 ## qurl库简介 模组的FTP功能是基于底层的qurl库实现的。 qurl是一个易于使用的客户端URL传输库,参考libcurl库的实现,支持多种协议来实现简单的短连接的服务,目前支持http(s)、ftp(s)、smtp(s)等服务。它支持在多种操作系统上运行,如RTOS、Windows、Linux、macOS等。qurl提供了一套简单的API,使开发者能够更方便地发起网络传输,处理网络响应等。 qurl是简化版的curl,提供了基本的网络协议栈功能,忽略了一些复杂以及不常用的配置项,相较于libcurl,qurl提供了一种轻量级的实现,以降低内存和CPU的使用率,适用于内存受限的模组平台。 开发者无需关心内部实现,只需要对qurl进行初始化,并创建一个qurl实例,为qurl实例设置选项,发起网络传输。然后通过配置的回调来处理网络的响应。 # API说明 ## 头文件 *qurl.h* ## 函数列表 | **函数** | **描述** | | --- | --- | | *qurl_global_init()* | qurl库全局初始化 | | *qurl_core_create()* | 创建一个新的qurl实例 | | *qurl_core_reset()* | 重置之前已经创建好的qurl实例的所有选项 | | *qurl_core_setopt()* | 为给定的qurl实例设置选项 | | *qurl_slist_add_strdup()* | 拷贝字符串的方式插入链表 | | *qurl_tls_cfg_init()* | qurl相关的tls参数初始化 | | *qurl_core_perform()* | 开始执行阻塞式的网络传输 | | *qurl_core_abort()* | 提前终止网络传输流程 | | *qurl_core_getinfo()* | 获取给定的qurl实例的信息 | | *qurl_slist_del_all()* | 释放整个单向链表 | | *qurl_core_get_last_close_event()* | 获取qurl实例最后关闭的原因 | | *qurl_core_delete()* | 删除之前创建的qurl实例 | | *qurl_global_deinit()* | qurl库全局去初始化 | ## API函数详解 本章节详细说明qurl库提供的API函数,包括初始化、控制操作、获取信息、去初始化以及选项配置。每个函数包括原型、功能描述、参数说明、返回值、使用注意事项和示例。 ### qurl初始化 #### qurl_global_init ##### **函数原型** ```c qurl_ecode_t qurl_global_init(void); ``` ##### **功能描述** 此函数会进行qurl库全局初始化。 用于设置qurl所需的程序环境。可将其视为库的加载器。 ##### **参数说明** 无 ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 在程序调用qurl中的任何其他函数之前,必须在程序内至少调用一次此函数。 2. 它设置的环境主要指的是库运行必要的资源的创建以及公共的全局变量的初始化,它创建的资源在整个程序运行周期内是不变的,并且对于每个qurl实例都是相同的,因此调用多次或者一次效果是相同的。 3. 该API线程不安全。需要外部调用的时候去保证线程安全。当有其他线程正在运行某个qurl实例的时候,不能调用此函数。 ``` ##### **示例** ```c int main(void) { qurl_global_init(); /* use qurl, then before exiting... */ qurl_global_deinit(); } ``` #### qurl_core_create ##### **函数原型** ```c qurl_ecode_t qurl_core_create(qurl_core_t *core_ptr); ``` ##### **功能描述** 此函数会创建一个新的qurl实例,并返回一个qurl实例的操作句柄。 对这个qurl实例进行操作的时候需要使用到这个句柄作为输入参数。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core_ptr | `qurl_core_t *` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 操作完成后,必须调用 `qurl_core_delete()` 删除对应的qurl实例。 2. qurl实例的操作句柄用于保持和控制网络传输,建议使用同一个qurl实例进行多次网络传输。如果下一次网络传输需要重新配置选项,必须调用 `qurl_core_reset()` 重置qurl实例并进行新的选项配置。 3. 调用 `qurl_core_create()` 之前必须调用过一次 `qurl_global_init()`。 4. 如果调用 `qurl_core_create()` 返回其他 `qurl_ecode_t` 枚举类型的值,表示创建qurl实例的时候出了问题,请不要使用该qurl实例的操作句柄执行其他qurl函数。 ``` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } qurl_global_deinit(); } ``` #### qurl_tls_cfg_init ##### **函数原型** ```c void qurl_tls_cfg_init(qurl_tls_cfg_t *tls_cfg_ptr); ``` ##### **功能描述** 该函数用于qurl相关的TLS参数初始化。 使用TLS的连接需要调用 `qurl_tls_cfg_init()` 对TLS的配置进行初始化,并使用配置项 `QURL_OPT_TLS_CFG` 将最终的TLS参数配置到qurl内部。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | tls_cfg_ptr | `qurl_tls_cfg_t *` | 是 | 无 | TLS配置参数 | ##### **返回值说明** 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_tls_cfg_t tls_cfg = {0}; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://112.31.84.164:8301/9X07-QAT/1K.txt"); qurl_tls_cfg_init(&tls_cfg); tls_cfg.negotiate_timeout = 30; qurl_core_setopt(core, QURL_OPT_TLS_CFG, &tls_cfg); qurl_core_perform(core); } } ``` ### qurl控制操作 #### qurl_core_reset ##### **函数原型** ```c qurl_ecode_t qurl_core_reset(qurl_core_t core); ``` ##### **功能描述** 该函数用于重置之前已经创建好的qurl实例,将之前在指定qurl实例上设置的所有选项重新初始化为默认值。 这将使qurl实例恢复到使用 `qurl_core_create()` 创建时的状态。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { /* ... the core is used and options are set ... */ qurl_core_reset(core); } } ``` #### qurl_slist_add_strdup ##### **函数原型** ```c qurl_slist_t qurl_slist_add_strdup(qurl_slist_t slist, char *str); ``` ##### **功能描述** 该函数用于拷贝字符串插入到字符串链表中。 应将现有列表作为第一个参数传递,并从此函数返回新列表。我们一开始在 `slist` 参数中传入 `QOSA_NULL` 以创建一个新的列表。此函数返回时,已附加指定的字符串。`qurl_slist_add_strdup()` 内部会自动去复制字符串。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | slist | `qurl_slist_t` | 是 | 无 | 要插入的单向链表 | | str | `char *` | 是 | 无 | 要添加的字符串 | ##### **返回值说明** 如果成功则返回插入完成的新链表,如果失败返回QOSA_NULL。 ```{note} **注意:** 1. 调用 `qurl_core_setopt` 将slist链表配置到qurl内部,使用之后应该调用 `qurl_slist_del_all()` 释放整个单向链表。 2. 为了避免在失败时返回空覆盖现有的非空列表,建议将新列表返回给一个临时变量,该变量可以在更新原始列表指针之前判断 `QOSA_NULL`。 ``` ##### **示例** ```c int main(void){ qurl_slist_t headers = QOSA_NULL; qurl_slist_t temp = QOSA_NULL; qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { headers = qurl_slist_add_strdup(headers, "Content-Type: 05"); if(headers == QOSA_NULL) { return -1; } temp = qurl_slist_add_strdup(headers, "Accept-Charset: ascii"); if(temp == QOSA_NULL) { qurl_slist_del_all(headers); return -1; } headers = temp; qurl_core_setopt(core, QURL_OPT_HTTP_HEADER, headers); qurl_core_perform(core); qurl_slist_del_all(headers); } } ``` #### qurl_slist_del_all ##### **函数原型** ```c void qurl_slist_del_all(qurl_slist_t list); ``` ##### **功能描述** 该函数用于释放整个单向链表。 它会删除先前 `qurl_slist_add_strdup()` 构建的链表的所有痕迹。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | slist | `qurl_slist_t` | 是 | 无 | 要释放的单向链表 | ##### **返回值说明** 无 ```{note} **注意:** 1. 对list传递 `QOSA_NULL` 指针会使此函数立即返回而不执行任何操作。 2. 调用此函数并返回后对链表的任何使用都是非法的。 ``` ##### **示例** ```c int main(void){ qurl_slist_t headers = QOSA_NULL; qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { headers = qurl_slist_add_strdup(headers, "Content-Type: 05"); if(headers == QOSA_NULL) { return -1; } qurl_core_setopt(core, QURL_OPT_HTTP_HEADER, headers); qurl_core_perform(core); qurl_slist_del_all(headers); } } ``` #### qurl_core_perform ##### **函数原型** ```c qurl_ecode_t qurl_core_perform(qurl_core_t core); ``` ##### **功能描述** 该函数用于执行阻塞式的网络传输,并在完成时返回,如果失败,则更早返回。 我们可以使用相同的qurl实例的操作句柄对 `qurl_core_perform()` 进行任意数量的调用。如果您打算传输多个文件,我们鼓励您这样做。在接下来的网络传输中qurl库会尝试重用现有的连接,从而使操作更快、CPU占用更少、使用更少的网络资源。您可能希望在调用之间使用 `qurl_core_setopt()` 来设置以下 `qurl_core_perform()` 调用的选项。我们可能需要在两次调用 `qurl_core_perform()` 之间调用 `qurl_core_setopt()` 来进行新的选项配置。 网络传输将数据传输到对等端或从对等端获取数据。应用程序通过配置 `QURL_OPT_WRITE_HEAD_CB`,`QURL_OPT_WRITE_CB` 和 `QURL_OPT_WRITE_CB_ARG` 选项来告诉qurl我们应用程序要如何接收数据。要告诉qurl我们应用程序要发送什么数据,有几种选择,但有常见的组合是 `QURL_OPT_READ_CB`,`QURL_OPT_READ_CB_ARG` 和 `QURL_OPT_UPLOAD_SIZE`。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 在 `qurl_core_create()` 和所有 `qurl_core_setopt()` 调用完成后再调用此函数,它将按照选项中的描述执行传输。必须使用和 `qurl_core_create()` 返回的相同的qurl实例的操作句柄作为输入来调用它。 2. 我们要避免使用相同的qurl实例的操作句柄从两个地方同时调用此函数。让函数在下次调用之前先返回。如果你想要并行传输,你必须使用多个qurl实例。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_ecode_t ret = QURL_OK; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); ret = qurl_core_perform(core); qurl_core_delete(core); } } ``` #### qurl_core_abort ##### **函数原型** ```c qurl_ecode_t qurl_core_abort(qurl_core_t core); ``` ##### **功能描述** 该函数可以提前终止网络传输流程。 使用此函数,我们可以终止正在运行的连接。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 我们可以在其他线程执行此函数去终止网络传输流程。与大多数其他qurl函数不同,我们也可以在回调函数中调用 `qurl_core_abort`。 ``` ##### **示例** ```c int thread1(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } //在其他线程或者配置的回调函数里执行qurl_core_abort() int thread2(qurl_core_t *core){ if (core != QOSA_NULL) { qurl_core_abort(core); } } ``` ### 获取qurl信息 #### qurl_core_getinfo ##### **函数原型** ```c qurl_ecode_t qurl_core_getinfo(qurl_core_t core, qurl_info_e opt, ...); ``` ##### **功能描述** 该函数可以获取保存在qurl实例中的信息。 如果您想获取与传输相关的数据,请在执行传输后使用此函数。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | | opt | `qurl_info_e` | 是 | 参考 `qurl_info_e` 枚举 | 对应要获取的信息选项 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 第三个参数必须指向所用选项的特定类型。 2. 数据会相应地存储在第三个参数中而且只有当此函数返回 `QURL_OK` 时得到的信息才是正确的。 3. 当第三个参数传入的是指针的指针的时候,函数内部会进行内存的拷贝,所以外部使用完成之后必须自行释放对应的内存。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long resp_code = 0; //响应状态码 if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); if (QURL_OK == qurl_core_perform(core)) { if (QURL_OK == qurl_core_getinfo(core, QURL_INFO_RESP_CODE, &resp_code)) { printf("We received http resp_code: %d\n", resp_code); } } qurl_core_delete(core); } } ``` #### qurl_core_get_last_close_event ##### **函数原型** ```c int qurl_core_get_last_close_event(qurl_core_t core); ``` ##### **功能描述** 该函数可以获取qurl实例最后关闭的原因。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 正常关闭返回 `CLOSE_EVENT_NORMAL`,否则返回 `close_event_t` 枚举值(详见附录)。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_ecode_t ret = QURL_OK; int http_last_close_event = 0; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); ret = qurl_core_perform(core); http_last_close_event = qurl_core_get_last_close_event(core); printf("http_last_close_event : %d\n", http_last_close_event); qurl_core_delete(core); } } ``` ### qurl去初始化 #### qurl_core_delete ##### **函数原型** ```c qurl_ecode_t qurl_core_delete(qurl_core_t core); ``` ##### **功能描述** 该函数用于删除之前创建的qurl实例。 此函数与 `qurl_core_create()` 相反。它关闭并释放之前创建的qurl实例相关的所有资源。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 此调用将关闭这个qurl实例使用过的所有连接。如果您打算进行更多的网络传输,请不要调用此函数,重复使用同一个qurl实例是qurl获得良好性能的关键。 2. 在调用此函数并返回后再去使用这个qurl实例是非法的。 3. 在句柄中传递 `QOSA_NULL` 指针会使此函数立即返回而不执行任何操作。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } ``` #### qurl_global_deinit ##### **函数原型** ```c qurl_ecode_t qurl_global_deinit(void); ``` ##### **功能描述** 该函数用于qurl库全局去初始化。 释放 `qurl_global_init()` 获取的资源。 ##### **参数说明** 无 ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 当我们开始使用qurl,每调用一次 `qurl_global_init()`,就应该调用一次 `qurl_global_deinit()`。当调用到对应次数的 `qurl_global_deinit()`,才会真正去释放 `qurl_global_init()` 获取的资源。 2. 该API线程不安全。需要外部调用的时候去保证线程安全。当有其他线程正在运行某个qurl实例的时候,不能调用此函数。 3. 调用 `qurl_global_deinit()` 不会等待所有qurl实例运行完才去执行,这样可能会出现dump或者其他问题,所以不建议随意调用 `qurl_global_deinit()` 去初始化。 ``` ##### **示例** ```c int main(void){ qurl_global_init(); /* use qurl, then before exiting... */ qurl_global_deinit(); } ``` ### qurl相关选项配置 #### qurl_core_setopt ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, qurl_opt_e opt, ...); ``` ##### **功能描述** 该函数用于为给定的qurl实例设置选项。 它告诉qurl该如何表现。通过设置适当的选项,应用程序可以更改qurl的行为。 所有选项都是用一个选项跟上后面的可变参数来设置的。该参数可以是long、字符串类型、函数指针、结构体指针等,具体取决于特定选项的预期。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | | opt | `qurl_opt_e` | 是 | 参考 `qurl_opt_e` 枚举 | qurl配置选项 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 请仔细阅读本文档的 `opt` 介绍,因为错误的输入值可能会导致qurl表现不佳。 2. 在每个函数调用中只能设置一个选项。典型的应用程序在设置阶段会调用很多次 `qurl_core_setopt()`。 3. `core` 是 `qurl_core_create()` 调用返回的操作句柄。 4. 使用此函数设置的选项具有粘性。多次调用 `qurl_core_setopt()` 配置新的选项之后,之前配置的选项依旧存在。当我们执行下一次网络传输的时候,这些配置项也不会自动重置,如果我们下一次网络传输需要不同的配置,那么必须在传输之前进行更改。也可以选择使用 `qurl_core_reset()` 将所有选项重置回内部默认值。 5. 设置选项的顺序不会影响 `qurl_core_perform()` 执行的结果。 6. 传入 `qurl_core_setopt()` 的char * 类型字符串,会在qurl内部进行拷贝,在 `qurl_core_setopt()` 返回后,上层的字符串内存即可修改或释放。qurl几乎不验证输入的字符串内容。但是我们尽量不要去输入特殊字符,可能会引发意想不到的结果。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } ``` ## qurl配置项详解 本章节详细说明qurl配置项,包括通用配置项、中间件配置项、Socket配置项、TLS配置项和HTTP协议配置项。每个配置项包括函数原型、功能描述、默认值、适用协议、依赖项、使用注意事项和示例。 ### 通用配置项 #### QURL_OPT_URL 配置发起网络传输使用到的URL ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_URL, char *URL); ``` ##### **功能描述** 传递要使用的URL的指针。 传递指向要使用的URL的指针。参数应该是一个以null结尾的char类型的字符串,该字符串必须以以下格式进行URL编码: `scheme://host:port/path` 有关格式的更多解释,请参阅RFC3986。 qurl在网络传输开始之前不会验证语法或使用URL。即使您在此处设置了一个疯狂的值,`qurl_core_setopt()` 仍可能返回 `QURL_OK`。 qurl不支持根据URL中的IP地址自动猜测出scheme以及默认scheme,所以URL必须带上scheme,如果给定的URL缺少scheme名称(如“http://”或“ftp://”等),虽然调用 `qurl_core_setop()` 配置URL成功,但是执行 `qurl_core_perform()` 的时候会返回错误码 `QURL_ECODE_URL_MALFORMED_INPUT`。目前支持的scheme有:http,https,ftp,ftps,smtp,smtps。 在开始传输之前,必须设置 `QURL_OPT_URL`。 设置完此选项后,应用程序不必保留字符串。 多次配置此选项会用最后一组字符串覆盖前面的字符串。将其设置为 `QOSA_NULL` 可以对之前配置的URL进行清空。但是请注意,qurl需要设置一个URL才能执行传输。 ##### **默认值** QOSA_NULL。如果未设置此选项,则无法执行网络传输。 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **安全问题** 从外部不受信任的一方获取URL会带来几个安全问题: 在Linux或者Windows系统运行qurl的时候,由于qurl配置URL的时候不会对URL进行任何过滤,如果你的qurl运行在一个有服务器运行的环境中,获得一个未经过滤的URL可以很容易地欺骗你的qurl程序去访问本地资源而不是远程资源。在接受用户提供的URL时,很难保证设备免受本地程序访问。 此类自定义URL还可以访问您计划之外的其他端口,因为端口号是常规URL格式的一部分。本地主机和自定义端口号的组合可以让外部用户随意访问您的本地服务。 ```{note} **注意:** 由于 `qurl_core_setopt()` 不会去解析URL是否是正确的,所以直到执行 `qurl_core_perform()` 才会发现URL不正确 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } ``` #### QURL_OPT_USERNAME 配置用户名用于登录需要身份认证的服务器 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_USERNAME, char *username); ``` ##### **功能描述** 传递一个char指针作为参数,该指针应指向用于传输的以null结尾的用户名。 `QURL_OPT_USERNAME` 设置在协议身份验证中要使用的用户名。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** 通常与 `QURL_OPT_PASSWORD` 结合使用。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_USERNAME, "clark"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_PASSWORD 配置密码用于登录需要身份认证的服务器 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_PASSWORD, char *password); ``` ##### **功能描述** 传递一个char指针作为参数,该指针应指向用于传输的以null结尾的密码。 `QURL_OPT_PASSWORD` 选项应与 `QURL_OPT_USERNAME` 选项结合使用。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** 通常与 `QURL_OPT_USERNAME` 结合使用。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_PASSWORD, "qwerty"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_NETWORK_ID 配置网络传输使用的网络ID ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_NETWORK_ID, long nw_id); ``` ##### **功能描述** 传递一个long类型的网络ID作为参数,该参数表示发起网络传输时使用的是哪个PDP通道。 < 0 表示不指定,>= 0 则需要参考平台适配说明,一般指的就是使用的PDP通道。 这个参数默认 < 0 不指定网络ID,所以如果没有配置此参数,直接执行 `qurl_core_perform()` 发起网络传输会失败返回 `QURL_ECODE_NETWORK_ERR`。所以在发起网络传输之前一定要根据平台的实际情况去手动配置此参数。 ##### **默认值** 默认 < 0 不指定 ##### **适用协议** 所有协议 ##### **依赖项** 与平台相关 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_NETWORK_ID, 1); qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_PORT 配置用户指定端口号去连接远端服务器 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_PORT, long port); ``` ##### **功能描述** 通常不建议使用此选项,因为这个配置项对所有协议有效且会覆盖URL的端口号。改为在URL中设置首选端口号。 此选项用来设置要连接的远程端口号,而不是URL中指定的端口号或所用协议的默认端口号。 通常,您只需让URL决定使用哪个端口,但这个配置项允许应用程序覆盖该端口。 虽然此选项输入的参数是long类型的,但端口号是一个无符号的16位数字,因此输入小于零或大于 65535的端口号会返回 `QURL_ECODE_PARAM_INVALID` 错误。 ##### **默认值** 默认值为0,即不指定端口号,使用URL中的端口号。 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_PORT, 8080L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TIMEOUT_MS 配置整个网络传输的超时时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TIMEOUT_MS, long timeout); ``` ##### **功能描述** 传递一个conn类型的 `timeout` 时间,即允许qurl传输操作花费的最长时间(毫秒)。也就是从执行 `qurl_core_perform()`,到此接口返回的最大时间。 这个时间需要配置为合适的值,如果这个超时时间配置过小,可能会中止完全正常的操作。 这个选项以毫秒为单位进行设置。 如果多次设置了 `QURL_OPT_TIMEOUT_MS`,则使用最后设置的值。 由于此选项对允许请求花费的时间进行了严格限制,因此它在具有不同传输时间的动态用例中的作用有限,除非你每次发起网络传输的时候都重新去配置它并且能获取到适当的值,这很难做到。当使用多qurl实例并行传输的时候,这一点尤其明显,因为多qurl实例并发可能会对传输进行排队,这时候消耗的时间也包括在内。 建议 `QURL_OPT_TIMEOUT_MS` 配置的超时时间要大于 `QURL_OPT_IDLE_TIMEOUT_MS` 和 `QURL_OPT_ROUSE_CHECK_TIME_MS`。如果配置 `QURL_OPT_IDLE_TIMEOUT_MS` 为5000 ms,`QURL_OPT_TIMEOUT_MS` 配置为2000 ms,则网络传输的持续时间永远不会超过2000 ms。 ##### **默认值** 30 秒 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* complete within 60000 milliseconds */ qurl_core_setopt(core, QURL_OPT_TIMEOUT_MS, 60 * 1000); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_IDLE_TIMEOUT_MS 配置网络传输过程中内部空闲超时时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_IDLE_TIMEOUT_MS, long timeout); ``` ##### **功能描述** 传递一个long类型的 `timeout` 时间,即允许没有任何网络数据传输的最长时间(毫秒)。不管是发送网络数据还是接收到网络数据,都会去重置这个 idle 定时器。 这个时间需要配置为合适的值,如果这个超时时间配置过小,出现网络波动的情况下,可能会中止完全正常的操作。 这个选项以毫秒为单位进行设置。 如果多次设置了 `QURL_OPT_IDLE_TIMEOUT_MS`,则使用最后设置的值。 根据周围网络环境的好坏,这个超时时间应该做相应的调整。当使用多qurl实例并行传输的时候,多qurl实例并发可能会对传输进行排队,这也可能会对 idle 超时造成一定的影响。 建议 `QURL_OPT_IDLE_TIMEOUT_MS` 配置的超时时间要小于 `QURL_OPT_TIMEOUT_MS`。否则 `QURL_OPT_IDLE_TIMEOUT_MS` 这个参数配置的超时将不起作用。 ##### **默认值** 0。默认不开启。 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* Network transmission is required within 5000 milliseconds */ qurl_core_setopt(core, QURL_OPT_IDLE_TIMEOUT_MS, 5 * 1000); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_ROUSE_CHECK_TIME_MS 定时检查qurl的状态 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_ROUSE_CHECK_TIME_MS, long rouse_check_time); ``` ##### **功能描述** 该函数可以配置系统周期性唤醒检查的时间,当执行 `qurl_core_perform()` 长时间阻塞的时候,会按这个时间值定时唤醒检查其他事件,如检查是否收到调用 `qurl_core_abort()` 触发的事件。 ##### **默认值** 1 秒 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* Wake up and check for other events within 500 milliseconds */ qurl_core_setopt(core, QURL_OPT_ROUSE_CHECK_TIME_MS, 500); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_RESUME_FROM 配置恢复传输的起始点。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_RESUME_FROM, long from); ``` ##### **功能描述** 该配置项可以指定恢复传输的起始点。若为负数,表示从最后开始算起,如-100,表示需要下载/上传最后的100节。单位:byte 传递一个long as参数。它包含您希望传输开始的字节数偏移量,也就是上传或者下载文件的起始位置。将此选项设置为0,使传输从头开始(禁用恢复)。 使用FTP进行上传时,恢复位置是qurl应尝试从本地/源文件中恢复上传的位置,然后将源文件附加到远程目标文件。 使用FTP进行下载时,恢复位置是远端服务器文件中恢复下载的位置,此参数作为起始位置将文件下载到qurl。 此配置和 `QURL_OPT_RANGE` 类似,当两个配置项都进行配置的时候,以 `QURL_OPT_RESUME_FROM` 优先。 ##### **默认值** 0。不使用,从头开始传输。 ##### **适用协议** FTP、HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long start_pos = 100; long file_maxsize = 1024; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com"); if (start_pos != 0) { /* download at byte index 100 */ qurl_core_setopt(core, QURL_OPT_RESUME_FROM, start_pos); } /* Maximum SIZE for downloading files */ qurl_core_setopt(core, QURL_OPT_MAXFILESIZE, file_maxsize); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_RANGE 配置下载范围 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_RANGE, char *range); ``` ##### **功能描述** 该配置项可以配置下载范围,比如设置HTTP Range字符串,或者FTP指定范围。 传递一个 char 类型的指针作为参数,该指针应包含要检索的指定范围。它采用“X-Y” 格式,其中 X或Y 可以省略,X和Y 是字节索引。 如果此配置项应用于 HTTP 传输还支持多个间隔,用逗号分隔,如 “X-Y,N-M”。使用这种多间隔会导致 HTTP 服务器将响应文档分块发送(使用标准 MIME 分隔技术),作为qurl按原样返回的多部分响应。除了请求的字节外,它还包含元信息。解析或以其他方式转换此响应是调用者的责任。 有一个点要注意,HTTP标准(RFC 7233 第3.1节)允许服务器忽略范围请求,因此即使您为请求设置了 `QURL_OPT_RANGE` ,您最终也可能会收到完整的响应。 对于 HTTP PUT 上传,不建议使用此选项,因为它可能与其他选项冲突。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 设置此选项后,应用程序不必保留字符串。 ##### **默认值** NULL,不使用指定范围。 ##### **适用协议** HTTP、FTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_RANGE, "100-200"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_RAW 配置HTTP请求使用自定义请求头 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_RAW, long onoff); ``` ##### **功能描述** 这个配置项只适用于具有请求头的协议。 将 `onoff` 设置为1,打开自定义请求头功能。 标记上传 HEAD 为原始数据,不需要内部提供合成。内部会从 `QURL_OPT_UPLOAD_HEAD_DATA`/`QURL_OPT_UPLOAD_HEAD_FILE`/`QURL_OPT_HTTP_HEADER` 这三种方式中去获取自定义的请求头,我们也是选择其中一种方式去配置我们的自定义请求头数据获取方式。如果三者均未设置也不会返回异常,则会尝试从请求body 中获取请求 header,这会被判定为从 `QURL_OPT_UPLOAD_DATA`/`QURL_OPT_UPLOAD_FILE`/`QURL_OPT_READ_CB` 中一同获取 HEAD和body。 通常使用 `QURL_OPT_HTTP_HEADER` 的方式去添加自定义请求头。 ##### **默认值** 0,不使用自定义请求头,内部自动合成。 ##### **适用协议** HTTP、SMTP ##### **依赖项** `QURL_OPT_UPLOAD_HEAD_DATA` 或 `QURL_OPT_UPLOAD_HEAD_FILE` 或 `QURL_OPT_HTTP_HEADER` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_HEAD_CB 配置处理响应header的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_HEAD_CB, qurl_write_head_cb *cb); ``` ##### **功能描述** 该配置项用于配置qurl客户端的响应头回调函数,类型:`qurl_write_head_cb`。 用户侧设置的用户接收header数据处理函数,如 HTTP的响应头。 此配置项和 `QURL_OPT_WRITE_HEAD_CB_ARG` 配合使用。 `qurl_write_head_cb()` 定义为 `typedef long (*qurl_write_head_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是接收的header数据,`size` 表示 head的长度,`arg` 表示 `QURL_OPT_WRITE_HEAD_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以自行在回调函数中对接收到的header数据进行处理。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_HEAD_CB_ARG` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_WRITE_HEAD_CB, header_write_callback); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_HEAD_CB_ARG 配置响应header的回调函数的用户参数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_HEAD_CB_ARG, void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的响应header回调函数的用户输入参数,类型为 `void *`。 此配置项和 `QURL_OPT_WRITE_HEAD_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_write_head_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_HEAD_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_WRITE_HEAD_CB, header_write_callback); /* pass in custom data to the callback */ qurl_core_setopt(core, QURL_OPT_WRITE_HEAD_CB_ARG, header_write_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_CB 配置处理响应body的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_CB, qurl_write_cb *cb); ``` ##### **功能描述** 该配置项用于配置qurl客户端的响应body回调函数,类型:`qurl_write_cb`。 用户侧设置的用户接收body处理函数。 此配置项和 `QURL_OPT_WRITE_CB_ARG` 配合使用。 `qurl_write_cb()` 定义为 `typedef long (*qurl_write_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是接收的body数据,`size` 表示body的长度,`arg` 表示 `QURL_OPT_WRITE_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以自行在回调函数中对接收到的body数据进行处理。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_CB_ARG` ##### **示例** ```c static char g_buff[2049] = {0}; struct qurl_app_r_buf_s{ char *buf_ptr; long valid_len; }; typedef struct qurl_app_r_buf_s qurl_app_r_buf_t; static long http_write_callback(char *buf, long size, void *arg){ long len = size; long log_len = len > 2048 ? 2048 : len; memcpy(g_buff, buf, log_len); g_buff[log_len] = 0; printf("%s", g_buff); return len; } int main(void){ qurl_core_t core = QOSA_NULL; long start_pos = 100; long file_maxsize = 1024; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* send all data to this function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB, http_write_callback); /* we pass our 'header_write_arg' struct to the callback function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, http_write_arg); /* send a request */ qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_CB_ARG 配置响应body的回调函数的用户参数。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_CB_ARG , void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的响应body 回调函数的用户输入参数,类型为 `void *`。 此配置项和 `QURL_OPT_WRITE_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_write_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* send all data to this function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB, http_write_callback); /* we pass our 'header_write_arg' struct to the callback function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, http_write_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_HEAD_CB 配置读取请求header的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_HEAD_CB, qurl_read_head_cb *cb); ``` ##### **功能描述** 读取请求header的一种方式:通过回调函数去读取请求 header。 该配置项用于配置qurl客户端读取请求header的回调函数,类型:`qurl_read_head_cb`。 用户侧设置的用户发送 head 数据处理函数 此配置项和 `QURL_OPT_READ_HEAD_CB_ARG` 配合使用。 `qurl_read_head_cb` 定义为 `typedef long (*qurl_read_head_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是读取的header数据,`size` 表示header的长度,`arg` 表示 `QURL_OPT_READ_HEAD_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以在回调函数中自行读取header数据传递到 `buf`。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP、SMTP ##### **依赖项** `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_READ_HEAD_CB_ARG` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_READ_HEAD_CB, read_head_callback); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_HEAD_CB_ARG 配置读取请求header的回调函数的用户参数。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_HEAD_CB_ARG, void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的请求header回调函数的用户输入参数,类型为 `void *`。 此配置项和 `QURL_OPT_READ_HEAD_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_read_head_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP、SMTP ##### **依赖项** `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_READ_HEAD_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_READ_HEAD_CB, read_head_callback); /* we pass our 'read_head_arg' struct to the callback function */ qurl_core_setopt(core, QURL_OPT_READ_HEAD_CB_ARG, read_head_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_CB 配置读取请求body的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_CB, void *arg); ``` ##### **功能描述** 读取请求body的一种方式:配置读取请求body的回调函数。 该配置项用于配置qurl客户端读取请求body的回调函数,类型:`qurl_read_cb`。 用户侧设置的用户发送body数据处理函数 此配置项和 `QURL_OPT_READ_CB_ARG` 配合使用。 `qurl_write_head_cb` 定义为 `typedef long (*qurl_read_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是读取的body数据,`size` 表示body的长度,`arg` 表示 `QURL_OPT_READ_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以在回调函数中自行读取body数据传递到buf。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_READ_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(lbs_ptr->http_hd, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(lbs_ptr->http_hd, QURL_OPT_READ_CB, read_callback); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_CB_ARG 配置读取请求body的回调函数的用户参数。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_CB_ARG, void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的请求body回调函数的用户输入参数,类型为 `void *`。 此配置项和 `QURL_OPT_READ_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_read_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_READ_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(core, QURL_OPT_READ_CB, read_callback); qurl_core_setopt(core, QURL_OPT_READ_CB_ARG, read_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_DATA 配置读取请求header的指针地址 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_DATA, void *upload_head_data_ptr); ``` ##### **功能描述** 读取请求header的一种方式:通过地址去获取请求header。 该配置项用于配置qurl客户端读取请求header的指针地址,类型:`void *`。 用户侧设置的该指针指定上传的header数据的地址。内部会自动读取指针中的数据作为请求header发送到对端。 直到达到 `QURL_OPT_UPLOAD_HEAD_SIZE` 中配置的size。 此配置项和 `QURL_OPT_UPLOAD_HEAD_SIZE` 配合使用。 ##### **默认值** QOSA_NULL ##### 适用协议 HTTP、SMTP ##### 依赖项 `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_UPLOAD_HEAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_DATA, data1); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_SIZE 配置上传的请求头size ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_SIZE, long size); ``` ##### **功能描述** 该配置项用于配置qurl客户端以指针的方式读取的请求header的size,类型:long。 指定上传的请求头size,如 http的header大小等等。单位:byte。 内部会自动读取 `QURL_OPT_UPLOAD_HEAD_DATA` 配置的指针中的数据作为请求header发送到对端。直到达到我们配置的size。 此配置项和 `QURL_OPT_UPLOAD_HEAD_DATA` 配合使用。 ##### **默认值** -1,不使用 ##### **适用协议** HTTP、SMTP ##### 依赖项 `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_UPLOAD_HEAD_DATA` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_DATA, data1); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_SIZE , strlen(data1)); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_FILE 配置读取哪个文件作为请求header的内容 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_FILE, char *filename_ptr); ``` ##### **功能描述** 读取请求header的一种方式:通过文件名去获取请求header。 该配置项用于配置qurl客户端读取请求header使用的文件,指定上传的文件,类型:`char *`。 指定上传的请求头数据,输入文件名,上传其文件内容。内部会读完整个文件作为请求header发送到对端。 ##### **默认值** QOSA_NULL ##### 适用协议 HTTP、SMTP ##### 依赖项 `QURL_OPT_UPLOAD_HEAD_RAW` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_FILE, "UFS:test.txt"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_DATA 配置读取请求body的指针地址 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_DATA, void *upload_data_ptr); ``` ##### **功能描述** 读取请求body的一种方式:通过指针去获取请求body。 该配置项用于配置qurl客户端读取请求body使用的指针地址,类型:`void *`。 用户侧设置的该指针指定上传的body数据的地址。内部会自动读取指针中的数据作为请求body发送到对端。 直到达到 `QURL_OPT_UPLOAD_SIZE` 中配置的size。 此配置项和 `QURL_OPT_UPLOAD_SIZE` 配合使用。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_UPLOAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_DATA, data1); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_SIZE 配置上传的请求body的size ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_SIZE, long size); ``` ##### **功能描述** 该配置项用于配置qurl客户端以指针的方式读取的请求body的size,类型:long。 指定上传的请求body的size,如 http的 body,ftp的文件大小等等。单位:byte。 内部会自动读取 `QURL_OPT_UPLOAD_DATA` 配置的指针中的数据作为请求body发送到对端。直到达到我们配置的size。 此配置项和 `QURL_OPT_UPLOAD_DATA` 配合使用。 ##### **默认值** -1,不使用 ##### 适用协议 所有协议 ##### 依赖项 `QURL_OPT_UPLOAD_DATA` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_DATA, data1); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, strlen(data1)); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_FILE 配置读取哪个文件作为请求body的内容 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_FILE, char *filename_ptr); ``` ##### **功能描述** 读取请求body的一种方式:通过文件名去获取请求body。 该配置项用于配置qurl客户端读取请求body使用的文件,指定上传的文件,类型:`char *`。 指定上传的请求body数据,输入文件名,上传其文件内容。 内部会读完整个文件作为请求body发送到对端。 ##### **默认值** QOSA_NULL ##### 适用协议 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_FILE, "UFS:test.txt"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_NOBODY 配置是否不需要下载body ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_NOBODY, long nobody); ``` ##### **功能描述** 该配置项用于配置这次请求是否需要下载body,类型:long。 设置为1 告诉qurl在进行下载时不要在输出中包含body部分。对于HTTP(S),这使得qurl执行 HEAD 请求。对于大多数其他协议,这意味着不要求传输body数据。 当 http 请求方法一直设置为HEAD 时,对于设置了 `QURL_OPT_NOBODY` 的 HTTP 操作,当再次禁用此选项 (0) 则会使其再次成为GET 请求。如果你想要执行 get 请求,请直接配置 `QURL_OPT_HTTP_GET`。 启用 `QURL_OPT_NOBODY` 意味着要求下载没有body的文件。 如果你没有开启此配置项而且你使用的是 HTTP 进行传输,正常除了 HEAD 方法,其他方法你都会得到body(除非资源和服务器为你请求的特定URL发送了一个零字节的正文)。 因为不管你配置的 http 请求方法什么,这个设置打开会修改 http的方法为head,所以建议先设置这个配置项,再去配置http 方法。 ##### **默认值** 0,需要下载body。 ##### 适用协议 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* get us the resource without a body - use HEAD */ qurl_core_setopt(core, QURL_OPT_NOBODY, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_DIRLISTONLY 配置FTP或者 sftp 读取目录的时候只读取文件名称 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_DIRLISTONLY, long list_only); ``` ##### **功能描述** 对于基于 FTP和SFTP的URL,设置为1 告诉qurl库只列出目录中文件的名称,而不列出其他细节信息,包括文件大小、日期等完整目录列表。 `QURL_OPT_DIRLISTONLY` 配置为1,FTP发送的是 NLST 指令。不配置的情况下发送的是 LIST 指令。 注意:对于FTP,这会导致向 FTP 服务器发送 NLST 命令。请注意,一些 FTP 服务器在对 NLST的响应中只列出文件;它们可能不包括子目录和符号链接。 将此选项设置为1 也意味着即使URL没有以斜线结尾,也会显示目录列表,如果没有配置这个配置项,那么URL就必须以斜线结尾。 如果您还使用 `QURL_OPT_WILDCARDMATCH`,请不要使用此选项,因为它会让该配置项无效。 ##### **默认值** 0,不使能此功能。 ##### 适用协议 FTP、FTPS、SFTP ##### 依赖项 `QURL_OPT_WILDCARDMATCH` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/"); qurl_core_setopt(core, QURL_OPT_DIRLISTONLY, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_IGNORE_CONTENT_LENGTH 配置下载的时候忽略数据长度 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_IGNORE_CONTENT_LENGTH, long ignore_cl); ``` ##### **功能描述** 忽略下载的内容长度(ignore content length)。 这个配置项主要是针对一些特殊服务器或者特殊资源而配置的,提供一些特殊的支持。 对于 http 请求,如果 ignore_cl设置为1,启用此配置项,则忽略 HTTP 响应中的 Content-Length 标头。 对于 ftp 请求,如果 ignore_cl设置为1,启用此配置项,则不会在 FTP 传输中请求或者使用文件大小。 这个配置项对比较旧的 web 服务器进行的 HTTP 传输非常有用,因为这些服务器传输超过 2GB的文件内容会报告错误的文件长度。如果使用此选项,qurl将无法知道文件的长度,那么就可以在服务器传输完整个文件结束连接时停止下载。不受这个错误的 CONTENT_LENGTH 影响。 这个配置项可以支持 FTP 下载增长中的文件。它阻止状态机从服务器请求文件大小。如果文件大小未知,则下载将继续,直到服务器终止它;否则,如果接收的字节数超过报告的文件大小,客户端将停止,报告qurl错误码。 注意:对于 “TYPE A” 传输请求大小是没有意义的,因为服务器不报告转换后的大小。因此无需关心这个配置项。 没必要的时候不要使用此配置项。 ##### **默认值** 0,不使能此功能。 ##### **适用协议** HTTP、HTTPS、FTP、FTPS ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* we know the server is silly, ignore content-length */ qurl_core_setopt(core, QURL_OPT_IGNORE_CONTENT_LENGTH, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TRANSFERTEXT 配置FTP传输的数据格式 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TRANSFERTEXT, long prefer_ascii); ``` ##### **功能描述** 指定首选的数据传输方式。ASCII or 二进制 FTP传输的数据格式:0就是text,1就是ASCII。 参数设置为1 告诉qurl库使用ASCII 模式进行 FTP 传输,而不是默认的二进制传输。 当我们在两个不同系统间传输文本数据的时候,如果我们传输的某些字符(如换行符)在两个系统之间的格式不一样,可以使用这个配置项配置为ASCII 模式进行传输。 通过 FTP 进行 ASCII 传输时,qurl不会进行完整的 ASCII 转换。这是一个已知限制。qurl只是将模式设置为ASCII 并执行标准传输。 ##### **默认值** 0,不使能,使用二进制传。 ##### 适用协议 FTP、FTPS、SFTP ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/textfile"); qurl_core_setopt(core, QURL_OPT_TRANSFERTEXT, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_APPEND 配置以增量的方式上传文件到远端 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_APPEND, long remote_append); ``` ##### **功能描述** 该配置项设置为1 告诉qurl库要以附加的方式写入到远程文件而不是覆盖它。这仅在 FTP 上传文件时有用。 ##### **默认值** 0,不使能,使用覆盖的方式上传文件。 ##### **适用协议** FTP、FTPS、SFTP ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long start_pos = 100; long file_maxsize = 1024; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/to/newfile"); qurl_core_setopt(core, QURL_OPT_APPEND, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FILETIME 配置获取远程文档的文件修改时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FILETIME, long get_filetime); ``` ##### **功能描述** 使能该配置项,将尝试获取远程文档的文件修改时间。之后,我们可以使用 `qurl_core_getinfo()` 函数来获取该时间。 如果为1,qurl将尝试在此操作中获取远程文档的修改时间。这要求远程服务器发送时间或回复时间查询命令。带有 `QURL_INFO_FILETIME` 参数的 `qurl_core_getinfo()` 函数可以在传输后用于提取接收时间(如果有的话)。 ##### **默认值** 0,不使能。 ##### **适用协议** HTTP、HTTPS、FTP、FTPS ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long filetime = -1; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/path.html"); qurl_core_setopt(core, QURL_OPT_FILETIME, 1L); if(qurl_core_perform(core) == QURL_OK); { if(qurl_core_getinfo(qurl, QURL_INFO_FILETIME, &filetime) == QURL_OK) { if(filetime >= 0) { time_t tmp_time = (time_t)filetime; const struct tm *tm; tm = gmtime(&tmp_time); debug_printf("%d-%d-%d %d:%d:%d\n\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); tmp_file_time_len = qosa_snprintf( tmp_file_time, 64, "%04d%02d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); } } } qurl_core_delete(core); } } ``` #### QURL_OPT_TIMEVALUE 配置需要重新下载文档的时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TIMEVALUE, long timevalue); ``` ##### **功能描述** 该配置项用于设置一个时间值,这应该是自 1970 年 1 月 1 日以来以秒计的时间,该时间值将与远程文档的最后修改时间进行比较,以确定是否需要重新下载该文档。 该选项通常与 `QURL_OPT_TIMECONDITION` 选项一起使用。 在具有 32 位 “long” 变量的系统(如Windows)上,此选项不能设置2038 年之后的日期。 ##### **默认值** 0 ##### **适用协议** HTTP、HTTPS ##### 依赖项 `QURL_OPT_TIMECONDITION` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* January 1, 2020 is 1577833200 */ qurl_core_setopt(core, QURL_OPT_TIMEVALUE, 1577833200L); /* If-Modified-Since the above time stamp */ qurl_core_setopt(core, QURL_OPT_TIMECONDITION, QURL_TIMECOND_IFMODSINCE); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TIMECONDITION 配置获取文件的时间条件 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TIMECONDITION, long timecondition); ``` ##### **功能描述** 该配置项用于配置获取文件的时间条件,参考 `qurl_timecond_t` 定义(详见附录)。 ```{image} images/image_M5Srbjw2koqbuBxLLCjc2gSUn3c.webp :width: 1460px :height: 220px ``` 传递一个 long 类型的参数。timecondition定义了如何处理 `CURLOPT_TIMEVALUE` 时间值。您可以将此参数设置为 `QURL_TIMECOND_IFMODSINCE` 或 `QURL_TIMECOND_IFUNMODSINCE` 或 `QURL_TIMECOND_LASTMOD`。 文件的最后修改时间并不总是已知的,在这种情况下,即使满足给定的时间条件,此功能也不起作用。 该选项通常与 `QURL_OPT_TIMEVALUE` 选项一起使用。 ##### **默认值** QURL_TIMECOND_NONE(0) ##### **适用协议** HTTP、HTTPS ##### 依赖项 `QURL_OPT_TIMEVALUE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* January 1, 2020 is 1577833200 */ qurl_core_setopt(core, QURL_OPT_TIMEVALUE, 1577833200L); /* If-Modified-Since the above time stamp */ qurl_core_setopt(core, QURL_OPT_TIMECONDITION, QURL_TIMECOND_IFMODSINCE); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_MAXFILESIZE 配置文件下载允许的最大SIZE ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_MAXFILESIZE, long max_filesize); ``` ##### **功能描述** 设置下载文件的最大SIZE。x < 0 :无效,返回参数设置错误;x = 0 :无限制(缺省值);x > 0 :指定限制值 。 传递一个 long 参数。这指定了要下载的文件的最大可接受大小(以字节为单位)。如果发现请求的文件大于此值,则传输将中止,并返回 `QURL_ECODE_FILESIZE_EXCEEDED`。传递零大小会禁用此功能,传递负数会报错 `QURL_ECODE_PARAM_INVALID`。 在下载开始之前,文件大小并不总是已知的,对于此类传输,此选项无效,即使文件传输最终大于此给定限制。 即使传输已经开始,如果已下载的文件大小达到这个最大值,此选项也会停止正在进行的传输。 ##### **默认值** 0,没有限制。 ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* refuse to download if larger than 1000 bytes */ qurl_core_setopt(core, QURL_OPT_MAXFILESIZE, 1000L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_CUSTOMREQUEST 配置用户自定义的请求命令 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_CUSTOMREQUEST, char *customrequest_ptr); ``` ##### **功能描述** 用户自定义的请求命令,如 HTTP的POST、GET方法;FTP替换 LIST或NLIST 命令。 将指向以 null 结尾的字符串的指针作为参数传递。 当通过设置 `CURLOPT_CUSTOMREQUEST` 来更改请求方法时,您实际上并没有改变qurl的行为或动作:您只更改了请求中发送的实际字符串。 qurl在其请求中传递逐字字符串,而不使用任何过滤器或其他安全防护措施。这包括空格和控制字符。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。将其设置为QOSA_NULL 可对其清空。 此选项可用于指定请求: - **Http**:在执行基于 HTTP的请求时,替换掉 GET或HEAD。这对于执行 HTTP DELETE 请求特别有用。 - **FTP**:执行 FTP 目录列表时,替换掉 LIST和NLST。 - **SMTP**:在发出基于 SMTP的请求时,替换掉 HELP或VRFY。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP、HTTPS、FTP、FTPS、SMTP、SMTPS ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftps://112.31.84.164/jamie_test/"); qurl_core_setopt(core, QURL_OPT_PORT, 8311L); qurl_core_setopt(core, QURL_OPT_USERNAME, "test"); qurl_core_setopt(core, QURL_OPT_PASSWORD, "test"); qurl_core_setopt(core, QURL_OPT_CUSTOMREQUEST, "LIST ."); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### 中间件配置项 #### QURL_OPT_BOUND_THREAD配置将qurl业务绑定到用户自定义的线程 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_BOUND_THREAD , long thread_id); ``` ##### **功能描述** 传递一个 long 参数作为线程id。将qurl实例锁定到我们指定的线程。0:锁定到当前调用 `qurl_core_setopt()` 的线程。 用于线程限制,对qurl实例的资源进行保护,其他线程调用该qurl实例的资源会报错 `QURL_ECODE_NO_PERMISSION`。 该选项通常与 `QURL_OPT_BOUND_THREAD_CTRL` 选项一起使用。 ##### **默认值** 0。在未配置 `QURL_OPT_BOUND_THREAD_CTRL` 的情况下,此配置项不生效。 ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_BOUND_THREAD_CTRL` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* 绑定到当前线程 */ qurl_core_setopt(core, QURL_OPT_BOUND_THREAD, 0); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_BOUND_THREAD_CTRL 配置qurl业务绑定线程的方式 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_BOUND_THREAD_CTRL , long arg); ``` ##### **功能描述** 传递一个 long 参数表示qurl业务绑定线程的方式。 - 0:解绑。 - 1:绑定用户指定线程。 - 2:绑定qurl业务创建线程,也就是调用 `qurl_core_create()` 的线程。 该选项通常与 `QURL_OPT_BOUND_THREAD` 选项一起使用。用于线程限制,对qurl实例的资源进行保护,其他线程调用该qurl实例的资源会报错 `QURL_ECODE_NO_PERMISSION`。 ##### **默认值** 0。不绑定线程。 ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_BOUND_THREAD` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* 绑定到调用qurl_core_create创建qurl实例的线程,也就是当前线程 */ qurl_core_setopt(core, QURL_OPT_BOUND_THREAD_CTRL , 2); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_REUSE_HOLD 配置是否在使用后立即关闭连接 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_REUSE_HOLD, long reuse_hold); ``` ##### **功能描述** 此配置项用于配置是否持久占用conn。用于解绑conn逻辑。 传递一个 long 类型的参数。设置为1,使qurl在传输完成时显式关闭连接。通常,qurl在完成一次传输后会保持所有连接的活动状态,以防后续的传输可以重用它们。 应谨慎使用此选项,并且只有当您了解它的作用时才能使用,因为它会严重影响性能。 设置为0,使qurl不去显式关闭连接,以便以后可能重用(默认行为)。 ##### **默认值** 0。默认使用后不去立即关闭连接。 ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_REUSE_HOLD, 1L); /* 多次执行qurl_core_perform会创建一个新的conn */ qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_CONN_IDLE_TIMEOUT_MS 配置允许重用连接的最大空闲时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_CONN_IDLE_TIMEOUT_MS, long timeout); ``` ##### **功能描述** 传递一个 long 参数作为超时时间。 允许现有连接空闲的最长时间(秒),在这个时间内可以请求重用这个conn,超过这个时间删除这个conn,需要重新建立新的连接。 “连接缓存”保存以前使用的连接。当要执行新请求时,qurl会考虑任何匹配的连接以供重用。`QURL_OPT_CONN_IDLE_TIMEOUT_MS` 限制可防止qurl尝试太旧的连接以供重用,因为旧连接不起作用的风险更高,因此尝试它们会导致性能损失,有时还会因为难以弄清楚情况而导致服务损失。如果在缓存中发现的连接时间早于此设置的时间,则会将其关闭。 ##### **默认值** 120 s ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_CONN_IDLE_TIMEOUT_MS, 120 *1000L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_CONN_MAXLIFETIME_MS 配置conn的最长生命周期 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_CONN_MAXLIFETIME_MS, long timeout); ``` ##### **功能描述** 传递一个 long 参数作为超时时间。 允许现有连接的最大连接时长(秒),计算的是从conn开始建立到现在的时间差,超过这个超时时间删除这个conn。 定期清除长时间占据资源的旧的conn。更新conn防止连接太旧导致使用出现异常。 ##### **默认值** 0。没有最大连接时长的限制。 ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_CONN_MAXLIFETIME_MS, 0); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### Socket配置项 #### QURL_OPT_SOCKET_SO_LINGER 配置socket SO_LINGER选项 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_SO_LINGER, qurl_so_linger_t *linger); ``` ##### **功能描述** 该配置项用于配置socket SO_LINGER选项,参考 `qurl_so_linger_t` 定义(详见附录)。 不同的 `l_onoff` 和 `l_linger` 对 socket close的不同影响如下所示: ```{image} images/image_DUpVbdeIcoRVqhxt9xdckB1YncS.webp :width: 1460px :height: 220px ``` ##### **默认值** 0。不使用 `SO_LINGER` 选项。 ##### **适用协议** TCP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_so_linger_t so_linger = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* close socket的时候,会尝试把剩余的数据发送出去,阻塞直到5秒超时或者数据发送完成 */ so_linger.l_onoff = 1; so_linger.l_linger = 5; qurl_core_setopt(core, QURL_OPT_SOCKET_SO_LINGER, &so_linger); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_SO_KEEPALIVE 配置开启 TCP的保活功能。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_SO_KEEPALIVE , long so_keepalive); ``` ##### **功能描述** `SO_KEEPALIVE` 配置,开启或关闭 TCP的保活功能。 传递一个 long 参数,如果设置为1,则开启 TCP 保活功能。发送探针的延迟和频率可以通过 `QURL_OPT_SOCKET_TCP_KEEPIDLE`、`QURL_OPT_SOCKET_TCP_KEEPINTVL` 和 `QURL_OPT_SOCKET_TCP_KEEPCNT` 选项进行控制。 设置为0 (默认行为)以禁用TCP 保活探测。 ##### **默认值** 0。不开启 TCP 保活功能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_TCP_KEEPIDLE`、`QURL_OPT_SOCKET_TCP_KEEPINTVL`、`QURL_OPT_SOCKET_TCP_KEEPCNT` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_TCP_KEEPIDLE 配置TCP 保活功能空闲时间。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_TCP_KEEPIDLE , long tcp_keepidle); ``` ##### **功能描述** TCP_KEEPIDLE配置,保活功能空闲时间。 传递一个 long 参数设置在连接空闲时等待发送保活探测的延迟(秒)。并非所有操作系统都支持此选项。 如果开启了 TCP 保活功能,在这段时间内 TCP 层没有任何数据收发,就会发送保活探测确认对端是否还在。 它接受的最大值是 2147483648。 ##### **默认值** 0,不使能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_SO_KEEPALIVE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_TCP_KEEPINTVL 配置TCP 保活功能报文间隔。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_TCP_KEEPINTVL , long tcp_keepintvl); ``` ##### **功能描述** `TCP_KEEPINTVL` 配置,保活功能报文间隔。 传递一个 long 参数设置发送保活探测之间的等待间隔(秒)。并非所有操作系统都支持此选项。 配置好了 `tcp_keepintvl`,发完一个探针在 `tcp_keepintvl` 时间内没有收到对端对探针的回应,就会再次发起新的探针。 它接受的最大值是 2147483648。 ##### **默认值** 0,不使能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_SO_KEEPALIVE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_TCP_KEEPCNT 配置保活功能报文次数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_TCP_KEEPCNT , long tcp_keepcnt); ``` ##### **功能描述** TCP_KEEPINTVL配置,保活功能报文次数。 传递一个 long 参数设置在断开连接之前要发送的探测数。并非所有操作系统都支持此选项。 此选项接受的最大值是您的系统允许的任何值。 ##### **默认值** 0,不使能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_SO_KEEPALIVE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### TLS配置项 #### QURL_OPT_TLS_CFG 配置TLS 连接用到的配置信息。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TLS_CFG , qurl_tls_cfg_t * tls_cfg_ptr); ``` ##### **功能描述** 该配置项可以配置TLS 连接用到的相关参数,传入一个 `qurl_tls_cfg_t` 类型的指针,里面保存着我们设置好的 SSL配置参数,内部会利用这个指针保存的的 TLS 相关参数发起 TLS 连接。TLS的配置参数参考 `qurl_tls_cfg_t`。 注意:传的是指针,实体资源由用户维护。 ##### **默认值** `version` = QURL_TLS_VERSION_ALL; `ciphersuites` = QOSA_NULL; `verify`= QURL_TLS_VERIFY_NONE; `ca_cert_path_slist` = QOSA_NULL; `own_cert_path_ptr` = QOSA_NULL; `own_key_path_ptr` = QOSA_NULL; `own_key_pwd_ptr` = QOSA_NULL; `negotiate_timeout` = 30; `bits.sni_enable` = false; `bits.session_match` = true; `bits.session_share` = true; ##### **适用协议** HTTPS、FTPS、SMTPS ##### **依赖项** `qurl_tls_cfg_init` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_tls_cfg_t tls_cfg = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/file.ext"); qurl_tls_cfg_init(&tls_cfg); tls_cfg.negotiate_timeout = 30; qurl_core_setopt(core, QURL_OPT_TLS_CFG, &tls_cfg); qurl_core_setopt(core, QURL_OPT_TLS_USETLS, QURL_USETLS_ALL); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TLS_USETLS 配置使用SSL/TLS 进行传输。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TLS_USETLS , long use_tls); ``` ##### **功能描述** 该配置项可以指定连接使用TLS。 传递一个 long 参数设置qurl在传输时使用的 SSL 级别。传递的参数参考 `qurl_usetls_e` 定义(详见附录)。 这些协议都是以纯文本开始并使用STARTTLS 命令升级到 SSL 协议。 ##### **默认值** QURL_USETLS_NONE ##### **适用协议** FTP、SMTP(升级为TLS) ##### **依赖项** `QURL_OPT_TLS_CFG` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_tls_cfg_t tls_cfg = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/file.ext"); qurl_tls_cfg_init(&tls_cfg); tls_cfg.negotiate_timeout = 30; qurl_core_setopt(core, QURL_OPT_TLS_CFG, &tls_cfg); qurl_core_setopt(core, QURL_OPT_TLS_USETLS, QURL_USETLS_ALL); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### FTP 协议配置项 #### QURL_OPT_ACCOUNT 配置登录 FTP 服务器使用的账号。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_ACCOUNT, char *account); ``` ##### **功能描述** 传递一个 char 指针作为参数,该指针应指向用于传输的以 null 结尾的账号。 当 FTP 服务器在提供用户名和密码后要求提供“帐户数据”时,将使用ACCT 命令发送此数据。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 ##### **默认值** QOSA_NULL ##### 协议 FTP ##### 依赖项 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_ACCOUNT, "human-resources"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_FILEMETHOD 配置FTP 获取文件方式。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_FILEMETHOD, qurl_ftp_filemethod_e method); ``` ##### 功能描述 传递一个 long 类型的参数告诉qurl使用哪种方法访问 FTP(S)服务器上的文件。 此选项的存在是因为某些服务器实现不符合标准所规定的工作方式。 指定 FTP 获取文件方式,参考 `qurl_ftp_filemethod_e` 的定义(详见附录)。 ##### 默认值 QURL_FTP_FILEMETHOD_DEFAULT ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/1/2/3/4/new.txt"); qurl_core_setopt(core, QURL_FTP_FILEMETHOD_MULTICWD, QURL_FTP_FILEMETHOD_SINGLECWD); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_AUTH 配置FTP AUTH 认证机制。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_AUTH, qurl_ftp_auth_e auth); ``` ##### 功能描述 传递一个long类型的参数指定 FTP AUTH 认证机制,参考 `qurl_ftp_auth_e` 的定义(详见附录)。 ##### 默认值 QURL_FTP_AUTH_DEFAULT ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/file.txt"); qurl_core_setopt(core, QURL_OPT_FTP_AUTH, QURL_FTP_AUTH_SSL); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_SKIP_PASV_IP 配置PASV 模式下是否忽略服务器下发的指定的数据连接IP。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_SKIP_PASV_IP, long skip); ``` ##### 功能描述 传递一个 long 类型的参数用来配置PASV 模式下是否忽略服务器下发的指定的数据连接IP。如果 `skip` 设置为1,则当qurl进行数据连接时,它指示qurl不要使用服务器在其对qurl的 PASV 命令的 227 响应中建议的 IP 地址。qurl实际上使用了它已经用于控制连接的相同 IP 地址。但是它仍然使用227 响应中的端口号。 此选项允许qurl与由于NAT、防火墙或功能不全而下发错误IP地址的异常服务器或异常的网络正常交互。设置此选项还可以降低恶意服务器滥用各种客户端的风险。 如果使用PORT、EPRT或EPSV 代替PASV,则此选项无效。 ##### 默认值 1 ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/file.txt"); qurl_core_setopt(core, QURL_OPT_FTP_SKIP_PASV_IP, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_PORT 配置FTP 数据连接使用主动模式。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_PORT, char *port_str); ``` ##### 功能描述 将指向以 null 结尾的字符串的指针作为参数传递。它指定使用主动模式进行 FTP 传输,并使用给定的字符串获取用于 FTP PORT 指令发送到对端的 IP 地址和端口号。 ftp_port_ptr字符串接收格式:(ipv4|ipv6|域名|网络接口)?(:端口(-范围)?)? PORT指令告诉远程服务器将使用TCP 连接到我们指定的 IP 地址和端口号。该字符串可以是普通 IP 地址、主机名、网络接口名称(在 Unix 下)、端口范围,也可以只是一个“-”符号,让库使用系统的默认 IP 地址和端口号。 地址后面可以跟“:”来指定端口,也可以跟“-”来指定一个端口范围。如果指定的端口为0,操作系统将选择一个空闲端口。无效的端口/范围设置将被忽略。IPv6地址后跟端口或端口范围必须在括号中。没有端口/范围说明符的 IPv6 地址可以放在括号中。 比如:192.16 [8.1.2]():32000-33000 默认 FTP 操作使用被动模式,不使用PORT 命令。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。通过将此选项设置为NULL,您可以再次禁用PORT 并使用被动版本。 ##### 默认值 QOSA_NULL ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/file.txt"); qurl_core_setopt(core, QURL_OPT_FTP_PORT, ":8888-8889"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_USE_EPRT 配置FTP 数据连接使用扩展的主动模式。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_USE_EPRT, long use_eprt); ``` ##### 功能描述 传入一个 long 类型的数据。如果该值设置为1,它会告诉qurl在主动模式下进行 FTP 数据连接时使用EPRT 命令(由 QURL_OPT_FTP_PORT 启用)。使用EPRT 意味着qurl在使用PORT 之前首先尝试使用EPRT,然后再去尝试使用传统的 PORT 命令。 但如果将零传递给此选项,则不使用EPRT 而只使用纯PORT。 EPRT命令是 FTP 协议中比 PORT 稍新的命令,是首选命令,因为它允许使用IPv6。旧的 FTP 服务器可能不支持它,这就是为什么qurl有回退机制。 如果服务器是 IPv6 主机,则此选项无效,因为此时需要EPRT,qurl会自动发送EPRT,而且服务器只认EPRT。 需要配置了QURL_OPT_FTP_PORT,启动了主动模式,这个配置项才有意义。 ##### 默认值 1,默认使用扩展命令 ##### 协议 FTP ##### 依赖项 QURL_OPT_FTP_PORT ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/file.txt"); /* contact us back, aka "active" FTP */ qurl_core_setopt(core, QURL_OPT_FTP_PORT, "-"); /* FTP the way the neanderthals did it */ qurl_core_setopt(core, QURL_OPT_FTP_USE_EPRT, 0L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_USE_EPSV 配置FTP 数据连接使用扩展的被动模式。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_USE_EPSV, long use_epsv); ``` ##### 功能描述 传入一个 long 类型的数据。如果该值设置为1,它会告诉qurl在被动模式下进行 FTP 数据连接时使用EPSV 命令(FTP默认的数据连接方式)。使用EPSV 意味着qurl在使用PASV 之前首先尝试使用EPSV 命令,然后再去尝试使用传统的 PASV 命令。 如果将零传递给此选项,则它不使用EPSV,只使用普通PASV。 EPSV命令是 FTP 协议中比 PASV 稍新的附加命令,是首选命令,因为它允许使用IPv6。旧的 FTP 服务器可能不支持它,这就是为什么qurl有回退机制。 如果服务器是 IPv6 主机,则此选项无效,因为此时需要EPSV,qurl会自动发送EPSV,而且服务器只认EPSV。 只有在被动模式下,这个配置项才有意义。 ##### 默认值 1,默认使用扩展命令 ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/old-server/file.txt"); /* let's shut off this modern feature */ qurl_core_setopt(core, QURL_OPT_FTP_USE_EPSV, 0L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FTP_USE_PRET 配置在 PASV 命令之前使用PRET 命令。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FTP_USE_PRET, long use_pret); ``` ##### 功能描述 配置是否在 PASV 命令之前使用PRET 命令。PRET命令用于告知 FTP 服务器在被动模式下准备接收数据。 传递一个 long 参数。如果这个配置项配置为1,则它告诉qurl在 PASV(和EPSV) 之前发送 PRET 命令。某些 FTP 服务器,主要是drftpd,在 PASV 模式下需要此非标准命令进行目录列表以及上传和下载。 使用FTP 主动传输模式时改配置项无效。 ##### 默认值 0 ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/old-server/file.txt"); /* a drftpd server, do it */ qurl_core_setopt(core, QURL_OPT_FTP_USE_PRET, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_QUOTE 配置FTP 传输前要运行的 FTP 命令。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_QUOTE, qurl_slist_t quote_slist); ``` ##### 功能描述 用户输入的命令链表。在 FTP 连接建立后,且在每次文件传输之前都发送指定的 QUOTE 命令。 在请求之前,将指向 FTP或SFTP 命令链接列表的指针传递给服务器。这是在发出任何其他命令之前完成的(甚至在 FTP的 CWD 命令之前)。链表应该是正确添加 'qurl_slist_t' 条目的有效列表,并正确填充文本字符串。使用'qurl_slist_add_strdup' 创建列表,使用'qurl_slist_del_all' 在使用后释放它。 多次使用此选项会使最后一组列表覆盖之前的列表。将其设置为QOSA_NULL 可以清除之前配置的 FTP 命令。qurl内部不会复制这个列表,所以应用需要一直保存直到传输完成之后才能释放。 当与FTP 服务器对话时,在命令前加上星号(*),即使命令失败,qurl也会继续运行,因为默认情况下qurl会在第一次失败时停止。 发送的 FTP 命令是否有效取决于服务器(有关强制命令的列表,请参阅RFC 959)。 qurl不会检查、解析或“理解”使用此选项传递给服务器的命令。如果您使用QUOTE 令更改连接状态、工作目录或类似操作,qurl库实际上是不会知道的。 FTP或SFTP的路径参数可以使用单引号或双引号来区分空格是参数分隔符还是路径的一部分。例如,使用QUOTE 命令发送sftp重命名,如下所示: "rename 'test/_upload.txt' 'test/Hello World.txt'" ##### 默认值 QOSA_NULL ##### 协议 FTP、SFTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_slist_t headers = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/foo.bin"); headers = qurl_slist_add_strdup(headers, "RNFR source-name"); headers = qurl_slist_add_strdup(headers, "RNTO new-name"); qurl_core_setopt(core, QURL_OPT_QUOTE, headers); qurl_core_perform(core); qurl_slist_del_all(headers); qurl_core_delete(core); } qurl_global_deinit(); } ``` #### QURL_OPT_POSTQUOTE 配置传输后要运行的 FTP 命令。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_POSTQUOTE, qurl_slist_t postquote_slist); ``` ##### 功能描述 用户输入的命令链表。在 FTP 传输完成之后,但仍处于连接状态时,发送指定的 QUOTE 命令。 在完成 FTP 传输请求后,将指向 FTP或SFTP 命令列表的指针传递给服务器。只有在没有发生错误的情况下才会发出命令。链表应该是正确添加 'qurl_slist_t' 条目的有效列表,并按照 QURL_OPT_QUOTE的描述正确填写。 多次使用此选项会使最后一组列表覆盖之前的列表。将其设置为QOSA_NULL 可以清除之前配置的 FTP 命令。 qurl内部不会复制这个列表,所以应用需要一直保存直到传输完成之后才能释放。 ##### 默认值 QOSA_NULL ##### 协议 FTP、SFTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_slist_t headers = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/foo.bin"); headers = qurl_slist_add_strdup(headers, "RNFR source-name"); headers = qurl_slist_add_strdup(headers, "RNTO new-name"); qurl_core_setopt(core, QURL_OPT_POSTQUOTE, headers); qurl_core_perform(core); qurl_slist_del_all(headers); qurl_core_delete(core); } qurl_global_deinit(); } ``` #### QURL_OPT_PREQUOTE 配置FTP 传输前运行的命令。 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_PREQUOTE, qurl_slist_t prequote_slist); ``` ##### 功能描述 用户输入的命令链表。在 FTP 连接建立后,但还未进行实际文件传输之前,发送指定的 QUOTE 命令。 设置传输类型后,将指向 FTP 命令链表的指针传递给服务器。链表应该是正确添加 'qurl_slist_t' 条目的有效列表,按照 QURL_OPT_QUOTE的描述正确填写。 多次使用此选项会使最后一组列表覆盖之前的列表。将其设置为QOSA_NULL 可以清除之前配置的 FTP 命令。 qurl内部不会复制这个列表,所以应用需要一直保存直到传输完成之后才能释放。 虽然 QURL_OPT_QUOTE和QURL_OPT_PREQUOTE 适用于SFTP,但此选项不适用。 ##### 默认值 QOSA_NULL ##### 协议 FTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_slist_t headers = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/foo.bin"); headers = qurl_slist_add_strdup(headers, "SYST"); qurl_core_setopt(core, QURL_OPT_PREQUOTE, headers); qurl_core_perform(core); qurl_slist_del_all(headers); qurl_core_delete(core); } qurl_global_deinit(); } ``` ## 结构体 ### qurl_tls_cfg_t ```c /** * @struct qurl_tls_cfg_t * @brief TLS配置参数 */ struct qurl_tls_cfg_s { qurl_tls_version_e version; /*!< TLS版本 */ int *ciphersuites; /*!< 加密套件列表,遇到0x0000为止,如果指针为NULL,表示支持所有加密套件。 */ qurl_tls_verify_e verify; /*!< 证书验证模式 */ qurl_slist_t ca_cert_path_slist; /*!< CA证书路径单向链表 */ char *own_cert_path_ptr; /*!< 客户端公钥数字证书路径 */ char *own_key_path_ptr; /*!< 客户端私钥文件路径 */ char *own_key_pwd_ptr; /*!< 客户端私钥密码串 */ long negotiate_timeout; /*!< 握手超时时间,单位:s */ int ignore_invalid_certsign; /*!< 忽略无效的证书签名 */ int ignore_multi_certchain_verify; /*!< 忽略多级证书链校验 */ unsigned int ignore_certitem; /*!< 是否忽略对证书的某项校验 */ int ssl_log_debug; /*!< 是否打印调试日志 */ int mfl_support; /*!< 是否支持MFL */ int mfl_size; /*!< MFL长度 */ qurl_tls_cfg_bits_t bits; }; typedef struct qurl_tls_cfg_s qurl_tls_cfg_t; ``` ### qurl_so_linger_t ```c /** * @struct qurl_so_linger_t * @brief socket opt SO_LINGER选项配置 */ struct qurl_so_linger_s { int l_onoff; /* 0 = off, nozero = on */ int l_linger; /* linger time, second */ }; typedef struct qurl_so_linger_s qurl_so_linger_t; ``` ## 枚举 ### qurl_ecode_e ```c /** * @enum qurl_ecode_e * @brief qurl全局错误码 */ typedef enum { QURL_OK = 0, QURL_ECODE_OK = 0, QURL_ECODE_FAILURE = (1 | QURL_CFG_ECODE_BASIC_NUM | 0x80000000), QURL_ECODE_PARAM_INVALID, QURL_ECODE_NO_MEMORY, QURL_ECODE_NO_PERMISSION, QURL_ECODE_OVER_LIMIT, QURL_ECODE_BUF_TOO_SMALL, QURL_ECODE_NO_URL, QURL_ECODE_NO_DATA_SEND, QURL_ECODE_PERFORM_TIMEOUT, QURL_ECODE_LOCK_TIMEOUT, QURL_ECODE_EVENT_TIMEOUT, QURL_ECODE_TLS_BAD_CONTENT_ENCODING, QURL_ECODE_TRANS_TOO_MANY_REDIRECTS, /*!< 重定向次数太多了 */ QURL_ECODE_CLIENT_WRITE_FAILD, /*!< 把数据写给上层用户时失败 */ QURL_ECODE_PROT_NO_MATCH, /*!< 没匹配到协议接口 */ QURL_ECODE_NO_SUPPORT, /*!< 不支持该功能或配置,如不支持该协议 */ QURL_ECODE_NOT_FIND, QURL_ECODE_UNKNOWN_OPTION, QURL_ECODE_FILESIZE_EXCEEDED, QURL_ECODE_DOWNLOAD_RESUME_ERR, /*!< 续传错误 */ QURL_ECODE_UPLOAD_ERR, /*!< 上传时间错误 */ QURL_ECODE_RANGE_ERR, /*!< range错误 */ QURL_ECODE_WRITE_HEAD_TO_CLIENT_FAILED, QURL_ECODE_NETWORK_ERR, /*!< 网络错误 */ QURL_ECODE_STATE_INVALID, /*!< 状态无效 */ QURL_ECODE_ABORT, /*!< 被终止了 */ QURL_ECODE_THREAD_CREATE_FAILED = (QURL_ECODE_FAILURE + 0x0020), QURL_ECODE_LOCK_CREATE_FAILD, QURL_ECODE_LOCK_DELETE_FAILD, QURL_ECODE_LOCK_LOCK_FAILD, QURL_ECODE_LOCK_UNLOCK_FAILD, QURL_ECODE_EVENT_FAILD, QURL_ECODE_CONN_CONNECT_FAILD = (QURL_ECODE_FAILURE + 0x0030), QURL_ECODE_CONN_CACHE_NOT_MATCH, QURL_ECODE_CONN_ALREADY_ATTACH, QURL_ECODE_CONN_NOT_ATTACH, QURL_ECODE_CONN_NOT_RESOLVE, QURL_ECODE_CONN_NOT_FOUND_IP, QURL_ECODE_CONN_CONNECT_FAILED, QURL_ECODE_CONN_FAMILY_NOT_SUPPORTED, QURL_ECODE_CONN_NOT_FOUND_SOCKFD, QURL_ECODE_CONN_SEND_FAILD, QURL_ECODE_URL_MALFORMED = (QURL_ECODE_FAILURE + 0x0040), QURL_ECODE_URL_MALFORMED_INPUT, QURL_ECODE_URL_TOO_LONG, QURL_ECODE_URL_BAD_PORT_NUMBER, QURL_ECODE_URL_USER_NOT_ALLOWED, QURL_ECODE_URL_NO_SCHEME, QURL_ECODE_URL_NO_USER, QURL_ECODE_URL_NO_PASSWORD, QURL_ECODE_URL_NO_OPTIONS, QURL_ECODE_URL_NO_HOST, QURL_ECODE_URL_NO_PORT, QURL_ECODE_URL_NO_QUERY, QURL_ECODE_URL_NO_FRAGMENT, QURL_ECODE_URL_DECODE, QURL_ECODE_SOCK_SOCK_FAILED = (QURL_ECODE_FAILURE + 0x0050), QURL_ECODE_SOCK_BIND_FAILED, QURL_ECODE_SOCK_CTRL_FAILED, QURL_ECODE_SOCK_LISTEN_FAILED, QURL_ECODE_SOCK_ACCEPT_FAILED, QURL_ECODE_SOCK_CONNECT_FAILED, QURL_ECODE_SOCK_SELECT_FAILED, QURL_ECODE_SOCK_WRITE_FAILED, QURL_ECODE_SOCK_READ_FAILED, QURL_ECODE_SOCK_CLOSE_FAILED, QURL_ECODE_SOCK_CONN_CLOSED, /*!< 连接关闭了 */ QURL_ECODE_SOCK_SETOPT_FAILED, QURL_ECODE_FS_OPEN_ERR = (QURL_ECODE_FAILURE + 0x0060), /*!< 文件打开错误 */ QURL_ECODE_FS_READ_ERR, QURL_ECODE_PP_ERR = (QURL_ECODE_FAILURE + 0x0070), /*!< pingpong错误 */ QURL_ECODE_PP_TIMEOUT, /*!< pingpong超时 */ QURL_ECODE_TLS_INIT_FAILED = (QURL_ECODE_FAILURE + 0x0080), /*!< TLS初始化失败 */ QURL_ECODE_TLS_CREATE_FAILED, /*!< TLS创建失败 */ QURL_ECODE_TLS_READ_ERR, /*!< TLS读取错误 */ QURL_ECODE_TLS_WRITE_ERR, /*!< TLS写错误 */ QURL_ECODE_TLS_WRITE_LEN_ERR, /*!< TLS写入数据的长度有误 */ QURL_ECODE_TLS_READ_AGAIN, /*!< 需要更多的数据才能继续读取。如块解密 */ QURL_ECODE_TLS_WRITE_AGAIN, /*!< 需要更多的空间才能写入发送。如块解密 */ QURL_ECODE_TLS_NEGOTIATE_ERR, /*!< 握手错误 */ QURL_ECODE_TLS_NEGOTIATE_TIMEOUT, /*!< 握手超时 */ QURL_ECODE_TLS_RANDOM_ERR, /*!< 随机数错误 */ QURL_ECODE_TLS_CONFIG_ERR, /*!<配置错误 */ QURL_ECODE_TLS_CONNECT_ERR, /*!< 连接错误 */ QURL_ECODE_TLS_GET_SESSION_FAILED, /*!< 获取SESSION失败 */ QURL_ECODE_TLS_SET_SESSION_FAILED, /*!<设置SESSION失败 */ QURL_ECODE_TLS_PEER_CLOSE_NOTIFY, /*!< 通知准备关闭,正常结果码 */ QURL_ECODE_HTTP_VER_ERR = (QURL_ECODE_FAILURE + 0x0090), /*!< http 版本错误 */ QURL_ECODE_HTTP_METHOD_ERR, /*!< http 请求方法错误 */ QURL_ECODE_HTTP_RESP_HEADER_ERR, /*!< http 响应头错误 */ QURL_ECODE_HTTP_UNSUPPORTED_PROTOCOL, /*!< http 响应不支持的协议 */ QURL_ECODE_HTTP_AUTH_FAILED, /*!< http 身份认证失败 */ QURL_ECODE_HTTP_CHUNKED_BAD, /*!< chunked 错误 */ QURL_ECODE_HTTP_CHUNKED_HEX_TOO_LONG, /*!< chunked hex 字段太长 */ QURL_ECODE_HTTP_CHUNKED_HEX_ILLEGAL, /*!< chunked hex 字段非法 */ QURL_ECODE_HTTP_MULTIFORM_ERR, /*!< 多表单运行异常 */ QURL_ECODE_HTTP_MULTIFORM_STATE_ERR, /*!< 多表单状态机运行异常 */ QURL_ECODE_FTP_ERR = (QURL_ECODE_FAILURE + 0x00A0), /*!< 通用错误 */ QURL_ECODE_FTP_WEIRD_SERVER_REPLY, /*!< 收到了不正常或无法解析的服务器回复 */ QURL_ECODE_FTP_LOGIN_DENIED, /*!< 登录失败,检查用户名、密码和账户信息 */ QURL_ECODE_FTP_REMOTE_ACCESS_DENIED, /*!< 服务器拒绝访问某项服务,原因是缺乏权限。当登录失败时,通常不会返回这个错误代码 */ QURL_ECODE_FTP_REMOTE_FILE_NOT_FOUND, /*!< 未找到远端文件 */ QURL_ECODE_FTP_COULDNT_USE_REST, /*!< 不能使用REST */ QURL_ECODE_FTP_WEIRD_PASV_REPLY, /*!< RASV响应失败 */ QURL_ECODE_FTP_WEIRD_227_FORMAT, /*!< 227响应码格式错误 */ QURL_ECODE_FTP_CMD_QUOTE_ERR, /*!< QUOTE 命令失败 */ QURL_ECODE_FTP_CMD_TYPE_ERR, /*!< TYPE 命令失败 */ QURL_ECODE_FTP_CMD_PRET_ERR, /*!< PRET 命令失败 */ QURL_ECODE_FTP_CMD_PROT_ERR, /*!< PROT 命令失败 */ QURL_ECODE_FTP_CANT_GET_HOST, /*!< 拿不到主机名 */ QURL_ECODE_FTP_COULDNT_RETR_FILE, /*!< 无法从 FTP 服务器上检索(retrieve)文件 */ QURL_ECODE_FTP_REMOTE_DISK_FULL, /*!< 服务器磁盘空间不足 */ QURL_ECODE_FTP_PARTIAL_FILE, /*!< 文件只有部分被下载或上传。这可能是由于网络中断或传输过程中出现其他问题导致的 */ 现其他问题导致的 */ QURL_ECODE_FTP_ACCEPT_ERR, /*!< 主动模式数据连接accept失败 */ QURL_ECODE_FTP_QUOTE_ERR, /*!< QUOTE发送失败 */ QURL_ECODE_SMTP_ERR = (QURL_ECODE_FAILURE + 0x00C0), /*!< 通用错误 */ QURL_ECODE_SMTP_URL_MALFORMAT, /*!< 收到了不正常或无法解析的服务器回复 */ QURL_ECODE_SMTP_AUTH_FAILED, QURL_ECODE_SMTP_UNSPPORTED_PROTOCOL, QURL_ECODE_SMTP_WEIRD_SERVER_REPLY, QURL_ECODE_SMTP_REMOTE_ACCESS_DENIED, QURL_ECODE_SMTP_LOGIN_DENIED, QURL_ECODE_SMTP_RECV_ERROR, QURL_ECODE_SMTP_SEND_ERROR, } qurl_ecode_e; ``` ### close_event_t ```c /** * @enum close_event_t * @brief qurl实例最后关闭的原因 */ typedef enum { // Ordinary TCP Connection Termination Event CLOSE_EVENT_NORMAL = 0, // TCP connection disconnection event caused by the server actively sending a FIN packet CLOSE_EVENT_FIN, // The server actively sends an RST packet, causing the TCP connection to be disconnected event. CLOSE_EVENT_RST, // Event of TCP connection disconnection caused by internal SYN retransmission timeout CLOSE_EVENT_SYN_TIMEOUT, // TCP connection disconnection event caused by internal ACK retransmission timeout CLOSE_EVENT_ACK_TIMEOUT, } close_event_t; ``` ### qurl_info_e ```c /** * @enum qurl_info_e * @brief qurl 信息选项 */ typedef enum { QURL_INFO_URL = 0x2000, /*!< URL */ QURL_INFO_RESP_CODE, QURL_INFO_RESP_CONTENT_LENGTH, /*!< http:是头字段中的 Content-Length,-1表示没有该字段,如chunck。ftp:SIZE命令等。 */ QURL_INFO_FILETIME, /*!< 格林尼治时间 */ QURL_INFO_RESP_DATE, /*!< http:是头字段中的 Date */ QURL_INFO_START_POS, /*!< ftp:分段传输的起始位置*/ } qurl_info_e; ``` ### qurl_opt_e ```c /** * @enum qurl_opt_e * @brief qurl配置选项 */ typedef enum { QURL_OPT_URL = 0x1000, /*!< URL */ QURL_OPT_USERNAME, /*!< 用户名:"user" */ QURL_OPT_PASSWORD, /*!< 密码:"password" */ QURL_OPT_ACCOUNT, /*!< 账户:"xxx" */ QURL_OPT_NETWORK_ID, /*!< 用户指定网络id。<0表示不指定。>=0:参考平台适配说明。 */ QURL_OPT_PORT, /*!< 用户指定端口号 */ QURL_OPT_TIMEOUT_MS, /*!< 整个操作超时时间 */ QURL_OPT_IDLE_TIMEOUT_MS, /*!< 操作时内部空闲超时时间 */ QURL_OPT_ROUSE_CHECK_TIME_MS, /*!< 唤醒检查时间。默认1000ms。主要用于长时间阻塞时,会按这个时间值定时唤醒检查其他事件,如检查是否收到调用qurl_core_abort()触发的事件。 */ QURL_OPT_RESUME_FROM, /*!< 在指定的偏移量处恢复传输,单位:byte。<0时,表示从最后开始往前算。 */ QURL_OPT_RANGE, /*!< 下载范围:如设置HTTP Range 字符串;如ftp指定范围 */ QURL_OPT_REDIRS_CNT_MAX, /*!< 最大的重定向跟随次数 */ QURL_OPT_UPLOAD_HEAD_RAW, /*!< 标记上传HEAD原始数据,不需要内部提供合成。内部会从HEAD_DATA/HEAD_FILE/HEAD_CB中获取,如果三者均未设置也不会抛出异常,这会被判定为从DATA/FILE/CB中一同获取HEAD和BODY。 */ QURL_OPT_WRITE_HEAD_CB, /*!< qurl --> client的响应头回调函数,类型:qurl_write_head_cb */ QURL_OPT_WRITE_HEAD_CB_ARG, /*!< qurl --> client的响应头回调函数的参数,类型:void * */ QURL_OPT_WRITE_CB, /*!< qurl --> client的回调函数,类型:qurl_write_cb */ QURL_OPT_WRITE_CB_ARG, /*!< qurl --> client的回调函数的参数,类型:void * */ QURL_OPT_READ_HEAD_CB, /*!< client --> qurl的请求头回调函数,类型:qurl_read_head_cb */ QURL_OPT_READ_HEAD_CB_ARG, /*!< client --> qurl的请求头回调函数的参数,类型:void * */ QURL_OPT_READ_CB, /*!< client --> qurl的回调函数,类型:qurl_read_cb */ QURL_OPT_READ_CB_ARG, /*!< client --> qurl的回调函数的参数,类型:void * */ QURL_OPT_UPLOAD_HEAD_DATA, /*!< 指定上传的请求头数据,传指针。 */ QURL_OPT_UPLOAD_HEAD_RAW, /*!< 标记上传HEAD原始数据,不需要内部提供合成。内部会从HEAD_DATA/HEAD_FILE/HEAD_CB中获取,如果三者均未设置也不会抛出异常,这会被判定为从DATA/FILE/CB中一同获取HEAD和BODY。 */ QURL_OPT_WRITE_HEAD_CB, /*!< qurl --> client的响应头回调函数,类型:qurl_write_head_cb */ QURL_OPT_WRITE_HEAD_CB_ARG, /*!< qurl --> client的响应头回调函数的参数,类型:void * */ QURL_OPT_WRITE_CB, /*!< qurl --> client的回调函数,类型:qurl_write_cb */ QURL_OPT_WRITE_CB_ARG, /*!< qurl --> client的回调函数的参数,类型:void * */ QURL_OPT_READ_HEAD_CB, /*!< client --> qurl的请求头回调函数,类型:qurl_read_head_cb */ QURL_OPT_READ_HEAD_CB_ARG, /*!< client --> qurl的请求头回调函数的参数,类型:void * */ QURL_OPT_READ_CB, /*!< client --> qurl的回调函数,类型:qurl_read_cb */ QURL_OPT_READ_CB_ARG, /*!< client --> qurl的回调函数的参数,类型:void * */ QURL_OPT_UPLOAD_HEAD_DATA, /*!< 指定上传的请求头数据,传指针。 */ /* 中间件相关 */ QURL_OPT_BOUND_THREAD = (QURL_OPT_URL + 0x0100), /*!< 锁定指定线程。NULL:锁定当前线程。注:这里只是更新可以锁定的线程,是否锁定会按维持原状态。 */ QURL_OPT_BOUND_THREAD_CTRL, /*!< qurl业务锁定线程:0:解绑。1:绑定用户指定线程。2:绑定qurl业务创建线程。 */ QURL_OPT_REUSE_FRESH, /*!< 强制新建conn。即不会从conn池中寻找现存连接。 */ QURL_OPT_REUSE_FORBID, /*!< 强制释放conn。即不会把当前conn共享到conn池中。 */ QURL_OPT_REUSE_HOLD, /*!< 持久占用连接标志。 */ QURL_OPT_CONN_IDLE_TIMEOUT_MS, /*!< conn未被使用的超时时间。类似curl中的CURLOPT_MAXAGE_CONN */ QURL_OPT_CONN_MAXLIFETIME_MS, /*!< conn最长生命 */ /* 协议相关 */ /** 多协议相关 */ /** socket */ QURL_OPT_SOCKET_SO_LINGER = (QURL_OPT_URL + 0x0200), /*!< SO_LINGER配置,传入参数为qurl_so_linger_t。 */ QURL_OPT_SOCKET_SO_KEEPALIVE, /*!< SO_KEEPALIVE配置,开启或关闭TCP的保活功能。 */ QURL_OPT_SOCKET_TCP_KEEPIDLE, /*!< TCP_KEEPIDLE配置,保活功能空闲时间。 */ QURL_OPT_SOCKET_TCP_KEEPINTVL, /*!< TCP_KEEPINTVL配置,保活功能报文间隔。 */ QURL_OPT_SOCKET_TCP_KEEPCNT, /*!< TCP_KEEPINTVL配置,保活功能报文次数。 */ /** TLS */ QURL_OPT_TLS_CFG = (QURL_OPT_URL + 0x0280), /*!<配置TLS CFG。注意:传的是指针,实体资源由用户维护。 */ QURL_OPT_TLS_USETLS, /*!< 指定连接使用TLS,参考 qurl_usetls_e */ /** http 协议 */ QURL_OPT_HTTP_VERSION = (QURL_OPT_URL + 0x0300 ), /*!< 通过设置QURL_OPT_HTTP_VERSION选项,你可以指定要使用的特定HTTP协议版本。这个选项的值应该是下面列出的QURL_HTTP_VERSION*枚举之一。 */ QURL_OPT_HTTP_GET, /*!< 指定为http get请求方法 */ QURL_OPT_HTTP_POST, /*!< 指定为http post请求方法 */ QURL_OPT_HTTP_POST_FORM, /*!< 指定为http post请求方法,专用于多表单 */ QURL_OPT_HTTP_PUT, /*!< 指定为http put请求方法 */ QURL_OPT_HTTP_PUT_FORM, /*!< 指定为http put请求方法,专用于多表单 */ QURL_OPT_HTTP_PATCH, /*!< 指定为http patch请求方法 */ QURL_OPT_HTTP_PATCH_FORM, /*!< 指定为http patch请求方法,专用于多表单 */ QURL_OPT_HTTP_AUTH, /*!< HTTP 身份验证方案。选项参考:qurl_http_auth_e */ QURL_OPT_FOLLOWLOCATION, /*!< 是否允许跟随重定向 */ QURL_OPT_UNRESTRICTED_AUTH, /*!< 是否允许重定向跟随后对新的地址还提供用(户名和密码)进行身份验证 */ QURL_OPT_AUTOREFERER, /*!< 跟随重定向后是否提供 Referer 字段 */ QURL_OPT_POSTREDIR, /*!< 在30x请求后保持POST请求为POST请求;每个位代表一个请求,从301到303。变量参考:qurl_http_redir_e */ QURL_OPT_FORM, /*!< 表单设置,参考 qurl_http_form_cfg_t */ QURL_OPT_HTTP_HEADER, /*!< 用户自定义的http的请求头 */ QURL_OPT_REFERER, /*!<设置HTTP Referer 字符串 */ QURL_OPT_ACCEPT_ENCODING, /*!<设置HTTP Accept-Encoding 字符串 */ QURL_OPT_USER_AGENT, /*!<设置HTTP User-Agent 字符串 */ /** ftp 协议 */ QURL_OPT_FTP_FILEMETHOD = (QURL_OPT_URL + 0x0400), /*!< 指定FTP 获取文件方式,参考:qurl_ftp_filemethod_e */ QURL_OPT_FTP_AUTH, /*!< 指定FTP AUTH 认证机制,参考:qurl_ftp_auth_e */ QURL_OPT_FTP_SKIP_PASV_IP, /*!< PASV模式下是否忽略服务器下发的指定的数据连接IP。默认开启 */ QURL_OPT_FTP_PORT, /*!< 是否启用主动模式并指定端口号。(FTP引擎默认被动)。接收格式:(ipv4|ipv6|域名|网络接口)?(:端口(-范围)?)? 注:网络接口暂未实现 */ QURL_OPT_FTP_USE_EPRT, /*!< 表示启用或禁用FTP引擎的EPRT命令。默认情况下,它会尝试使用EPRT,然后尝试使用传统的PORT命令。 */ QURL_OPT_FTP_USE_EPSV, /*!< 表示启用或禁用FTP引擎的EPSV命令。默认情况下FTP引擎会先使用EPSV,再考虑PASV。 */ QURL_OPT_FTP_USE_PRET, /*!< 发送PASV前先发送PRET。 */ QURL_OPT_FTP_TLS_CCC, /*!< 控制FTP连接是否使用CCC以及切换到何种状态,参考:qurl_ftp_ccc_e */ QURL_OPT_QUOTE, /*!< 用户输入的命令链表。在FTP连接建立后,且在每次文件传输之前都发送指定的QUOTE命令 */ QURL_OPT_POSTQUOTE, /*!< 用户输入的命令链表。在FTP传输完成之后,但仍处于连接状态时,发送指定的QUOTE命令。 */ QURL_OPT_PREQUOTE, /*!< 用户输入的命令链表。在FTP连接建立后,但还未进行实际文件传输之前,发送指定的QUOTE命令。 */ QURL_OPT_SMTP_MAIL_FROM, /*!<设置邮件的发送者 */ QURL_OPT_SMTP_MAIL_RCPT, /*!<设置邮件的接收者 */ QURL_OPT_SMTP_MAILAUTH, /*!<设置邮件的接收者 */ QURL_OPT_SMTP_MAIl_RCPT_ALLOWFAILS, /*!<设置邮件的接收者 */ QURL_OPT_SMTP_LOGIN_OPTIONS, /*!< LOGIN 鉴权 */ QURL_OPT_SMTP_XOAUTH2_BEARER, /*!< XOAUTH2 鉴权 */ /** smtp 协议 */ } qurl_opt_e; ``` ### qurl_ftp_filemethod_e ```c /** * @struct qurl_ftp_filemethod_e * @brief FTP 获取文件方式 */ typedef enum { QURL_FTP_FILEMETHOD_MULTICWD, /*!< 表示按照 RFC1738 定义的方式进行多次 CWD(Change Working Directory)操作,然后再进行文件操作 */ QURL_FTP_FILEMETHOD_NOCWD, /*!< 表示直接在完整路径上执行 SIZE、RETR或STOR 操作,而不需要额外的 CWD 操作。 */ QURL_FTP_FILEMETHOD_SINGLECWD, /*!< 表示首先进行一次 CWD 操作,直达目标路径,然后在文件上执行 SIZE、RETR或STOR 操作。 */ } qurl_ftp_filemethod_e; ``` ### qurl_ftp_auth_e ```c /** * @struct qurl_http_auth_e * @brief FTP 安全认证机制。 * 只是优先选择,如选了SSL,AUTH SSL失败后会选择 AUTH TLS继续。 */ typedef enum { QURL_FTP_AUTH_DEFAULT, /*!< 内部决定。 */ QURL_FTP_AUTH_SSL, /*!< AUTH SSL。 */ QURL_FTP_AUTH_TLS, /*!< AUTH TLS。 */ QURL_FTP_AUTH_LAST, /*!< 仅做溢出判断 */ } qurl_ftp_auth_e; ``` ### qurl_usetls_e ```c /** * @struct qurl_usetls_e * @brief 连接是否使用TLS */ typedef enum { QURL_USETLS_NONE, /*!< 不尝试使用TLS,即明文通信。 */ QURL_USETLS_TRY, /*!< 尝试使用TLS,如果 TLS 连接失败,则继续进行非 TLS 连接。 */ QURL_USETLS_CONTROL, /*!< 仅对控制连接使用TLS,如果 TLS 连接失败,则操作失败。 */ QURL_USETLS_ALL, /*!< 对所有通信(包括控制连接和数据连接)都使用TLS,如果 TLS 连接失败,则操作失败。 */ QURL_USETLS_LAST, /*!< 仅做溢出判断 */ } qurl_usetls_e; ``` ### qurl_timecond_t ```c /** * @struct qurl_timecond_t * @brief 获取文件的时间条件 */ typedef enum { QURL_TIMECOND_NONE, /*!< 无时间条件。 */ QURL_TIMECOND_IFMODSINCE, /*!< 如果文件自指定日期或更近以来已被修改,则传输文件。 */ QURL_TIMECOND_IFUNMODSINCE, /*!< 如果文件自指定日期或更近以来未被修改,则传输文件。 */ QURL_TIMECOND_LASTMOD, /*!< 传输文件,如果文件的最后修改日期比指定日期要晚。 */ QURL_TIMECOND_LAST /*!< 仅做溢出判断 */ } qurl_timecond_t; ``` # 应用示例 本章节提供完整的、可直接编译运行的示例代码,帮助开发者快速上手 ### ftp_get_demo FTP文件拉取 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/ftp/ftp_get_demo.c ### ftp_put_demo FTP文件上传 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/ftp/ftp_put_demo.c # 常见问题排查指南 ## 线程安全 - `qurl_global_init()` 和 `qurl_global_deinit()` 线程不安全,需要外部加锁保证安全。 - 当有其他线程正在运行qurl实例时,不能调用 `qurl_global_init()` 或 `qurl_global_deinit()`。 ## 性能提示 - 复用qurl实例以减少连接开销:使用同一个qurl实例进行多次传输,避免频繁创建和删除实例。 - 启用连接复用:设置 `QURL_OPT_REUSE_HOLD` 为0以持久占用连接,减少TCP握手开销。 ## 超时配置建议 - 根据网络环境设置QURL_OPT_TIMEOUT_MS,这个时间为从执行'qurl_core_perform',到此接口返回的最大时间。建议 `QURL_OPT_TIMEOUT_MS` 配置的超时时间要大于 `QURL_OPT_IDLE_TIMEOUT_MS` 和 `QURL_OPT_ROUSE_CHECK_TIME_MS`。这个值的设置和网络状况以及传输的数据大小有关,例如在蜂窝网络中设置为30~60秒。 - 使用 `QURL_OPT_IDLE_TIMEOUT_MS` 避免长时间空闲连接占用资源,建议设置为10~20秒。 ## 错误码 | **问题现象** | **可能原因** | **建议解决方案** | | --- | --- | --- | | `qurl_core_perform()` 返回 `QURL_ECODE_NETWORK_ERR` | 未配置 `QURL_OPT_NETWORK_ID` 或网络未激活 | 确保先注网成功,并根据平台文档正确设置网络ID(通常为1或0) | | `qurl_core_perform()` 返回 `QURL_ECODE_URL_MALFORMED_INPUT` | URL未带scheme(如http://或https://) | 检查URL格式,必须完整包含scheme、主机、端口(可选)和路径 | | `qurl_core_perform()` 返回 `QURL_ECODE_NO_MEMORY` | 动态内存不足 | 检查内存分配,优化资源使用。 | | `qurl_core_perform()` 返回 `QURL_ECODE_CONN_CONNECT_FAILED` | 三次握手失败,可能是dns或者网络的原因 | 检查网络连接和DNS解析。 | | `qurl_core_perform()` 返回 `QURL_ECODE_FTP_LOGIN_DENIED` | 用户名和密码错误 | 验证用户名和密码正确。 | | `qurl_core_perform()` 返回 `QURL_ECODE_FTP_REMOTE_FILE_NOT_FOUND` | 要下载的远端文件不存在 | 确认远程文件路径正确。 | | `qurl_core_perform()` 返回 `QURL_ECODE_TLS_NEGOTIATE_ERR` | tls参数错误 | 检查TLS配置和证书有效性。 | | 请求超时 | 超时时间设置过短或网络状况差 | 适当增大 `QURL_OPT_TIMEOUT_MS` 和 `QURL_OPT_IDLE_TIMEOUT_MS` | | 数据接收不完整或乱码 | 未正确处理回调返回的字节数 | 回调函数必须返回实际处理的字节数(通常等于 `size`),否则传输会中断 | | 内存泄漏 | 未释放slist或未调用delete/deinit | 每次使用 `qurl_slist_add_strdup()` 后必须 `qurl_slist_del_all()`;每个core必须 `qurl_core_delete()` | # 参考附录 ### 术语缩写表 | **缩写** | **全称** | **说明** | | --- | --- | --- | | FTP | File Transfer Protocol | 文件传输协议 | | TCP | Transmission Control Protocol | 传输控制协议 | | SSL | Secure Sockets Layer | 安全套接层 | | TLS | Transport Layer Security | 传输层安全协议 | | FTPS | FTP over SSL/TLS | 基于SSL/TLS的FTP | | SFTP | SSH File Transfer Protocol | 基于SSH的文件传输协议 | | PASV | Passive Mode | 被动模式 | | PORT | Active Mode | 主动模式 | | EPSV | Extended Passive Mode | 扩展被动模式 | | EPRT | Extended Active Mode | 扩展主动模式 | | PDP | Packet Data Protocol | 分组数据协议 | | DNS | Domain Name System | 域名系统 | | MIME | Multipurpose Internet Mail Extensions | 多用途互联网邮件扩展 | | API | Application Programming Interface | 应用程序接口 | | REPL | Read-Eval-Print Loop | 读取-求值-打印循环 | | NAT | Network Address Translation | 网络地址转换 | | IPv4 | Internet Protocol version 4 | 互联网协议第4版 | | IPv6 | Internet Protocol version 6 | 互联网协议第6版 | | ASCII | American Standard Code for Information Interchange | 美国信息交换标准代码 | | CRLF | Carriage Return Line Feed | 回车换行 |