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.

MSP432E401Y: Contribution Post/Guide for Simplelink TCP Client Example

Part Number: MSP432E401Y


Hello TI Community,

I just wanted to give back since I've had good people help me here. There are a lot of posts regarding TCP client and how to get it to work but it is all over the place in my opinion. Hopefully this post summarise what people need to do in the future as far as Simplelink NDK API is concerned.

For TCP Client example, while the existing Python scripts in SDK for TCP Server can be modified to get it to work, the steps below aim to reduce effort. The code provided below sends a message as well as expects messages from a server. It will block until a server message is sent.

Thanks,

Utsav

  • Part Number: MSP432E401Y



    1. Download and install Hercules Tool from https://www.hw-group.com/software/hercules-setup-utility . This tool allows a computer to be be a TCP Server/Client and much more. No code needed!

    2. Click on TCP Server tab and enter a port number under Port field. Leave the TEA authorisation to default unless you know what you are doing.


    3. Replace tcpEcho.c and tcpEchoHooks.c with code below in Simplelink tcpEcho example. Modify as per your needs. Click on Server Echo check box to see messages when you type it for sending to client.
       

    Note: The code for tcpEcho.c has an ip address in the example. Replace it with the dynamic ip printed when network services start or put in the static ip assigned via .syscfg. I can make a another post for how to setup static ip on request.

    tcpEchoHooks.c :

    /*
     * Copyright (c) 2015-2019, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  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.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
     * CONTRIBUTORS 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.
     */
    
    /*
     *    ======== tcpEchoHooks.c ========
     */
    
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdbool.h>
    
    #include <pthread.h>
    
    
    /* XDC module Header Files */
    #include <xdc/std.h>                                                            // XDC "base types" - must be included FIRST
    #include <xdc/runtime/Types.h>                                                  // XDC constants/types used in xdc.runtime pkg
    #include <xdc/cfg/global.h>                                                     // For all BIOS instances created statically in RTOS .cfg file
    #include <xdc/runtime/Error.h>                                                  // For error handling (e.g. Error block)
    #include <xdc/runtime/System.h>
    
    /* TI-RTOS Kernel Header Files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    
    #include <ti/ndk/inc/netmain.h>
    
    #include <ti/ndk/slnetif/slnetifndk.h>
    #include <ti/net/slnet.h>
    #include <ti/net/slnetif.h>
    #include <ti/net/slnetutils.h>
    
    #include <ti/display/Display.h>
    #include <ti/drivers/emac/EMACMSP432E4.h>
    
    #define TCPPORT 65
    #define TCPHANDLERSTACK 2048
    
    /* Prototypes */
    //extern void *tcpHandler(void *arg0);
    extern void tcpClient(UArg a0, UArg a1);
    extern void *tcpClientThread(void *arg0);
    
    extern Display_Handle display;
    
    /*
     *  ======== netIPAddrHook ========
     *  user defined network IP address hook
     */
    void netIPAddrHook(uint32_t IPAddr, unsigned int IfIdx, unsigned int fAdd)
    {
        pthread_t           thread;
        pthread_attr_t      attrs;
        struct sched_param  priParam;
        int                 retc;
        int                 detachState;
        uint32_t            hostByteAddr;
        static uint16_t     arg0 = TCPPORT;
        static bool         createTask = true;
        int32_t             status = 0;
    
        if (fAdd) {
            Display_printf(display, 0, 0, "Network Added: ");
        }
        else {
            Display_printf(display, 0, 0, "Network Removed: ");
        }
    
        /* print the IP address that was added/removed */
        hostByteAddr = NDK_ntohl(IPAddr);
        Display_printf(display, 0, 0, "If-%d:%d.%d.%d.%d\n", IfIdx,
                (uint8_t)(hostByteAddr>>24)&0xFF, (uint8_t)(hostByteAddr>>16)&0xFF,
                (uint8_t)(hostByteAddr>>8)&0xFF, (uint8_t)hostByteAddr&0xFF);
    
        /* initialize SlNet interface(s) */
        status = ti_net_SlNet_initConfig();
        if (status < 0)
        {
            Display_printf(display, 0, 0, "Failed to initialize SlNet interface(s)"
                           "- status (%d)\n", status);
            while (1);
        }
    
    
        if (fAdd && createTask) {
            /*
             *  Create the Task that farms out incoming TCP connections.
             *  arg0 will be the port that this task listens to.
             *  @note - Only pthread works. Not sure why
             */
    
            /* Set priority and stack size attributes */
            pthread_attr_init(&attrs);
            priParam.sched_priority = 1;
    
            detachState = PTHREAD_CREATE_DETACHED;
            retc = pthread_attr_setdetachstate(&attrs, detachState);
            if (retc != 0) {
                Display_printf(display, 0, 0,
                        "netIPAddrHook: pthread_attr_setdetachstate() failed\n");
                while (1);
            }
    
            pthread_attr_setschedparam(&attrs, &priParam);
    
            retc |= pthread_attr_setstacksize(&attrs, TCPHANDLERSTACK);
            if (retc != 0) {
                Display_printf(display, 0, 0,
                        "netIPAddrHook: pthread_attr_setstacksize() failed\n");
                while (1);
            }
    
            retc = pthread_create(&thread, &attrs, tcpClientThread, (void *)&arg0);
            if (retc != 0) {
                Display_printf(display, 0, 0,
                        "netIPAddrHook: pthread_create() failed\n");
                while (1);
            }
    
            createTask = false;
    
        }
    }
    
    /*
     *  ======== serviceReport ========
     *  NDK service report.  Initially, just reports common system issues.
     */
    void serviceReport(uint32_t item, uint32_t status, uint32_t report, void *h)
    {
        static char *taskName[] = {"Telnet", "", "NAT", "DHCPS", "DHCPC", "DNS"};
        static char *reportStr[] = {"", "Running", "Updated", "Complete", "Fault"};
        static char *statusStr[] =
            {"Disabled", "Waiting", "IPTerm", "Failed","Enabled"};
    
        Display_printf(display, 0, 0, "Service Status: %-9s: %-9s: %-9s: %03d\n",
                taskName[item - 1], statusStr[status], reportStr[report / 256],
                report & 0xFF);
    
        /* report common system issues */
        if ((item == CFGITEM_SERVICE_DHCPCLIENT) &&
                (status == CIS_SRV_STATUS_ENABLED) &&
                (report & NETTOOLS_STAT_FAULT)) {
            Display_printf(display, 0, 0,
                    "DHCP Client initialization failed; check your network.\n");
    
            while (1);
        }
    }



    tcpEcho.c :

    /*
     * Copyright (c) 2014-2019, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  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.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
     * CONTRIBUTORS 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.
     */
    
    /*
     *    ======== tcpEcho.c ========
     *    Contains BSD sockets code.
     */
    
    /* For usleep() */
    #include <unistd.h>
    #include <stdint.h>
    #include <stddef.h>
    #include <string.h>
    #include <stdio.h>
    
    #include <pthread.h>
    
    
    /* NDK support */
    #include <ti/ndk/inc/socketndk.h>
    #include <ti/ndk/inc/socket.h>
    #include <ti/ndk/inc/netmain.h>
    
    #include <ti/net/slnetutils.h>
    
    
    #include <ti/display/Display.h>
    
    #define TCPPACKETSIZE 256
    
    extern Display_Handle display;
    
    extern int fdOpenSession(void *hTask);
    extern void fdCloseSession(void *hTask);
    extern void *TaskSelf();
    
    
    /*
     *  ======== tcpWorker ========
     *  Task to handle TCP connection. Can be multiple Tasks running
     *  this function.
     */
    
    void *tcpClientThread(void *arg0){
        fdOpenSession(TaskSelf());
    
        struct sockaddr_in serverAddr;
        uint16_t port = *((uint16_t *)(arg0));
    
        Display_printf(display, 0, 0, "TCP Echo example started\n");
    
        SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        if(clientSocket == INVALID_SOCKET){
            Display_printf(display, 0, 0, "Con, err: [%d]\n", fdError());
            Display_printf(display, 0, 0, "Unable to create socket\n");
        }
    
        /* SL supports IPv4 DNS only */
        bzero(&serverAddr, sizeof(struct sockaddr_in));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = NDK_htons(port); // Pass to arg0 or hard code here
        /* Replace with Dynamic Ip printed in example 
        *  or static ip set in .syscfg
        */
        serverAddr.sin_addr.s_addr = inet_addr("192.168.86.217"); 
    
    /* @note Uncomment for optional Socket options
     *
     * {
            struct timeval timeout;
            // Configure our Tx and Rx timeout to be 5 seconds
            timeout.tv_sec = 5;
            timeout.tv_usec = 0;
            int res = NDK_setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof( timeout ) );
    
            if(res < 0){
                Display_printf(display, 0, 0, "Con, err: [%d]\n", fdError());
                Display_printf(display, 0, 0, "Unable to setSocketopt\n");
            }
        }
            // Linger ensures that
        {
            struct linger lng;
            lng.l_onoff = 1; // enable linger
            lng.l_linger = 1; // seconds
            int res = NDK_setsockopt(clientSocket, SOL_SOCKET, SO_LINGER, &lng,
                  sizeof(lng));
    
            if(res < 0){
                Display_printf(display, 0, 0, "Con, err: [%d]\n", fdError());
                Display_printf(display, 0, 0, "Unable to setSocketopt\n");
            }
        }
        {
            // Set KEEPALIVE option to 1
            int                optval;
            int                optlen = sizeof(optval);
            optval = 1;
            int res = NDK_setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
    
            if(res < 0){
                Display_printf(display, 0, 0, "Con, err: [%d]\n", fdError());
                Display_printf(display, 0, 0, "Unable to setSocketopt\n");
            }
        }*/
    
        int connectStatus;
        connectStatus = connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    
        if(connectStatus < 0){
            Display_printf(display, 0, 0, "Con, err: [%d]\n", fdError());
            Display_printf(display, 0, 0, "Unable to connect socket\n");
            sleep(2);
        }else{
            Display_printf(display, 0, 0, "Connected\n");
        }
    
        char *sendBuffer = "I am the Client. Hello :)\n";
        int nbytes = strlen(sendBuffer) + 1;
        send(clientSocket, (char *)sendBuffer, nbytes, 0 );
    
        char recvBuffer[TCPPACKETSIZE] = {0}; //Either this or heap allocated space
    
        while(1){
    
            Display_printf(display, 0, 0, "\nSend a message from server!\n");
            int bytes = recv(clientSocket, (char *)recvBuffer, 1000, 0);
            if (bytes > 0) {
                Display_printf(display, 0, 0, "\n%s\n", recvBuffer);
            }else if (bytes == 0){
                Display_printf(display, 0, 0, "\nServer shutdown\n");
            }else{
                Display_printf(display, 0, 0, "\nFailed to get bytes\n");
            }
        }
    
    shutdown:
    
        if(shutdown(clientSocket,
                        SHUT_RD) < 0){
            Display_printf(display, 0, 0, "Con, err: [%d]\n", fdError());
            Display_printf(display, 0, 0, "Unable to shutdown socket\n");
        }
        fdCloseSession(TaskSelf());
        return(NULL);
    }