eclipse-threadx / netxduo

Eclipse ThreadX - NetXDuo is an advanced, industrial-grade TCP/IP network stack designed specifically for deeply embedded real-time and IoT applications
https://github.com/eclipse-threadx/rtos-docs/blob/main/rtos-docs/netx-duo/index.md
MIT License
230 stars 131 forks source link

web http post #97

Closed chengshuihang closed 2 years ago

chengshuihang commented 2 years ago

您好,请问有关于web http client post的例程吗?我在使用nx_web_http_client_put_packet出现了一些问题,想对照确认下。问题是nx_web_http_client_put_packet在某些时候会返回NX_NOT_CLOSED。当前测试代码如下: image

wenhui-xie commented 2 years ago

您好,您截图的这段代码使用方法是正确的。以下问题麻烦您再确认一下以便分析:

  1. 确认一下返回的status的十六进制数值是多少?
  2. 如果方便debug的话,能否在nx_web_http_client_put_packet设个断点,然后跟踪进去看一下具体是哪一行出错?
  3. 能否分享一下更完整的代码,以便排除相关问题?
chengshuihang commented 2 years ago

抱歉,我写错了,是nx_web_http_client_response_body_get返回的NX_NOT_CONNECTED(0x38),原因应该是数据包没有发送出去,因此nx_web_http_client_response_body_get函数无法获取到响应,奇怪的是nx_web_http_client_post_start_extended和nx_web_http_client_put_packet返回的都是NX_SUCCESS。 image image 上面给了两份截图,一份是在status为NX_NOT_CONNECTED时,count次数为3的截图,另外一份是采集post请求数据的截图。 可以看到,post请求了3次,但是采集到的post请求只有2次。当然截图无法证明NX_NOT_CONNECTED是由nx_web_http_client_response_body_get返回的,这个需要再次复现这个现象(它并不好复现,我会继续测试的)。 那么当前就是两个问题了,1 为什么会发送失败 2 post请求头数据会不一样。 image image image 这是测试代码

wenhui-xie commented 2 years ago

您好,截图中确实无法定位error status是哪个函数返回的。您在测试的时候可以加一些调试信息,如在返回error status的时候输出: http_client.nx_web_http_client_state web_client_pool.nx_packet_pool_available 等信息。

另外您能否将数据包文件分享给我,我可以看一下TCP的连接情况,以及HTTP包里具体的数据。 谢谢。

chengshuihang commented 2 years ago

是的截图无法定位,我给一份可以定位的截图 59f08e7d9d42cdec1dfeff1b89362fb 我发现这个问题只会出现在单步调试时,我在全速运行时,并没有发现这个故障。在单步调试时,故障在偶数次会稳定出现,全速运行,并未出现。我将本次单步调试的数据包也发送给您。 web_http_post_err.zip

wenhui-xie commented 2 years ago

您好,数据包里可以看出是server端主动断开了连接,您可以确认一下server端配置的超时时间吗? image

chengshuihang commented 2 years ago

image 默认的30s,server断开链接的话,nx_web_http_client_post_start_extended和nx_web_http_client_put_packet返回NX_SUCCESS是正常的吗?

wenhui-xie commented 2 years ago

从server回复的response里可以看到keep-alive timeout设置为5s,数据包里server也是5s后发了fin包断开了连接,符合预期。 image image 如果server已经断开连接之后,post_start和put_packet应该是会失败的,数据包里也确实没有抓到第二次post request。您可以先尝试把server端的keep-alive timeout时间调长一些再测试一下。另外再确认一下您的工程有没有开优化。谢谢。

chengshuihang commented 2 years ago

您好,您说的应该是对的,应该就是keep-alive导致的数据无法发出,比较难的是我的测试软件中没有keep-alive参数配置功能。另外我的工程没有开优化。post_start和put_packet确实是返回的NX_SUCCESS,这个在调试截图中就能确认。 image

wenhui-xie commented 2 years ago

您好,根据现在函数返回成功却没有抓到数据包的情况,可能是post_start和put_packet虽然返回的NX_SUCCESS,但是底层并没有发送成功。您可以在程序运行到第二次循环时,在_nx_ip_packet_send()函数里加一个断点,单步调试观察一下后续有没有出错。另外您的代码里有一处需要调整一下,在status为NX_SUCCESS的时候不需要release http_post_packet. image

chengshuihang commented 2 years ago

好的,我会进行尝试。 在status为NX_SUCCESS的时候不需要release http_post_packet吗? image 这是put_packet的代码,只有在非NX_SUCCESS put_packet才能释放packet,若status为NX_SUCCESS,不进行release的话,不就内存泄漏了吗?

chengshuihang commented 2 years ago

您好,根据您的方案,我在下面截图打了断点,获取到了USB端的返回值,其返回了0。这么看来问题出在我的USB设备上,我会接下来排查我的USB设备,非常感谢您的帮助! 另外关于release packet,你能确认下吗? image

wenhui-xie commented 2 years ago

您好,status为NX_SUCCESS时,将由netx内部来release这个packet。status为error时,由用户来release。您圈出来的部分我们内部代码有点问题,不应该在5942行调用nx_packet_release,我们会在下一个版本修复这个问题。谢谢。 image

chengshuihang commented 2 years ago

`UINT _nx_web_http_client_put_packet(NX_WEB_HTTP_CLIENT client_ptr, NX_PACKET packet_ptr, ULONG wait_option) {

NX_PACKET response_packet_ptr; CHAR buffer_ptr; UINT length; UINT status;

/* First, check and see if the client instance is still in the PUT state.  */
if (client_ptr -> nx_web_http_client_state != NX_WEB_HTTP_CLIENT_STATE_PUT &&
    client_ptr -> nx_web_http_client_state != NX_WEB_HTTP_CLIENT_STATE_POST)
{

    /* Client not ready, return error.  */
    return(NX_WEB_HTTP_NOT_READY);
}

/* Next, check and see if there is a response from the Server.  */
status =  _nx_web_http_client_receive(client_ptr, &response_packet_ptr, NX_NO_WAIT);

/* Check for an early response from the Server.  */
if (status == NX_SUCCESS)
{

    /* This is an error condition since the Server should not respond until the PUT is complete.  */

    /* Setup pointer to server response.  */
    buffer_ptr =  (CHAR *) response_packet_ptr -> nx_packet_prepend_ptr;

    /* Determine if it is an authentication error.  */
    if (((buffer_ptr + 11) < (CHAR *)response_packet_ptr -> nx_packet_append_ptr) &&
        (buffer_ptr[9] == '4') && (buffer_ptr[10] == '0') && (buffer_ptr[11] == '1'))
    {

        /* Inform caller of an authentication error.  */
        status =  NX_WEB_HTTP_AUTHENTICATION_ERROR;
    }
    else
    {

        /* Inform caller of general Server failure.  */
        status =  NX_WEB_HTTP_INCOMPLETE_PUT_ERROR;
    }

    /* Release the packet.  */
    nx_packet_release(response_packet_ptr);

    /* Disconnect and unbind the socket.  */
    _nx_web_http_client_error_exit(client_ptr, wait_option);

    /* Return to the READY state.  */
    client_ptr -> nx_web_http_client_state =  NX_WEB_HTTP_CLIENT_STATE_READY;

    /* Return error to caller.  */
    return(status);
}

/* Otherwise, determine if the packet length fits in the available bytes to send.  */
if (packet_ptr -> nx_packet_length > 
        (client_ptr -> nx_web_http_client_total_transfer_bytes - client_ptr -> nx_web_http_client_actual_bytes_transferred))
{

    /* Request doesn't fit into the remaining transfer window.  */
    return(NX_WEB_HTTP_BAD_PACKET_LENGTH);
}

/* Remember the packet length.  */
length =  packet_ptr -> nx_packet_length;

/* Now send the packet out.  */
status =  _nx_web_http_client_send(client_ptr, packet_ptr, wait_option);

/* Determine if the send was successful.  */
if (status != NX_SUCCESS)
{

    /* No, send was not successful.  */

    /* Release the packet.  */
    nx_packet_release(packet_ptr);

    /* Disconnect and unbind the socket.  */
    _nx_web_http_client_error_exit(client_ptr, wait_option);

    /* Return to the READY state.  */
    client_ptr -> nx_web_http_client_state =  NX_WEB_HTTP_CLIENT_STATE_READY;

    /* Return an error.  */
    return(status);
}

/* Otherwise, update the actual bytes transferred.  */
client_ptr ->  nx_web_http_client_actual_bytes_transferred =  client_ptr ->  nx_web_http_client_actual_bytes_transferred + length;

/* Return status to caller.  */
return(NX_SUCCESS);

}` 那是不是意味着,我只需要在status为 NX_WEB_HTTP_NOT_READY、NX_WEB_HTTP_AUTHENTICATION_ERROR、NX_WEB_HTTP_INCOMPLETE_PUT_ERROR、NX_WEB_HTTP_BAD_PACKET_LENGTH的时候,进行release就行?

wenhui-xie commented 2 years ago

是的,针对当前的版本,您可以在这些error_status时进行release。

chengshuihang commented 2 years ago

好的,非常感谢!

chengshuihang commented 2 years ago

您好,关于nx_web_http_client_request_packet_allocate成功后数据包release问题。在http内部有些错误会release,有些不会release,在nx_web_http_client_request_packet_allocate函数的调用方,依据nx_packet_data_append和nx_web_http_client_put_packet的返回值来判断是否需要release,比较麻烦。有没有一种办法,可以检测到allocate的数据当前可以release?

bo-ms commented 2 years ago

Hi @chengshuihang 谢谢你的反馈,是的,我们也注意到了这个问题,会在下一个release里面修复这个问题。现在能否把5948行的packet release移掉试试?

    /* Now send the packet out.  */
    status =  _nx_web_http_client_send(client_ptr, packet_ptr, wait_option);

    /* Determine if the send was successful.  */
    if (status != NX_SUCCESS)
    {

        /* No, send was not successful.  */

        /* Release the packet.  */
        //nx_packet_release(packet_ptr);

        /* Disconnect and unbind the socket.  */
        _nx_web_http_client_error_exit(client_ptr, wait_option);

        /* Return to the READY state.  */
        client_ptr -> nx_web_http_client_state =  NX_WEB_HTTP_CLIENT_STATE_READY;

        /* Return an error.  */
        return(status);
    }
chengshuihang commented 2 years ago

好的!

chengshuihang commented 2 years ago

您好!关于web http post内存池的问题,我需要传输0-15K大小的数据,一次post请求,我的http内存池该如何分配?下面代码是我现在使用的方案,它有一些问题,在传输12K的数据时,尾部有一些数据并没有发出来。当我将CLIENT_POOL_SIZE增大到(CLIENT_PACKET_SIZE * 12),它是可以发送出来的,但是这样我的内存消耗太大了。我没有找到发生这个现象的原因,您那边知道吗? `

define CLIENT_PACKET_SIZE (1024 * 4)

define CLIENT_POOL_SIZE (CLIENT_PACKET_SIZE * 9)

define PACKET_PAYLOAD_SIZE (1536)

define NX_PACKET_POOL_SIZE ((1536 + sizeof(NX_PACKET)) * 30)

static uint8_t nx_client_pool[CLIENT_POOL_SIZE]; static NX_PACKET_POOL web_client_pool; static uint8_t nx_packet_pool[NX_PACKET_POOL_SIZE]; static NX_PACKET_POOL nx_pool; status = nx_packet_pool_create(&nx_pool, "Nx Pool", PACKET_PAYLOAD_SIZE, nx_packet_pool, NX_PACKET_POOL_SIZE); status = nx_packet_pool_create(&web_client_pool, "HTTP Client Packet Pool", CLIENT_PACKET_SIZE, nx_client_pool, CLIENT_POOL_SIZE); static UINT RealTimeUploadDataUpload(NXD_ADDRESS server_ip_addr, UINT server_port) { TboxRealTimePackage_t package; UINT status = NX_SUCCESS; NX_PACKET http_get_packet = NULL; NX_PACKET http_post_packet = NULL; uint8_t* buffer = NULL; uint32_t size = 0u; uint32_t item;

/* 取出一帧待发送的数据 */
status = tx_queue_receive(&real_time_placing_order_send_queue, (void*)&item, TX_NO_WAIT);
if(status == TX_SUCCESS){
    /* 转换为数据包格式 */
    package = (TboxRealTimePackage_t*)item;
    /* 有无网? */
    if(mqtt_connect_flag == false){
        /* 无网,直接通知发送失败 */
        package->res(package, false);
    }
    else{
        /* 获取数据包 */
        buffer = package->buffer;
        /* 获取数据包大小 */
        size = package->size;
        /* 启动http post请求 */
        status = nx_web_http_client_realtime_data_upload_post_start_extended(&http_client,
                server_ip_addr, server_port,
                HTTP_URL_REAL_TIME_DATA,
                strlen(HTTP_URL_REAL_TIME_DATA),
                size, NX_WAIT_FOREVER);
        if(status == NX_SUCCESS){
            /* 申请http数据包 */
            status = nx_web_http_client_request_packet_allocate(&http_client, &http_post_packet, NX_WAIT_FOREVER);
            if(status == NX_SUCCESS){
                /* 将当前数据包,添加到http数据包中 */
                status = nx_packet_data_append(http_post_packet, buffer, size, &web_client_pool, NX_WAIT_FOREVER);
                if(status == NX_SUCCESS){
                    /* 将http数据包发送出去 */
                    status = nx_web_http_client_put_packet(&http_client, http_post_packet, NX_WAIT_FOREVER);
                    if(status == NX_SUCCESS){
                        /* 获取服务器响应 */
                        status = nx_web_http_client_response_body_get(&http_client, &http_get_packet, NX_WAIT_FOREVER);
                        /* 成功从服务器获取到响应数据包? */
                        if(http_get_packet != NULL){
                            /* 释放服务器响应数据包 */
                            nx_packet_release(http_get_packet);
                            /* 清零,防止野指针 */
                            http_get_packet = NULL;
                            /* 通知发送成功 */
                            package->res(package, true);
                            /* 返回结果状态 */
                            return status;
                        }
                    }
                    else{
                        /* 释放这个http发送数据包 */
                        nx_packet_release(http_post_packet);
                        /* 清空,防止野指针 */
                        http_post_packet = NULL;
                    }
                }
                else{
                    /* 释放这个http发送数据包 */
                    nx_packet_release(http_post_packet);
                    /* 清空,防止野指针 */
                    http_post_packet = NULL;
                }
            }
        }
        /* 通知发送失败 */
        package->res(package, false);
    }
}

return status;

} ` 这是将CLIENT_POOL_SIZE增大到(CLIENT_PACKET_SIZE 12)后的过程 image 这是CLIENT_POOL_SIZE为(CLIENT_PACKET_SIZE 9)的过程 image 对比发现,最后有几包数据包,并没有发出来。 post_一次成功一次失败.zip

bo-ms commented 2 years ago

你可以尝试将nx_pool传给web http client, 这样你只需要一个packet pool.

chengshuihang commented 2 years ago

非常感谢!按照您说的方案,我现在传输20K数据都是正常的。

bo-ms commented 2 years ago

好的,那我先把这个issue关了,有问题可以随时提问。

chengshuihang commented 2 years ago

好的