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.

AM2634: Send data to two servers

Part Number: AM2634
Other Parts Discussed in Thread: SYSCONFIG,

Tool/software:

Dear Team,

I want to use two client and 1 server application, for that I m using AM263 two ethernet port and two IP address assign by two LWIP instance by sysconfig,

now I'm able to ping both the port now the question is how to send data by both the ports to the server(Any protocol can used TCP/UDP) 

Example used -  enet_cpsw_tcpclient_am263x-cc_r5fss0-0_freertos_ti-arm-clang

hardware - Am2634 Evaluation board

Enet - CPSW

  • Hi Vikas,

    1. I am assuming both your AM263x devices are clients and since you are able to ping both the different clients, your both NetIFs are up and running, 

    2. If you see the app_tcpclient.c file, you will find a connection being created between the server and client using netconn_connect. This netconn_connect takes in the host IP address and host port number. In your case, you would need two such netconn_connect API calls to create 2 connections between the same server and 2 clients.

    3. Then, you will also find an API netconn_write. This will send the data to the server.

    You can try using the above two APIs and that should do it for you. Let me know if you face any other issues.

    Regards,
    Shaunak

  • Agree

    but how to switch between client-1 to client-2 and vice-versa, this is the only problem

  • Hi Vikas,

    What does "switching" exactly mean? Like in scenario-1, you just want to send data from client-1 to server and in scenario-2, do you just want to send data from client-2 to server?

    I see you raised another E2E where you mention the use of two servers instead of 1.

    Regards,
    Shaunak

  • What does "switching" exactly mean? Like in scenario-1, you just want to send data from client-1 to server and in scenario-2, do you just want to send data from client-2 to server? 

    Yes, exactly same

    I see you raised another E2E where you mention the use of two servers instead of 1

    Yes, thats another requirement

  • Hi Vikas,

    Yes, thats another requirement

    Thanks for confirming,

    This can simply be handled as follows, I will write the rough algorithm here (this is not the complete code):

    const enum netconn_type connType = NETCONN_TCP;
    struct netconn *pConn1 = NULL;
    struct netconn *pConn2 = NULL;
    
    /* Pointers to the netconn connections */
    pConn1 = netconn_new(connType);
    pConn2 = netconn_new(connType);
    
    if (pConn1 != NULL)
    {
        /* Connect to the TCP Server */
        EnetAppUtils_print("<<<< ITERATION %d >>>>\r\n", (pktIdx + 1));
        EnetAppUtils_print(" Connecting to: %s:%d \r\n", gHostServerIp4, HOST_SERVER_PORT);
        connectError = netconn_connect(pConn1, &pHostInfo->ipAddr, pHostInfo->port);
        if (connectError != ERR_OK)
        {
            netconn_close(pConn1);
            DebugP_log("[1] Connection with the server isn't established\r\n");
            continue;
        }
        DebugP_log("Connection with the server is established\r\n");
    }
    
    if (pConn2 != NULL)
    {
        /* Connect to the TCP Server */
        EnetAppUtils_print("<<<< ITERATION %d >>>>\r\n", (pktIdx + 1));
        EnetAppUtils_print(" Connecting to: %s:%d \r\n", gHostServerIp4, HOST_SERVER_PORT);
        connectError = netconn_connect(pConn2, &pHostInfo->ipAddr, pHostInfo->port);
        if (connectError != ERR_OK)
        {
            netconn_close(pConn2);
            DebugP_log("Connection with the server isn't established\r\n");
            continue;
        }
        DebugP_log("[2] Connection with the server is established\r\n");
    }
    /* Now two netconn connections are open and are referenced by pConn1 and pConn2 */
    
    /* Send from Client-1 */
    
    memset(&snd_buf, 0, sizeof(snd_buf));
    buf_len = snprintf(snd_buf, sizeof(snd_buf), "Hello over TCP %d", 1);
    err = netconn_write(pConn1, snd_buf, buf_len, NETCONN_COPY);
    
    /* Send from Client-2  */
    buf_len = snprintf(snd_buf, sizeof(snd_buf), "Hello over TCP %d", 2);
    err = netconn_write(pConn2, snd_buf, buf_len, NETCONN_COPY);

    Just referencing the netconn connection in the write API will handle the switching part. Can you try the same and let me know if it works for you?

    This approach will work for the multi-client, single server, for multi-client multi-server, you will have to pass in the appropriate server's Ip address in netconn_connect API.

    Regards,
    Shaunak

  • Sorry, this solution isn't working,

    As per my understanding the problem is, 

    We create two netconn *pConn1 and netconn *pConn2, it should be link to different LWIP instances but here both are linked same lwip instance,

    You can also try on your side, Use SocketTest software as Server

    kindly suggest any other solution 

    Two Client Ip - 192.168.1.200 & 192.168.1.201

    Server always getting data from 192.168.1.201 only and 192.168.1.200 not able to send data but both IPs are able to ping by server

  • Shaunak,

    Can we connect on teams to solve this problem?

  • Hi Vikas,

    I tried running these experiments on my end. This seems to be a lwIP limitation. LwIP wasn't designed to work well with multiple netif's. The function ip4_route is used, which wrongly selects the first netif it can find, that is the reason you see data being taken from 192.168.1.200 always. lwIP seems to use a default NetIF for out-going connections.

    1. You can try creating two different tcpip_threads, in each thread bind the netconn to the IP address and port using netconn_bind. 

    2. Handle each client-server connection as a different thread because the netconn APIs are internally exclusively accessing the lwIP cores. Trying to open two different lwIP instances in the same example would not be easy and thus we do not have an out of box example for it

    Given my current bandwidth crunch and other meeting clashes, I won't be able to find time this week for a meet

    Regards,
    Shaunak

  • I think we are missing something because two IPs are assigned and both are pingable that mean LWIP working on both the IPs, but for transmission we need to select that particular LWIP instance, and it should be the part of struct netif *g_pNetif[ENET_SYSCFG_NETIF_COUNT]; 

    So requesting to discuss with other team members also.

    Because in the real world I see so many examples in which multiple client  can be created and they are capable to send data to different servers.

  • Dear Shaunak,

    kindly do need full to close my issue

  • Hi Vikas,

    but for transmission we need to select that particular LWIP instance, and it should be the part of struct netif *g_pNetif[ENET_SYSCFG_NETIF_COUNT]; 

    The Netif Structs do not point to any lwIP instance and neither does lwIP expect it to happen. The lwIP instance is a shared resource on that core. Rather you need to bind the lwIP netconn connection to the netif using netconn_bind_if function (As suggested in my previous reply).  Having 2 lwIP instances for 2 NetIFs in the same application would have a very bad hit on memory and is not recommended and neither do we have any example in the SDK demonstrating this.

    Because in the real world I see so many examples in which multiple client  can be created and they are capable to send data to different servers.

    Yes, agreed. This is not a board or CPSW limitation. Some of the netconn lwIP calls are blocking calls. This is a known scalability limitation with lwIP but since lwIP is not TI owned, we cannot modify the lwIP source code to handle multiple clients in the same netconn socket. But we try to implement it by spawning multiple threads to handle multiple clients seperately

    I tried running this experiment on my end.

    1. Create 2 threads for 2 clients. In each thread's function, use netconn_new to open a new netconn connection, then use netconn_bind_if to bind the connection to a netif then call the netconn_connect.

    2. Read the IP addresses of 2 servers and connect to the servers.

    3. Confirm using logs that both connections are established (confirm using Wireshark as well). Im attaching the logs below:

    As you can see, the 2 Client IPs are highlighted in red, the server IPs in green and both the successful connection logs in blue.

    Now lwIP netconn APIs acquire the lwip core exclusively, as you can see in the comment from lwIP's implementation below:

    I am able to establish a connection, but as you can see the yellow highlighted box in the logs above, the CCS logs from 2 different tcp_ip threads are improper because both the threads have same priority. 

    Im still looking into it, but so far I have been able to successfully connect 2 clients to two different servers using 2 different threads (as suggested previously as well)

    Regards,
    Shaunak

  • Great and appreciate your efforts,

    Can your share the code if possible, that will be more helpful.

  • Hi Vikas,

    Attaching the TCP client file

    /*
     * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     * 3. The name of the author may not be used to endorse or promote products
     *    derived from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
     * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
     * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     * OF SUCH DAMAGE.
     *
     * This file is part of the lwIP TCP/IP stack.
     *
     * Author: Adam Dunkels <adam@sics.se>
     *
     */
    
    /* ========================================================================== */
    /*                             Include Files                                  */
    /* ========================================================================== */
    #include <string.h>
    #include <stdio.h>
    #include <kernel/dpl/TaskP.h>
    #include <kernel/dpl/ClockP.h>
    #include "lwip/opt.h"
    #include "lwip/sys.h"
    #include "lwip/api.h"
    #include "enet_apputils.h"
    
    /* ========================================================================== */
    /*                           Macros & Typedefs                                */
    /* ========================================================================== */
    #define HOST_SERVER_IP6  ("FE80::12:34FF:FE56:78AB")
    
    #define HOST_SERVER_PORT  (8888)
    
    #define APP_MAX_RX_DATA_LEN (1024U)
    
    #define APP_NUM_ITERATIONS (2U)
    
    #define APP_SEND_DATA_NUM_ITERATIONS (5U)
    
    #define MAX_IPV4_STRING_LEN (20U)
    
    char snd_buf[APP_MAX_RX_DATA_LEN];
    
    /* ========================================================================== */
    /*                         Structure Declarations                             */
    /* ========================================================================== */
    
    struct App_hostInfo_t
    {
        ip_addr_t ipAddr1;
        ip_addr_t ipAddr2;
        uint16_t port;
    };
    
    /* ========================================================================== */
    /*                          Function Declarations                             */
    /* ========================================================================== */
    static void AppTcp_fillHostSocketInfo(struct App_hostInfo_t* pHostInfo);
    
    /* ========================================================================== */
    /*                            Global Variables                                */
    /* ========================================================================== */
    
    static struct App_hostInfo_t gHostInfo;
    static char   gHostServerIp4_1[MAX_IPV4_STRING_LEN] = "";
    static char   gHostServerIp4_2[MAX_IPV4_STRING_LEN] = "";
    
    /* ========================================================================== */
    /*                          Function Definitions                              */
    /* ========================================================================== */
    
    
    static void AppTcp_fillHostSocketInfo(struct App_hostInfo_t* pHostInfo)
    {
        EnetAppUtils_print("[0] IP eneterd is: %s \r\n", gHostServerIp4_1);
        EnetAppUtils_print("[1] IP eneterd is: %s \r\n", gHostServerIp4_2);
        int32_t addr_ok;
        pHostInfo->port = HOST_SERVER_PORT;
        memset(&pHostInfo->ipAddr1, 0, sizeof(pHostInfo->ipAddr1));
        memset(&pHostInfo->ipAddr2, 0, sizeof(pHostInfo->ipAddr2));
        ip_addr_t*  pAddr1 = &pHostInfo->ipAddr1;
        ip_addr_t*  pAddr2 = &pHostInfo->ipAddr2;
        IP_SET_TYPE_VAL(*pAddr, IPADDR_TYPE_V4);
        addr_ok = ip4addr_aton(gHostServerIp4_1, ip_2_ip4(pAddr1));
        addr_ok = ip4addr_aton(gHostServerIp4_2, ip_2_ip4(pAddr2));
        EnetAppUtils_assert(addr_ok);
    
        return;
    }
    
    static void AppTcp_simpleclient_1(void *pArg)
    {
        struct netconn *pConn1 = NULL;
        err_t err1 = ERR_OK, connectError1 = ERR_OK;
        struct App_hostInfo_t* pHostInfo = (struct App_hostInfo_t*) pArg;
        uint32_t buf_len = 0;
        const enum netconn_type connType = NETCONN_TCP;
    
        /* Create a new connection identifier. */
        for (uint32_t pktIdx = 0; pktIdx < APP_NUM_ITERATIONS; pktIdx++)
        {
            struct netbuf *rxBbuf = NULL;
            pConn1 = netconn_new(connType);
            if (pConn1 != NULL)
            {
                /* Connect to the TCP Server */
                EnetAppUtils_print("[1]<<<< ITERATION %d >>>>\r\n", (pktIdx + 1));
                EnetAppUtils_print("[1] Connecting to: %s:%d \r\n", gHostServerIp4_2, HOST_SERVER_PORT);
                connectError1 = netconn_bind_if(pConn1, 1);
                connectError1 = netconn_connect(pConn1, &pHostInfo->ipAddr2, 8888);
                if (connectError1 != ERR_OK)
                {
                    netconn_close(pConn1);
                    DebugP_log("[1]Connection with the server isn't established\r\n");
                    continue;
                }
               
    
                DebugP_log("[1]Connection with the server is established\r\n");
                // send the data to the server
                for ( uint32_t i = 0; i < APP_SEND_DATA_NUM_ITERATIONS; i++)
                {
                    memset(&snd_buf, 0, sizeof(snd_buf));
                    buf_len = snprintf(snd_buf, sizeof(snd_buf), "[1]Hello over TCP %d", i+1);
                    err1 = netconn_write(pConn1, snd_buf, buf_len, NETCONN_COPY);
                    if (err1 == ERR_OK)
                    {
                        printf("[1]\"%s\" was sent to the Server\r\n", snd_buf);
                    }
                    else
                    {
                        DebugP_log("couldn't send packet to server\r\n");
                        continue;
                    }
    
                    /* wait until the data is sent by the server */
                    if (netconn_recv(pConn1, &rxBbuf) == ERR_OK)
                    {
                        DebugP_log("[1]Successfully received the packet %d\r\n", i+1);
                        netbuf_delete(rxBbuf);
                    }
                    else
                    {
                        DebugP_log("No response from server\r\n");
                    }
                }
                netconn_close(pConn1);
                netconn_delete(pConn1);
                DebugP_log("[1]Connection closed\r\n");
                ClockP_sleep(1);
            }
        }
    }
    
    static void AppTcp_simpleclient(void *pArg)
    {
        struct netconn *pConn = NULL;
        err_t err = ERR_OK, connectError = ERR_OK;
        struct App_hostInfo_t* pHostInfo = (struct App_hostInfo_t*) pArg;
        uint32_t buf_len = 0;
        const enum netconn_type connType = NETCONN_TCP;
    
        /* Create a new connection identifier. */
        for (uint32_t pktIdx = 0; pktIdx < APP_NUM_ITERATIONS; pktIdx++)
        {
            struct netbuf *rxBbuf = NULL;
            pConn = netconn_new(connType);
            if (pConn != NULL)
            {
                /* Connect to the TCP Server */
                EnetAppUtils_print("[0]<<<< ITERATION %d >>>>\r\n", (pktIdx + 1));
                EnetAppUtils_print("[0] Connecting to: %s:%d \r\n", gHostServerIp4_1, HOST_SERVER_PORT);
                connectError = netconn_bind_if(pConn, 0);
                connectError = netconn_connect(pConn, &pHostInfo->ipAddr1, 8888);
                if (connectError != ERR_OK)
                {
                    netconn_close(pConn);
                    DebugP_log("[0]Connection with the server isn't established\r\n");
                    continue;
                }
               
    
                DebugP_log("[0]Connection with the server is established\r\n");
                // send the data to the server
                for ( uint32_t i = 0; i < APP_SEND_DATA_NUM_ITERATIONS; i++)
                {
                    memset(&snd_buf, 0, sizeof(snd_buf));
                    buf_len = snprintf(snd_buf, sizeof(snd_buf), "[0]Hello over TCP %d", i+1);
                    err = netconn_write(pConn, snd_buf, buf_len, NETCONN_COPY);
                    if (err == ERR_OK)
                    {
                        printf("[0]\"%s\" was sent to the Server\r\n", snd_buf);
                    }
                    else
                    {
                        DebugP_log("couldn't send packet to server\r\n");
                        continue;
                    }
    
                    /* wait until the data is sent by the server */
                    if (netconn_recv(pConn, &rxBbuf) == ERR_OK)
                    {
                        DebugP_log("[0]Successfully received the packet %d\r\n", i+1);
                        netbuf_delete(rxBbuf);
                    }
                    else
                    {
                        DebugP_log("No response from server\r\n");
                    }
                }
                netconn_close(pConn);
                netconn_delete(pConn);
                DebugP_log("[0]Connection closed\r\n");
                ClockP_sleep(1);
            }
        }
    }
    
    void AppTcp_showMenu(void)
    {
        ip_addr_t ipAddr1;
        ip_addr_t ipAddr2;
        int32_t addr_ok = 0;
        EnetAppUtils_print(" TCP Client Menu: \r\n");
    
        do
        {
            EnetAppUtils_print(" Enter TCP server-1 IPv4 address:(example: 192.168.101.100)\r\n");
            DebugP_scanf("%s", gHostServerIp4_1);
            EnetAppUtils_print(" Enter TCP server-2 IPv4 address:(example: 192.168.101.100)\r\n");
            DebugP_scanf("%s", gHostServerIp4_2);
            addr_ok = ip4addr_aton(gHostServerIp4_1, ip_2_ip4(&ipAddr1));
            addr_ok = ip4addr_aton(gHostServerIp4_2, ip_2_ip4(&ipAddr2));
            TaskP_yield();
        } while (addr_ok != 1);
    }
    
    void AppTcp_startClient(void)
    {
        AppTcp_showMenu();
        AppTcp_fillHostSocketInfo(&gHostInfo);
        sys_thread_new("tcpinit_thread", AppTcp_simpleclient, &gHostInfo, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
        sys_thread_new("tcpinit_thread1", AppTcp_simpleclient_1, &gHostInfo, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
    
      /* USER CODE END 5 */
    }
    

    Along with this I had made changes in the following macros in source/networking/lwip/lwip-config/am263x/enet/lwipopts.h

    I increased the number of netconns and netbuf structs (not necessarily needed but did it for testing purposes to ensure we have more than sufficient structs. This can be later reduced when you have everything working.

    Then rebuild your lwip library followed by rebuilding your application

    # STEPS TO REBUILD lwIP
    
    gmake -s -f makefile.am263x lwip-contrib-freertos_r5f.ti-arm-clang
    gmake -s -f makefile.am263x lwipif-cpsw-freertos_r5f.ti-arm-clang
    gmake -s -f makefile.am263x lwip-freertos_r5f.ti-arm-clang
    
    # Then clean and build your example with the changed TCP Client file

    Regards,
    Shaunak