# SMTP ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 概述 本章节介绍SMTP协议的基本概念、要素、流程概述以及消息结构,帮助用户理解SMTP在嵌入式/物联网场景中的应用。 ## 简介 SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是用于从源地址到目的地址传输电子邮件的规范,通过它来控制邮件的中转方式。它属于TCP/IP协议簇,帮助每台计算机在发送或中转信件时找到下一个目的地。 SMTP协议专注于邮件的发送环节,不负责邮件的最终存储和读取(由POP3/IMAP协议处理)。它是电子邮件系统的核心协议,确保了不同邮件服务商之间的邮件互通。 在嵌入式/物联网场景中,SMTP常见用途: - **邮件客户端发送邮件**:SMTP的核心功能是将电子邮件从发送方服务器传输到接收方服务器,支持文本、图片、文档等多种格式的内容传输。Outlook、Thunderbird、Foxmail等桌面邮件客户端以及手机邮件APP都会使用SMTP协议来发送邮件。用户在设置邮件账户时需要配置SMTP服务器地址(如smtp.gmail.com)。 - **网站/应用程序发送自动邮件**:网站的注册验证码、密码重置邮件、订单通知等自动邮件都是通过SMTP协议发送的。开发者通过配置SMTP服务器信息,让程序自动触发邮件发送(如使用Python的smtplib库)。 - **邮件服务器之间的中继转发**:当发送方和接收方使用不同邮件服务商时(如Gmail发送到Outlook),SMTP负责将邮件从发送方服务器转发到接收方服务器,确保跨服务商的邮件互通。 - **企业邮件系统**:企业内部邮件系统(如Microsoft Exchange Server)对外发送邮件时也依赖SMTP协议,以保证与外部邮箱系统的兼容性。 - **批量邮件发送**:虽然大规模邮件发送场景中API方案(如SendGrid、Mailgun)逐渐兴起,但许多系统仍使用SMTP协议进行批量邮件发送,尤其是中小规模的应用。 ## 基本要素 1. **协议架构**:SMTP是基于客户端-服务器(C/S)架构的应用层协议,负责将电子邮件从发送方服务器传递到接收方服务器。 2. **基于文本的协议**:所有命令和响应都是人类可读的ASCII字符串,协议逻辑简单,易于实现和调试。 3. **重试机制**:若接收方服务器暂时不可用,发送方服务器会在一段时间内重复发送(如每15分钟重试一次,持续24小时),避免邮件丢失。 4. **中继转发**:当发送方和接收方使用不同邮件服务商时,SMTP负责将邮件从发送方服务器转发到接收方服务器,实现跨服务商互通。 5. **附件支持**:通过MIME(多用途互联网邮件扩展)协议,SMTP可传输各种格式的附件,本质是将附件编码为文本格式传输。 6. **多收件人发送**:通过多次使用RCPT TO命令,可同时向多个收件人发送邮件。 7. **工作流程**: - 连接建立:客户端连接服务器端口(25、465或587),服务器返回220状态码表示就绪。 - 邮件发送:通过MAIL FROM、RCPT TO、DATA等命令传递发件人、收件人和邮件内容,完成邮件传输。 - 连接关闭:客户端发送QUIT命令,服务器返回221状态码后关闭连接。 8. **核心命令**: - HELO/EHLO:标识客户端身份。 - MAIL FROM:指定发件人。 - RCPT TO:指定收件人。 - DATA:开始传输邮件内容。 - QUIT:关闭连接。 9. **状态码机制**: - 通过标准化状态码(如220表示服务就绪、250表示操作成功、550表示邮箱不存在)反馈执行结果,便于客户端处理或重试。 - 2xx:成功。 - 3xx:需进一步操作。 - 4xx:临时错误(可重试)。 - 5xx:永久错误(不可重试)。 10. **安全特性**: - 支持STARTTLS(将明文连接升级为TLS/SSL加密)和SMTPS(直接建立SSL加密连接),确保邮件内容在传输过程中的安全性。 - 原生支持明文传输(25端口)。 - 可通过STARTTLS升级为加密连接(587端口)。 - 支持SMTPS直接加密连接(465端口)。 - 支持SMTP AUTH机制验证发件人身份,防止服务器被滥用发送垃圾邮件,常见验证方式包括PLAIN、LOGIN和CRAM-MD5。 ## 流程概述 SMTP流程是指网络设备在本地设置好收件人、发件人、SMTP服务器地址之后,再去构建好邮件的内容,然后通过蜂窝网络发送SMTP邮件到接收端服务器的端到端过程。这个过程的核心在于SMTP邮件内容的构建。 SMTP的流程通常包含以下几个步骤: 1. **qurl初始化**: - `qurl_global_init()`:qurl库全局初始化。 - `qurl_core_create()`:创建一个新的qurl实例。 2. **配置qurl相关参数**: - `qurl_core_setopt()`:为给定的qurl实例设置选项。 - QURL_OPT_URL,QURL_OPT_PORT:配置URL和端口号(SMTP服务器的IP地址或域名,SMTP服务器端口)。 - QURL_OPT_USERNAME,QURL_OPT_PASSWORD:配置身份验证的用户名和密码。 - QURL_OPT_SMTP_MAIL_FROM:配置发件人的邮箱地址。 - QURL_OPT_TLS_USETLS:配置SSL类型。 - QURL_OPT_SMTP_MAIL_RCPT:添加收件人。 - QURL_OPT_READ_CB:配置读取邮件内容的回调函数。 3. **配置TLS相关参数**: - `qurl_tls_cfg_init()`:qurl相关的TLS参数初始化。 - TLS相关参数赋值。 - 通过 `qurl_core_setopt()` 和QURL_OPT_TLS_CFG配置TLS相关参数。 4. **发起SMTP请求**: - `qurl_core_perform()`:开始执行阻塞式的网络传输。 5. **激活网络**: - 等待注网成功。 - PDP通道激活。 6. **DNS域名解析**。 7. **发起TCP连接到远端SMTP服务器**: - 客户端连接到接收方邮件服务器的指定端口(如25、465或587)。 - 创建socket。 - 绑定local地址。 - 发起socket连接进行三次握手。 - 服务器返回状态码220表示就绪,客户端发送HELO或EHLO命令标识自身身份。 8. **身份验证**: - 客户端通过SMTP AUTH机制提供用户名和密码(或授权码)进行身份验证。 - 验证成功后,服务器返回状态码250确认。 9. **邮件发送**: - 客户端发送MAIL FROM命令指定我们配置好的发件人邮箱。 - 发送RCPT TO命令指定我们配置好的收件人邮箱(可多次使用)。 10. **在回调里读取发送的邮件内容**: - 添加邮件主题。 - 添加邮件正文。 - 添加邮件附件。 - 发送DATA命令开始传输回调函数里构建好的邮件内容,包括头部和正文。 - 以单独一行的句点(.)结束传输,读取到服务器返回状态码250表示成功。 11. **连接关闭**: - 客户端发送QUIT命令,服务器返回状态码221确认关闭连接。 12. **SMTP请求完成,模块主动断开TCP连接**: - 是否在数据传输完成后立即自动断开连接与QURL_OPT_REUSE_HOLD的配置相关。 13. **qurl去初始化**: - `qurl_slist_del_all()`:释放之前申请的单向链表。 - `qurl_core_delete()`:删除之前创建的qurl实例。 - `qurl_global_deinit()`:qurl库全局去初始化。 ```{image} images/board_OfGBwke4VhZqXzbDs9KcbdaLnUe.jpg :width: 787px :height: 801px :align: center ``` ## 消息结构 SMTP(Simple Mail Transfer Protocol)的消息结构遵循特定的格式规范,主要由 **邮件头部** 和 **邮件正文** 两部分组成,中间用空行分隔。以下是SMTP消息结构的详细说明: ```{figure} images/board_TnmNwxbDdhadzyb7uuOcRpd2nFf.jpg :align: center :alt: image ``` ### 邮件头部(Headers) 邮件头部包含多个字段,每个字段由"字段名: 字段值"的格式组成,常见字段包括:主题(Subject)、发件人(From)、收件人(To)、日期(Date)等字段。 - **From:** 发件人邮箱地址。 - **To:** 收件人邮箱地址。 - **Subject:** 邮件主题。 - **Date:** 邮件发送日期。 - **Content-Type:** 邮件内容类型(如text/plain或text/html)。 - **MIME-Version:** MIME协议版本(通常为1.0)。 ### 邮件正文(Body) 邮件正文是实际要传输的内容,可以是纯文本或HTML格式。当包含附件时,正文会使用MIME(Multipurpose Internet Mail Extensions)格式进行编码,将附件转换为文本形式嵌入邮件中。 ``` +-------------------------------------------------+ MAIL FROM: RCPT TO: DATA Subject: Test Email From: sender@example.com To: recipient@example.com Date: Mon, 1 Jan 2024 12:00:00 +0000 This is the email body. . QUIT +-------------------------------------------------+ ``` ## qurl库简介 模组的SMTP功能是基于底层的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_resete()` 重置qurl实例并进行新的选项配置。 3. 调用 `qurl_core_createe()` 之前必须调用过一次 `qurl_global_inite()`。 4. 如果调用 `qurl_core_createe()` 返回其他 `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 *` | 是 | 无 | 要添加的字符串 | ##### **返回值说明** 如果成功则返回插入完成的新链表,如果失败返回NULL。 ```{note} **注意:** 1. 调用 `qurl_core_setopt()` 将 `slist` 链表配置到qurl内部,使用之后应该调用 `qurl_slist_del_all()` 释放整个单向链表。 2. 为了避免在失败时返回空覆盖现有的非空列表,建议将新列表返回给一个临时变量,该变量可以在更新原始列表指针之前判断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; 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_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; //响应状态码 qurl_global_init(); 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; 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); 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; 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 ##### **函数原型** ```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; 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配置项详解 本章节详细说明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` 有关格式的更多解释,请参阅RFC 3986。 qurl在网络传输开始之前不会验证语法或使用URL。即使您在此处设置了一个疯狂的值,`qurl_core_setopt()` 仍可能返回 `QURL_OK` 。 qurl 不支持根据URL中的IP 地址自动猜测出 scheme 以及默认 scheme,所以URL必须带上 scheme,如果给定的URL缺少 scheme 名称(如“http://”或“ftp://” 等),虽然调用 `qurl_core_setopt()` 配置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; 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_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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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); ``` ##### **功能描述** 传递一个long类型的 `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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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"; qurl_global_init(); 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"; qurl_global_init(); 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"; qurl_global_init(); 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"; qurl_global_init(); 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"; qurl_global_init(); 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"; qurl_global_init(); 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; qurl_global_init(); 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 abody- use HEAD */ qurl_core_setopt(core, QURL_OPT_NOBODY, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_DIRLISTONLY 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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` 定义(详见附录)。 传递一个long 类型的参数。timecondition定义了如何处理 `QURL_OPT_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; qurl_global_init(); 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; qurl_global_init(); 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 结尾的字符串的指针作为参数传递。 当通过设置 `QURL_OPT_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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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的不同影响如下所示: ##### **默认值** 0。不使用SO_LINGER选项。 ##### **适用协议** TCP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_so_linger_t so_linger = {0}; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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; qurl_global_init(); 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`。 ```{note} **注意:** 传的是指针,实体资源由用户维护。 ``` ##### **默认值** `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_matc` h = 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}; qurl_global_init(); 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}; qurl_global_init(); 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); } } ``` ### SMTP 协议配置项 #### QURL_OPT_SMTP_MAIL_FROM 配置邮件的发送者 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SMTP_MAIL_FROM, char *from); ``` ##### 功能描述 将指向以 null 结尾的字符串的指针作为参数传递。配置qurl发送 SMTP 邮件时指定发件人的电子邮件地址。 发件人电子邮件地址应使用尖括号(<>)指定,如果未指定,则会自动添加。 如果未指定此参数,则会向 SMTP 服务器发送一个空地址,这可能会导致电子邮件被拒绝。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为QOSA_NULL 将清除该配置。 ##### 默认值 QOSA_NULL ##### 协议 SMTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "smtp://example.com/"); qurl_core_setopt(core, QURL_OPT_SMTP_MAIL_FROM, "president@example.com"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SMTP_MAIL_RCPT 配置邮件的接收者 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SMTP_MAIL_RCPT, qurl_slist_t rcpt); ``` ##### 功能描述 传递一个指向收件人列表的指针,以传递给服务器。链表应该是正确添加 `qurl_slist_t` 条目的有效列表。使用 `qurl_slist_add_strdup` 创建列表,使用 `qurl_slist_del_all` 在使用后释放它。 qurl内部不会复制这个列表,所以应用需要一直保存直到传输完成之后才能释放。 执行邮件传输时,建议每个收件人都应该包含在一对尖括号(<>)内,如果您不使用尖括号,qurl发现第一个收件人的第一个字符不是尖括号,qurl会假设您只提供了一个电子邮件地址,并将该地址自动添加在尖括号内。 执行地址验证(VRFY命令)时,应将每个收件人指定为用户名或用户名加域(根据 RFC 5321 第 3.5 节)。 执行邮件列表展开(EXPN命令)时,应使用邮件列表名称指定每个收件人,如“朋友”或“伦敦办公室”。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为QOSA_NULL 将清除该配置。 ##### 默认值 QOSA_NULL ##### 协议 SMTP ##### 依赖项 无 ##### 示例 ```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, "smtp://example.com/"); headers = qurl_slist_add_strdup(headers, "root@localhost"); headers = qurl_slist_add_strdup(headers, "person@example.com"); headers = qurl_slist_add_strdup(headers, " NOTIFY=SUCCESS"); qurl_core_setopt(core, QURL_OPT_SMTP_MAIL_RCPT, headers); qurl_core_perform(core); qurl_slist_del_all(headers); qurl_core_delete(core); } } ``` #### QURL_OPT_SMTP_MAILAUTH 配置SMTP 身份验证地址 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SMTP_MAILAUTH, char *auth); ``` ##### 功能描述 将指向以 null 结尾的字符串的指针作为参数传递。该配置项用于指定正在转发到另一个服务器的已提交消息的身份验证地址。 此可选参数允许受信任环境中的协作代理通信单个消息的身份验证,如果应用程序本身是在这种环境中运行的邮件服务器,则只能由应用程序使用qurl。如果应用程序以这种方式运行,并且 AUTH 地址未知或无效,则应为此参数使用空字符串。 与 `QURL_OPT_SMTP_MAIL_FROM` 和 `QURL_OPT_SMTP_MAIL_RCPT` 不同,不应在一对尖括号(<>)内指定地址。但是,如果使用空字符串,则qurl会按照 RFC 2554的要求发送一对括号。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为QOSA_NULL 将清除该配置。 ##### 默认值 QOSA_NULL ##### 协议 SMTP ##### 依赖项 无 ##### 示例 ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "smtp://example.com/"); qurl_core_setopt(core, QURL_OPT_SMTP_MAILAUTH, ""); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SMTP_MAIL_RCPT_ALLOWFAILS 配置允许某些收件人接收失败 ##### 函数原型 ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SMTP_MAIL_RCPT_ALLOWFAILS, long allow_fails); ``` ##### 功能描述 如果 `QURL_OPT_SMTP_MAIl_RCPT_ALLOWFAILS` 设置为1,则允许某些收件人返回失败。 当向多个收件人发送数据时,默认情况下,如果其中任何一个收件人导致 RCPT to 命令返回错误,qurl会中止 SMTP对话。 默认行为可以通过将 `QURL_OPT_SMTP_MAIl_RCPT_ALLOWFAILS` 设置为1来更改,这将使qurl忽略单个收件人的错误,并继续处理其余已接受的收件人。 如果所有收件人都触发 RCPT TO 失败,并且指定了此标志,qurl将中止 SMTP对话,并将收到的错误返回给最后一个RCPT TO 命令。 ##### 默认值 0 ##### 协议 SMTP ##### 依赖项 无 ##### 示例 ```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, "smtp://example.com/"); /* Adding one valid and one invalid email address */ headers = qurl_slist_add_strdup(headers, "person@example.com"); headers = qurl_slist_add_strdup(headers, "invalidemailaddress"); qurl_core_setopt(core, QURL_OPT_SMTP_MAIL_RCPT, headers); qurl_core_setopt(core, QURL_OPT_SMTP_MAIl_RCPT_ALLOWFAILS, 1L); qurl_core_perform(core); qurl_slist_del_all(headers); qurl_core_delete(core); } } ``` ## 结构体定义 #### 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_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, /*!< PASV响应失败 */ 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 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; ``` # 应用示例 ## smtp_no_tls_demo 请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/smtp/smtp_no_tls_demo.c ## smtp_tls_demo 请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/smtp/smtp_tls_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_SMTP_AUTH_FAILED` | 用户名和密码错误 | 检查用户名和密码。 | | `qurl_core_perform` 返回 `QURL_ECODE_SMTP_LOGIN_DENIED` | 登录请求被拒绝 | 确认SMTP服务器是否允许该用户登录。 | | `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()` | # 参考附录 ## 术语缩写表 | **缩写** | **全称** | **说明** | | --- | --- | --- | | SMTP | Simple Mail Transfer Protocol | 简单邮件传输协议 | | POP3 | Post Office Protocol version 3 | 邮局协议第3版 | | TLS | Transport Layer Security | 传输层安全协议 | | SSL | Secure Sockets Layer | 安全套接字层(TLS的前身) | | PDP | Packet Data Protocol | 分组数据协议(蜂窝网络上下文) | | DNS | Domain Name System | 域名系统 | | TCP/IP | Transmission Control Protocol/Internet Protocol | 传输控制协议/互联网协议 | | URL | Uniform Resource Locator | 统一资源定位符 | | IMAP | Internet Message Access Protocol | 互联网消息访问协议 | | MIME | Multipurpose Internet Mail Extensions | 多用途互联网邮件扩展 | | SMTPS | SMTP over SSL/TLS | 基于SSL/TLS的SMTP | | STARTTLS | Start Transport Layer Security | 启动传输层安全 | | ASCII | American Standard Code for Information Interchange | 美国信息交换标准代码 | | CRLF | Carriage Return Line Feed | 回车换行 | | API | Application Programming Interface | 应用程序接口 | | REPL | Read-Eval-Print Loop | 读取-求值-打印循环 |