This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CC3135:处理 HTTPS 客户端时出错

Guru**** 655270 points
Other Parts Discussed in Thread: CC3135MOD
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/wireless-connectivity/wi-fi-group/wifi/f/wi-fi-forum/1119360/cc3135-error-handling-https-client

器件型号:CC3135

您好!

我们将 CC3135MOD 模块与 STM32搭配使用、同时搭配 FreeRTOS。 该模块通过 SPI 连接。  我的应用程序以 HTTPS 客户端的形式运行、并频繁传输数据、例如每10秒传输一次数据。 我想知道处理错误的理想方法。

我的伪代码:

httpTask()

{
    static int16_t retClientConnectStatus{-1};
    static int16_t retClientSetHeaderStatus{-1};
    static int16_t retClientSendStatus{-1};
    static int16_t retClientCreateStatus{-1};
    static int16_t retClientDestroyStatus{-1};
    HTTPClient_extSecParams esParams;
 
    char hostIpAddr[NO_23U_NUMERIC];
    std::array<char, NO_62U_NUMERIC> requestURI;

    requestURI.fill('\0');

    if(retClientCreateStatus != 0)
    {
        HttpClient::httpClientHandle = HttpClient::CreateHttpClient(0, &retClientCreateStatus);
    }


    if(retClientCreateStatus >= 0)
    {
        
        if((retClientConnectStatus < 0))
        {
            esParams.clientCert = nullptr;
            esParams.privateKey = nullptr;
            esParams.rootCa = ROOT_CA_CERT;

            retClientSetHeaderStatus = HttpClient::SetHeaderHttpClient(
                HttpClient::httpClientHandle, HTTPClient_HFIELD_REQ_USER_AGENT, USER_AGENT,
                strlen(USER_AGENT) + 1U, HTTPClient_HFIELD_PERSISTENT);
            retClientSetHeaderStatus = HttpClient::SetHeaderHttpClient(
                HttpClient::httpClientHandle, HTTPClient_HFIELD_REQ_CONNECTION,
                CONNECTION_KEEP_ALIVE, strlen(CONNECTION_KEEP_ALIVE) + 1U,
                HTTPClient_HFIELD_PERSISTENT);
            retClientSetHeaderStatus = HttpClient::SetHeaderHttpClient(
                HttpClient::httpClientHandle, HTTPClient_HFIELD_REQ_CONTENT_TYPE, CONTENT_TYPE,
                strlen(CONTENT_TYPE) + 1U, HTTPClient_HFIELD_PERSISTENT);

            

            static_cast<void>(memset(hostIpAddr, '\0', sizeof(hostIpAddr)));
            static_cast<void>(strcpy(hostIpAddr, HOSTNAME));
    
            retClientConnectStatus = HttpClient::ConnectHttpClient(HttpClient::httpClientHandle,
                                                                   (hostIpAddr), &esParams, 0);
            static_cast<void>(memset(hostIpAddr, '\0', sizeof(hostIpAddr)));

        }

        if(retClientConnectStatus == 0)
        {
            switch(msgType)
            {
                case wifiDataType::Msg_1:
                    std::copy_n(REQUEST_URI_Msg_1, strlenRequestURIMsg1, requestURI.begin());

                    break;
                case wifiDataType::Msg_2:
                    std::copy_n(REQUEST_URI_Msg_2, strlenRequestURIMsg2, requestURI.begin());
                    break;
                case wifiDataType::Msg_3
                    std::copy_n(REQUEST_URI_Msg_3, strlenRequestURIMsg3, requestURI.begin());
                    break;
                default:
                    break;
            }
            std::copy_n(devName, FindMessageSize(devName),
                        requestURI.begin() + FindMessageSize(&requestURI[0]));

            retClientSendStatus = HttpClient::SendHttpRequest(
                HttpClient::httpClientHandle, HTTP_METHOD_POST, &requestURI[0],
                reinterpret_cast<const char*>(msgToSend), (strlen(msgToSend) + 1U), 0);

        
        }
        

        if((retClientSendStatus < 0) &&
           ((retClientSendStatus != SL_RET_CODE_INVALID_INPUT) && (retClientSendStatus != SLNETERR_ESEC_HAND_SHAKE_TIMED_OUT)) && (retClientSendStatus != HTTPClient_ERESPONSEINVALID) )
        {
            
            retClientDestroyStatus = HttpClient::DestroyHttpClient(HttpClient::httpClientHandle);
        
            static_cast<void>(memset(hostIpAddr, '\0', sizeof(hostIpAddr)));
            retClientCreateStatus = -1;
            retClientConnectStatus = -1;

        }

}

每10秒调用一次 httpTask。

我的问题是:

1.伪代码是否正确?

2. HttpClient_sendRequest 或 HttpClient_connect 中的哪些错误会导致 HttpClient_destroy 和随后 的 HttpClient_create?

我在 SDK 中看到了 HTTPSGET 示例、在该示例中、每次发送操作后客户端都会被破坏。 我不想这样做、除非出现错误、因为我不希望每次发送 HTTP 握手的延迟。

请提供建议。

谢谢、

序列号

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    虽然每次  成功建立连接时我都会调用断开连接(即使 SendRequest 返回错误),但看起来正常。

    您可以尝试在不连接/断开连接的情况下发送几个请求,但是大多数 Web 服务器对连接使用相对较短的看门狗(空闲)超时时间,甚至10秒也可能足以关闭连接。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Kobi、

    感谢您的回复。 我有以下问题:

    1.如果服务器未运行、则在调用 HttpClient_connect 时、我会按预期获得 SL_ERROR_BSD_ECONNREFUSED。 就像在伪代码中一样、我会销毁客户端并在每次发生这种情况时重新创建它。 一段时间后、我开始为 HttpClient_connect 函数获取 sl_RET_CODE_malloc_error。 频繁创建和破坏客户端会导致内存耗尽、并可能导致内存碎片问题。

    进一步分析后发现使用 malloc 函数的 HttpClient_create 和 HttpClient_connect 函数。 使用静态存储器管理(即在 user.h 中注释 sl_memory_Mgmt_dynamic)会如何导致 malloc 错误?

    此外、使用 sl_Start 和 sl_Stop 重新启动 NWP 不会帮助解决 sl_RET_CODE_malloc_error。 解决该错误的唯一方法是执行电源复位。

    2.在内部,只要 HttpClient_connect 失败,就会调用 HttpClient_disconnect。 那么、我们为什么需要显式执行它呢?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    "user.h" 用于 simplelink 驱动程序。 不建议使用静态存储器(当  从 SL 事件处理程序调用 SL 命令时、它将特别引起问题)。

    SDK 中的网络库(如 httpclient)使用动态内存。

    我们不知道 内存泄漏、它可能与应用程序的使用有关。

    当然、您可以将 httpclient 替换为任何其他使用静态存储器的 http 实现(在顶层到 SL 套接字或 SlNetSock 上)。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我可以确认我们的应用在运行期间不使用任何动态存储器。 FreeRTOS 的所有动态内存分配仅在初始化期间完成。 运行时唯一的动态内存分配由 HttpClient 函数完成。

    我们注释了 httpclient connect、transfer 调用,即为 simplelink API 保持所有其他应用程序代码不变而进行的调用,以固定的间隔打印堆统计数据,并确认堆始终是恒定的,即在运行时没有 alloc 和 dealloc。  

     

    问题说明:

    1. 方案1:我们最初启动应用程序而不启动服务器。 因此、HttpClient_Connect 返回 sl_error_bsd_ECONNREFUSED 的错误。 由于连接失败、我们会破坏客户端、然后重试创建客户端、并在10秒后再次建立连接。 HttpClient_Connect 提供相同的 sl_error_bSD_ECONNREFUSED 错误。 但10小时后、HttpClient_Connect 返回 sl_RET_CODE_malloc_error。 内存的频繁分配和取消分配会导致内存不足。 我们可以看到、在连接和断开 HttpClient 的每个周期、它都会泄露24字节的内存。
    1. 方案2:在不启动服务器的情况下启动应用程序。HttpClient_Connect 返回 sl_error_BSD_ECONNREFUSED 的错误。 在不破坏客户端的情况下、重试 HttpClient_Connect。 即使不破坏 HttpClient、调用 HttpClient_Connect 的每个周期都会发生内存泄漏。
    1. 方案3:一段时间后运行服务器。 现在 HttpClient_Connect 成功并开始发送数据。 我们可以看到、即使这样、我们也会不断内存不足。

    无论成功连接还是断开连接、它都会不断泄漏内存。

    每种情况下可用的堆内存(以字节为单位):

    PS:plz 在原始问题中记下更新的代码

    为了进一步分析、我们在每次 http 客户端调用前后添加了堆统计信息、我们发现是

    创建客户端会消耗744字节的堆。

    连接客户端消耗24字节的堆、在这种情况下、客户端无法成功连接。

    Destroy 客户端返回744、

    连接调用消耗的24个字节会累计累加每个调用,直到堆内存耗尽....

    我们将着色器调用移到顶部并为此测试设置了一次、这是与上述共享代码的唯一区别。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    谢谢。 我们将对此进行检查。

    您是否使用安全连接(HTTPS)?  

    我简单地尝试在 HttpGet 中的序列上创建一个循环、并且在第一次迭代时仅显示泄漏迹象(看起来在连接中分配了16个字节、但不会释放)、但这不会在下一次迭代中发生 (一切都被释放)。

    我没有从服务器检查错误。

    我们需要几天的时间来更深入地调试这个内容(基本上你有 http 库的源、所以你可以从之前开始-它不是太复杂)。

    当我有更多信息或修复时、我会报告。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    是的、我们使用的是 HTTPS、我们还将尝试进一步调试。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我们在 http_client connect 上将断点放置在 FreeRTOS 的 malloc 和 free 函数中、这就是我们找到的结果。

    HttpClient_connect->createSecAttribs->SlNetSock_secAttribCreat->4字节分配。
    HttpClient_connect->createSecAttribs->SlNetSock_secAttribSet->16字节分配。
    HttpClient_connect->HttpClient_connect2->HttpClient_setHeader->16字节。
    HttpClient_connect->HttpClient_connect2->HttpClient_setHeader->12字节。
    HttpClient_connect->HttpClient_connect2->slNetsock_connect->SlNetSock_create->SlNetSock_AllocVirtualSocket ->12字节。

    尝试连接、连接失败。


    HttpClient_connect->HttpClient_connect2->HttpClient_disconnect->SlNetSock_secAttribDelete -> 4字节
    HttpClient_connect->HttpClient_connect2->HttpClient_disconnect->SlNetSock_secAttribDelete -> 16字节
    HttpClient_connect->HttpClient_connect2->HttpClient_disconnect->clearReqHeaders -> 24字节
    HttpClient_connect->HttpClient_connect2->HttpClient_disconnect->clearReqHeaders -> 24字节

    连接失败返回错误代码为-111。

    这表明 slnetsock_AllocVirtualSocket 的分配未被释放。 希望这将对您有所帮助。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    谢谢。 我将检查这个问题、并在几天内返回给您。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    我在 httpclient 中找不到问题、但由于我仅使用标准云服务器、我无法关闭它们。

    您是否注意到服务器启动并运行时出现内存泄漏问题?

    如果您将此文件添加到项目中、它将添加相关的 malloc/free 信息。

    它使用 Display_printf。    如果使用另一种打印方法,只要打印 不是自行分配内存,就可以替换它。   

    e2e.ti.com/.../memory.c

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我们看到 simplelink 的 http 和少数其他 API 直接调用 pvPortMalloc 和 vPortFree、而不是 malloc /sl_malloc 和 free/sl_free、不确定如何检测 malloc 和 free 将有助于打印额外的信息?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    在 http 库中的何处可以看到对 pvPortMalloc 的直接调用?  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    从我们的团队了解到、我们的工程师用 pvportmalloc 和 vportfree 替换了所有调用、而不是提供 malloc 和 free 的包装、而是用实际的文件替换修改后的文件、然后更新结果。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您是否对 HttpClient 库进行了更多更新?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    另一个更新完成的操作是使用直接 FreeRTOS 调用替换所有 SEM_INIT 和其他信标调用、现在我们将移植代码库、以便除了所需的移植文件外不会接触任何 TI 文件。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    好的。 我将等待更新。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    最可能的问题是我们一方的问题、在用特定目标替换 malloc 和 free 调用时、我们的工程师可能错过了一个。更多的免费调用、我们在连接失败的情况下运行、并确认没有内存泄漏。 我们面临不同的问题、在尝试连接时、器件始终返回-111。 我们正在尝试对其进行故障排除。