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.

[参考译文] CC3220SF:客户端程序定期重新连接到基于 CC3220SF 的服务器

Guru**** 2528390 points
Other Parts Discussed in Thread: CC3220SF

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

https://e2e.ti.com/support/wireless-connectivity/wi-fi-group/wifi/f/wi-fi-forum/998352/cc3220sf-client-programs-are-periodically-reconnecting-to-cc3220sf-based-server

器件型号:CC3220SF

您好!

我根据网络终端和云 OTA 示例为 CC3220SF LAUNCHXL 编写了一个程序。

该方案的工作方式如下:
1.计算机上运行的客户端程序通过 WiFi 连接到 LaunchPad 服务器套接字并向设备发送命令
2. Launchpad 解码命令并通过 UART 将命令传输到器件
该器件通过 UART 对命令做出响应
4. Launchpad 通过 WiFi 加密数据并将其传输到客户端程序

当只有一个 LaunchPad 连接时、该程序工作稳定、至少一天内没有重新连接。
当有多个连接时、客户端程序会定期重新连接到 LaunchPad。
连接客户端程序的新副本时,重新连接会变得更加频繁。
一段时间后、某种稳定和重新连接变得不太频繁。
连接客户端程序的另一个副本时、情况会重复出现。
如果我删除一行 usleep(1000);在 for (i=0;i<16; i++)之后,重新连接的数量会急剧增加。
如果在1秒内服务器没有响应、客户端程序将重新连接。

当 有多个连接时,为什么客户端程序正在重新连接?

Code Composer Studio 10.2.0、SimpleLink CC32xx SDK 4.30.00.06

以下是我有关 WiFi 数据交换的源代码:

int32_t TCPServerInit(sockAddr_t *sAddr, uint16_t port)
{
    int32_t sock;
    int32_t status;
    int32_t nonBlocking = TRUE;
    /* Contains the local ip address and port */
    SlSockAddr_t    *sa;
    int32_t addrSize;


    /* filling the TCP server socket address */
    sAddr->in4.sin_family = SL_AF_INET;

    /* Set the server's port:
       We'll receive connection requests on this port */
    sAddr->in4.sin_port = sl_Htons(port);
    sAddr->in4.sin_addr.s_addr = SL_INADDR_ANY;

    sa = (SlSockAddr_t*)sAddr;
    addrSize = sizeof(SlSockAddrIn_t);

    /*
     *  Open a TCP socket:
     *  Since TCP is a connection oriented channel,
     *  the opened socket would serve as 'welcome' socket,
     *  on which we'll receive connection requests from clients.
     */
    sock = sl_Socket(sa->sa_family, SL_SOCK_STREAM, TCP_PROTOCOL_FLAGS);
    ASSERT_ON_ERROR(sock, SL_SOCKET_ERROR);

    /* Bind socket to server's port */
    status = sl_Bind(sock, sa, addrSize);
    if(status < 0)
    {
        UART_PRINT("[line:%d, error:%d] %s\n\r", __LINE__, status,
                   SL_SOCKET_ERROR);
        sl_Close(sock);
        return(-1);
    }

   /* 'Listen' signify that wer'e ready to receive connection's from clients */
    status = sl_Listen(sock, 0);
    if(status < 0)
    {
        UART_PRINT("[line:%d, error:%d] %s\n\r", __LINE__, status,
                   SL_SOCKET_ERROR);
        sl_Close(sock);
        return(-1);
    }

    /* Set socket as non-blocking socket (if needed):
     * Non-blocking sockets allows user to handle other tasks rather than block
     * on socket API calls.
     * If an API call using the Non-blocking socket descriptor
     * returns 'SL_ERROR_BSD_EAGAIN' -
     * this indicate that the user should try the API again later.
     */
    nonBlocking = TRUE;
    status =
        sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &nonBlocking,
                      sizeof(nonBlocking));
    if(status < 0)
    {
        UART_PRINT("[line:%d, error:%d] %s\n\r", __LINE__, status,
                   SL_SOCKET_ERROR);
        return(-1);
    }

    return sock;
}

typedef union
{
    uint32_t ipv4;          /* Ipv4 Address */
    uint8_t ipv6[16];       /* Ipv6 Address */
}ip_t;


SlSockAddr_t *IP2SlSockAddr(uint8_t sl_fa, uint16_t portNumber, ip_t ipAddress, sockAddr_t *sAddr)
{
    // filling the TCP server socket address
    sAddr->in4.sin_family = SL_AF_INET;

    // Since this is the client's side,
    // we must know beforehand the IP address
    // and the port of the server wer'e trying to connect.
    sAddr->in4.sin_port = sl_Htons((unsigned short)portNumber);
    sAddr->in4.sin_addr.s_addr = sl_Htonl((unsigned int)ipAddress.ipv4);

    return (SlSockAddr_t*)sAddr;
}

int32_t TCPClientInit(uint8_t sa_family)
{
    int32_t sock;
    int32_t status;
    int32_t nonBlocking;

    // Get socket descriptor - this would be the
    // socket descriptor for the TCP session.
    sock = sl_Socket(sa_family, SL_SOCK_STREAM, TCP_PROTOCOL_FLAGS);
    ASSERT_ON_ERROR(sock, SL_SOCKET_ERROR);

    // Set socket as non-blocking socket (if needed):
    // Non-blocking sockets allows user to handle
    // other tasks rather than block
    // on socket API calls.
    // If an API call using the Non-blocking socket descriptor
    // returns 'SL_ERROR_BSD_EAGAIN' -
    // this indicate that the user should try the API again later.
    nonBlocking = TRUE;
    status = sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &nonBlocking, sizeof(nonBlocking));

    if(status < 0)
    {
        UART_PRINT("[line:%d, error:%d] %s\n\r", __LINE__, status,
                   SL_SOCKET_ERROR);
        sl_Close(sock);
        return(-1);
    }

    return sock;
}

void *appThread(void *arg0)
{
    uint8_t run_first = 1;

    int32_t server_sock;

    /* Contains the ip address and port of the connected peer. */
    static SlSockAddr_t    *csa;
    static sockAddr_t sAddr;
    static int32_t addrSize;
    int32_t ret;
    uint32_t ans;
    int32_t newsock = -1;

    static uint8_t server_states[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t *psrvstate;
    uint32_t uart_send_size;
    uint32_t *pusendsz;

#define server_state *psrvstate

    static uint32_t socket_sizes[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    uint32_t *psocksz;

#define socket_size *psocksz

    static uint32_t socket_flags[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    uint32_t *psockfl;

#define socket_flag *psockfl

    int32_t status;
    static int32_t client_socks[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    int32_t *pclsock;
    static uint8_t client_states[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t *pclstate;

#define client_sock *pclsock
#define client_state *pclstate

    uint8_t queue_state;
    uint16_t queue_size;
    uint8_t *queue_bufptr;
    static uint8_t queue_items[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

#define queue_item queue_items[i]

...

    while(1)
    {
        usleep(1000);

        if(run_first)
        {
            server_sock = TCPServerInit(&sAddr, 2612);

            csa = (SlSockAddr_t*)&sAddr;
            addrSize = sizeof(SlSockAddrIn_t);

            if(server_sock >= 0)
                run_first = 0;
        }
        else
        {
            newsock = sl_Accept(server_sock, csa, (SlSocklen_t*)&addrSize);

            if(newsock == SL_ERROR_BSD_EAGAIN)
            {
                //break;
            }
            else if(newsock < 0)
            {
                server_states[newsock] = 0;
            }
            else
            {
                UART_PRINT("Connected to client %d: ", newsock);

                sAddr.in4.sin_addr.s_addr = sl_Htonl(sAddr.in4.sin_addr.s_addr);
                PrintIPAddress(FALSE,(void*)&sAddr.in4.sin_addr);

                UART_PRINT(lineBreak);

                if(newsock < 16)
                {
                    server_states[newsock] = 1;
                }
            }

uint8_t i = 0;
            for(i = 0; i < 16; i++)
            {
usleep(1000);

                socketBuf = sockbuf[i];
                psrvstate = &server_states[i];
                psocksz = &socket_sizes[i];
                psockfl = &socket_flags[i];

                switch(server_state)
                {
                case 0:
                    // Not connected
                    break;
                case 1:
                    ret = sl_Recv(i, socketBuf, MAX_BUF_SIZE, 0);

                    if(ret == SL_ERROR_BSD_EAGAIN)
                    {
                        break;
                    }
                    else if(ret < 0)
                    {
                        UART_PRINT("[line:%d, error:%d] %s\n\r", __LINE__, ret,
                                   BSD_SOCKET_ERROR);
                        UART_PRINT("Socket %d closed\n\r", i);

                        sl_Close(i);

                        server_state = 0;

                        break;
                    }
                    else if(ret == 0)
                    {
                        UART_PRINT("TCP Client (socket %d) closed the connection \n\r", i);

                        sl_Close(i);

                        server_state = 0;

                        break;
                    }
					
                    uart_send_size = ret;

                    queue_item = AddToQueue(socketBuf, uart_send_size, socketBuf, 0);

                    server_state = 2;

                    break;
                case 2:
                    queue_state = QueueState(queue_item);

                    if(queue_state >= ready)
                    {
                        queue_size = QueueSize(queue_item);
                        socket_size = queue_size;
                    }
                    else
                        break;
                case 3:
                    Encrypt((char*)socketBuf, (char*)socketBuf, socket_size);
					
                    ret = sl_Send(i, socketBuf, socket_size, 0);
                    if(ret == SL_ERROR_BSD_EAGAIN)
                    {
                        break;
                    }
                    else if(ret < 0)
                    {
                        UART_PRINT("[line:%d, error:%d] %s\n\r", __LINE__, ret,
                                   SL_SOCKET_ERROR);
                        UART_PRINT("Socket %d closed\n\r", i);

                        sl_Close(i);

                        server_state = 0;

                        break;
                    }

                    server_state = 1;

                    break;
                }
            }
        }
    }
}
 

此外、文件 cloud_ota.c 中函数[CheckLanConnection]的[1531]行有时会出现错误错误[-2]
第1531行对应于代码

    /* Ping the GW */
    retVal = sl_NetAppPing((SlNetAppPingCommand_t*)&pingParams, \
                           SL_AF_INET, (SlNetAppPingReport_t*)&pingReport, \
                           SimpleLinkPingReport);
    ASSERT_ON_ERROR(retVal);

尝试更新时、程序很少会在 cloud_ota.c 文件中的 loop_forever ()行上挂起

        if(NULL != pEntry->p_evtHndl)
        {
            if(pEntry->p_evtHndl() < 0)
            {
                UART_PRINT("Event handler failed..!! \r\n");
                LOOP_FOREVER();
            }
        }


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

    您好!

    每个客户正在发送/接收多少数据?

    是否可能有2个客户端的吞吐量接近无线带宽、从而导致断开连接? 或者 、您的应用程序可能正在占用一个连接的时间(例如、在缓冲区已满且对等设备最终断开连接之前、您很长时间无法从中读取)。  

    我们需要检查空气嗅探器日志或 NWP 日志(请参阅 www.ti.com/.../swru455中的第20.1章)。

    谁触发断开连接? LaunchPad 还是客户端?

    我不确定-2错误代码是什么、请在 sl_NetAppPing 内进行调试(simplelink 项目中提供源代码)。 NWP 日志也可能有助于理解这一点。  

      也需要调查 pentry->p_evtHnele()问题。 请检查该值(应为函数地址)。 在  这种情况下,还要检查"pCtx->currentState"和"event"的值。

    BR、

    Kobi

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

    每个客户端每隔5..10ms 发送和接收不到2 * 1000字节(Tx 和 Rx)(平均为100..200字节)的数据。 对于5个客户端-每秒小于400、000字节。 即使与两个客户端重新连接也会发生。
    这不会超过无线网络的带宽。 此客户端程序可通过基于 WIZnet w5500 (最多7个套接字)的 IP 模块与器件配合使用。
    显然、客户端发起重新连接(如果服务器3次尝试没有响应、客户端程序将重新连接、每次尝试1秒)。
    我在连接5个客户端时附加 NWP 日志。
    此问题目前是主要问题。 我稍后会研究其他问题。
    e2e.ti.com/.../7077.putty.log

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

    您提供的数字不会累加(根据时间间隔、每个客户端发送和接收400KB)、但无论如何、无法根据总带宽来计算此数字。  

    您能否提供监听器日志以更好地了解无线传输过程中发生的情况?

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

    我知道这些数字不会相加。
    我估计了最大通道负载、以此说明即使是最坏情况下的交换也不能超过 WiFi 通道的带宽。
    上层协议在请求响应原则下工作、与 Modbus 有点相似。
    第一条消息中给出的与程序的一个客户端(平均为100..200字节的请求和响应)进行此交换的周期约为60ms (对于 for (i = 0;i <16;i ++)循环内的注释行 usleep(1000)为10ms)
    在我看来、这基本上不能超过 WiFi 带宽。
    CC3220SF 硬件或软件库对交换是否有任何限制?
    您是否看到了我的 NWP 日志?
    这是否提供了任何其他信息来了解问题?
    我可以提供监听器日志、但我不知道如何获取。
    我应该使用哪款监听器?
    是否有关于如何获取 CC3220SF 监听器日志的指南、如 NWP 日志指南?

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

    我检查了 NWP 日志、但无法准确理解 问题(我只能看到接受请求、而不能看到未记录的实际数据传输)。

    802.11流量的监听是通过商业监听器完成的。 您可以检查免费的 Wireshark 并查找支持的适配器。

    您能否发送终端日志(包含更多日志以更好地了解情况、例如打印套接字编号/索引+状态+ sl_recv/sl_send 返回代码)?

    希望这不会改变太多的行为。 您可以删除 usleep。

    当使用多个套接字时、每个套接字的缓冲区被分割、因此可能会出现更多问题、但这里的确切问题并不简单。

    BR、

    Kobi  

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

    我获得了两个客户端程序的 UART 和 Wireshark 日志。 usleep 线路已移除-重新连接大约每秒一次。
    UART 日志中的最后两行是定期关闭客户端程序。
    我选择了具有时间戳的 UART 终端- BR@y++提供的终端 v1.9b。 但如果我将交换数据添加到日志中、它不能足够快地显示数据。
    因此、重新连接时、程序仅显示事件和错误代码。
    根据日志、当 sl_Recv 函数返回0时、始终会发生重新连接。

    UART 日志:

    9:17:40.816>已连接到客户端1:192.168.1.71
    9:17:41.376>已连接到客户端2:192.168.1.71
    9:17:41.626> TCP 客户端(套接字1)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:41.626>已连接到客户端3:192.168.1.71
    9:17:42.161> TCP 客户端(套接字2)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:42.161>已连接到客户端4:192.168.1.71
    9:17:44.876> TCP 客户端(套接字3)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:44.876>已连接到客户端5:192.168.1.71
    9:17:45.473> TCP 客户端(套接字4)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:45.473>已连接到客户端6:192.168.1.71
    9:17:48.189> TCP 客户端(套接字5)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:48.189>已连接到客户端7:192.168.1.71
    9:17:48.728> TCP 客户端(套接字6)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:48.728>已连接到客户端8:192.168.1.71
    9:17:51.251> TCP 客户端(套接字7)已关闭连接。 SL_Recv 返回0。 服务器状态1
    9:17:51.612> TCP 客户端(套接字8)已关闭连接。 SL_Recv 返回0。 服务器状态1

    Wireshark 日志(重命名为 Wireshark.pcapng)

    e2e.ti.com/.../wireshark.pcapng.log

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

    我在 TCP 级别没有看到任何应触发客户端终止连接的内容。 一切似乎都正常、客户端突然发送 FIN 数据包。

      应用 级别是否有任何逻辑可以解释这一点? (例如、一些错误/意外接收的数据会导致客户端关闭连接?)  

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

    客户端程序的开发人员发现重新连接的原因-来自服务器的数据不正确。
    我发现数据有什么问题:来自目标器件的数据包含 crc16、客户端程序会发现违反此数据包并重新连接。
    我通过使用 UART-USB 适配器物理连接到线路来监听来自目标器件的数据包、并将其与 CC3220SF 接收到的数据包进行比较、然后通过调试 UART CC3220SF LaunchPad 输出。
    在 UART CC3220SF 上接收数据时会发生数据包违规-接收到的数据小于目标器件发送的数据。
    下面显示了此类违规的示例:

    目标器件 UART Tx、114字节
    55 97 01 00 E2 07 01 00 01 01 01 02 03 27 9C DA A3
    74 20 4A B5 CC 6E 94 5B 9D F7 04 0B 77 01 00 01
    27 00 00 00 00 00 00 00 00 03 06 1E 15 05 04 1B
    00 01 06 1C 15 0A 25 01 00 03 06 1E 15 04 20
    16 00 00 03 06 1E 15 04 20 30 00 03 06 1E 15
    05 02 07 00 03 06 1E 15 05 04 1C 00 CE 04
    80 FE 00 00 00 00 00 00 00 00 EC FF FF FF EC 00
    6B F5

    CC3220SF UART Rx、111字节
    55 97 01 00 E2 07 01 00 01 01 01 02 03 27 9C DA A3
    74 20 4A B5 CC 6E 94 5B 9D F7 04 0B 77 01 00 01
    27 00 00 00 00 00 00 00 00 03 06 1E 15 05 04 1B
    00 01 06 1C 15 0A 25 01 00 03 06 1E 15 04 20
    16 00 1E 15 04 20 30 00 03 06 1E 15 05 02 07
    00 00 03 06 1E 15 05 04 1C 00 CE 04 80 FE 00
    00 00 00 00 00 00 00 00 EC FF FF EC 00 6B F5

    客户端程序接收、111字节
    55 97 01 00 E2 07 01 00 01 01 01 02 03 27 9C DA A3
    74 20 4A B5 CC 6E 94 5B 9D F7 04 0B 77 01 00 01
    27 00 00 00 00 00 00 00 00 03 06 1E 15 05 04 1B
    00 01 06 1C 15 0A 25 01 00 03 06 1E 15 04 20
    16 00 1E 15 04 20 30 00 03 06 1E 15 05 02 07
    00 00 03 06 1E 15 05 04 1C 00 CE 04 80 FE 00
    00 00 00 00 00 00 00 00 EC FF FF EC 00 6B F5


    在第二个客户端程序通过 TCP 连接后、通过 UART 接收到的数据包会立即发生类似的违反、即使该程序不交换数据也是如此。
    在 TI RTOS 上运行的应用程序似乎相互冲突、从而导致 UART 发生故障。
    以下是与 UART 和任务初始化以及 UART 发送/接收功能相关的代码:

    #define UART_READ_TASK_STACK_SIZE  2000
    #define UART_READ_TASK_PRIORITY    1
    
    UART_Handle UARThandle;
    
    Task_Struct uartReadTask;  // not static so you can see in ROV
    static uint8_t uartReadTaskStack[UART_READ_TASK_STACK_SIZE];
    
    UART_Handle InitUART(void)
    {
        UART_Params uartParams;
        UART_Handle uartHandle;
    
        UART_init();
        UART_Params_init(&uartParams);
    
        uartParams.writeMode      = UART_MODE_BLOCKING;
        uartParams.readMode       = UART_MODE_BLOCKING;
        uartParams.writeDataMode  = UART_DATA_BINARY;
        uartParams.readDataMode   = UART_DATA_BINARY;
        uartParams.readReturnMode = UART_RETURN_FULL;
        uartParams.readEcho       = UART_ECHO_OFF;
        uartParams.readTimeout    = 1000 / Clock_tickPeriod;
        uartParams.baudRate       = 921600;
    
        uartHandle = UART_open(CONFIG_UART_1, &uartParams);
    
        return(uartHandle);
    }
    
    void InitTaskUART(void)
    {
        Task_Params taskParams;
    
        Task_Params_init(&taskParams);
        taskParams.stack = uartReadTaskStack;
        taskParams.stackSize = UART_READ_TASK_STACK_SIZE;
        taskParams.priority = UART_READ_TASK_PRIORITY;
        Task_construct(&uartReadTask, uartReadTaskFxn, &taskParams, NULL);
    }
    
    void *appThread(void *arg0)
    {
    	...
    	UARThandle = InitUART();
    	InitTaskUART();
    	...
    	UART_write(UARThandle, Queue.Items[Queue.rptr].send_buf, min(Queue.Items[Queue.rptr].size, MAX_UART_DMA));
    	bytesRead = UART_read(UARThandle, pBuf, min(RX_BUF_SIZE - Queue.Items[Queue.rptr].size, MAX_UART_DMA));
    	...
    }

    appThread 的初始化:

    	...
    	pthread_attr_init(&pAttrs);
        priParam.sched_priority = 1;
        RetVal = pthread_attr_setschedparam(&pAttrs, &priParam);
        RetVal |= pthread_attr_setstacksize(&pAttrs, TASK_STACK_SIZE);
    
    	if(RetVal)
    		while(1);
    
        RetVal = pthread_create(&g_app_thread, &pAttrs, appThread, NULL);
    
    	if(RetVal)
    		while(1);
    	...

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

    cc3220sf 上正在运行哪些应用程序(您认为它们可能会发生冲突)?

    是否有不同的应用程序可访问 UART? 您是否在 UART 上使用流控制? 在数据发送到 UART 之前打印数据可能会帮助您调试问题。