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: How to work on TCP client protocol.

Part Number: AM2634

Tool/software:

Hi team,

I am working on TCP client implementation on evaluation Board  so, i am editing this enet_cpsw_rawhttpserver_am263x-cc_r5fss0-0_nortos_ti-arm-clang Project.

/*
 * tcp_client
 *
 *  Created on: 08-Aug-2024
 *      Author: AAKASH V
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
/* lwIP core includes */
#include "lwip/opt.h"
#include "lwip/sys.h"
#include "lwip/init.h"
#include "lwip/dhcp.h"
#include "lwip/timeouts.h"
#include <apps/tcpecho_raw/tcpecho_raw.h>

#include <kernel/dpl/ClockP.h>
#include <kernel/dpl/EventP.h>
#include <networking/enet/utils/include/enet_apputils.h>
#include <networking/enet/utils/include/enet_board.h>
#include "ti_board_config.h"
//#include "ti_board_open_.h"
//#include "ti_drivers_open_.h"
#include "ti_enet_config.h"
//#include "ti_enet_open_.h"
//#include "app_cpswconfighandler.h"
#include "ti_enet_lwipif.h"


#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"

#include "stdio.h"
#include "string.h"

#include "lwip/opt.h"

#include "lwip/tcp.h"




/*  protocol states */
enum tcp_client_states
{
  ES_NONE = 0,
  ES_CONNECTED,
  ES_RECEIVING,
  ES_CLOSING
};

/* structure for maintaining connection infos to be passed as argument
   to LwIP callbacks*/
struct tcp_client_struct
{
  u8_t state;             /* current connection state */
  u8_t retries;
  struct tcp_pcb *pcb;    /* pointer on the current tcp_pcb */
  struct pbuf *p;         /* pointer on the received/to be transmitted pbuf */
};



/* This callback will be called, when the client is connected to the server */
static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err);

/* This callback will be called, when the client receive data from the server */
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);

/* This callback will be called, when the server Polls for the Client */
static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb);

/* This callback will be called, when the server acknowledges the data sent by the client */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);

/* A Function to send the data to the server */
static void tcp_client_send(struct tcp_pcb *tpcb, struct tcp_client_struct *es);

/* Function to close the connection */
static void tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct *es);

/* This is the part where we are going to handle the incoming data from the server */
static void tcp_client_handle (struct tcp_pcb *tpcb, struct tcp_client_struct *es);

static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);

int counter = 0;
uint8_t data[100];

//extern TIM_HandleTypeDef htim1;

/* create a struct to store data */
struct tcp_client_struct *es = 0;

struct tcp_pcb *pcbTx = 0;

//void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
//{
//  char buf[100];
//
//  /* Prepare the first message to send to the server */
//  int len = sprintf (buf, "Sending TCPclient Message %d\n", counter);
//
//  if (counter !=0)
//  {
//    /* allocate pbuf */
//    esTx->p = pbuf_alloc(PBUF_TRANSPORT, len , PBUF_POOL);
//
//
//    /* copy data to pbuf */
//    pbuf_take(esTx->p, (char*)buf, len);
//
//    tcp_client_send(pcbTx, esTx);
//
//    pbuf_free(esTx->p);
//  }
//
//}






/* IMPLEMENTATION FOR TCP CLIENT

1. Create TCP block.
2. connect to the server
3. start communicating
*/

void tcp_client_init(void)
{
  /* 1. create new tcp pcb */
    err_t err;
  struct tcp_pcb *tpcb;

  tpcb = tcp_new();

  /* 2. Connect to the server */
  ip_addr_t destIPADDR;
  IP_ADDR4(&destIPADDR, 192, 168, 2, 2);
  err = tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected);

  if(err == ERR_OK)
  {
      tcp_client_send(tpcb, es);
  }
}


static void tcp_client_send(struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
//    struct tcp_client_struct *es = 0;

    struct tcp_pcb pcbTx;

//    struct tcp_pcb *tpcb = 0;

    struct pbuf *ptr = 0;

    err_t wr_err = ERR_OK;

    struct pbuf *txBuf;
//    tcp_pcb *pcbTx;

    char buf[1000];



    /* get pointer on pbuf from es structure */
    ptr = es->p;


    int len = sprintf (buf, "Sending TCPclient Message %d\n", counter);

    txBuf = pbuf_alloc(PBUF_TRANSPORT, len , PBUF_POOL);

    if(txBuf != NULL)
    {
       pbuf_take(es->p, (char*)buf, len);

       wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);

       if (wr_err == ERR_OK)
       {
         u16_t plen;
//         u8_t freed;

         plen = ptr->len;

         /* continue with next pbuf in chain (if any) */
         es->p = ptr->next;

         if(es->p != NULL)
         {
           /* increment reference count for es->p */
           pbuf_ref(es->p);
         }
         else
         {
           /* other problem ?? */
         }
       }
    }
}

/** This callback is called, when the client is connected to the server
 * Here we will initialise few other callbacks
 * and in the end, call the client handle function
  */
static err_t tcp_client_connected(void *arg, struct tcp_pcb *newpcb, err_t err)
{
  err_t ret_err;
  struct tcp_client_struct *es;

  LWIP_UNUSED_ARG(arg);
  LWIP_UNUSED_ARG(err);

  /* allocate structure es to maintain tcp connection information */
  es = (struct tcp_client_struct *)mem_malloc(sizeof(struct tcp_client_struct));
  if (es != NULL)
  {
    es->state = ES_CONNECTED;
    es->pcb = newpcb;
    es->retries = 0;
    es->p = NULL;

    /* pass newly allocated es structure as argument to newpcb */
    tcp_arg(newpcb, es);

//    /* initialize lwip tcp_recv callback function for newpcb  */
//    tcp_recv(newpcb, tcp_client_recv);

    /* initialize lwip tcp_poll callback function for newpcb */
    tcp_poll(newpcb, tcp_client_poll, 0);


    /* initialize LwIP tcp_sent callback function */
    tcp_sent(newpcb, tcp_client_sent);

    /* handle the TCP data */
    tcp_client_handle(newpcb, es);

    ret_err = ERR_OK;
  }
  else
  {
    /*  close tcp connection */
    tcp_client_connection_close(newpcb, es);
    /* return memory error */
    ret_err = ERR_MEM;
  }
  return ret_err;
}


/** This callback is called, when the client receives some data from the server
 * if the data received is valid, we will handle the data in the client handle function
  */
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
  struct tcp_client_struct *es;
  err_t ret_err;

  LWIP_ASSERT("arg != NULL",arg != NULL);

  es = (struct tcp_client_struct *)arg;

  /* if we receive an empty tcp frame from server => close connection */
  if (p == NULL)
  {
    /* remote host closed connection */
    es->state = ES_CLOSING;
    if(es->p == NULL)
    {
       /* we're done sending, close connection */
       tcp_client_connection_close(tpcb, es);
    }
    else
    {
      /* we're not done yet */
//      /* acknowledge received packet */
//      tcp_sent(tpcb, tcp_client_sent);

      /* send remaining data*/
//      tcp_client_send(tpcb, es);
    }
    ret_err = ERR_OK;
  }
  /* else : a non empty frame was received from server but for some reason err != ERR_OK */
  else if(err != ERR_OK)
  {
    /* free received pbuf*/
    if (p != NULL)
    {
      es->p = NULL;
      pbuf_free(p);
    }
    ret_err = err;
  }
  else if(es->state == ES_CONNECTED)
  {
   /* store reference to incoming pbuf (chain) */
    es->p = p;

    // tcp_sent has already been initialized in the beginning.
//    /* initialize LwIP tcp_sent callback function */
//    tcp_sent(tpcb, tcp_client_sent);

    /* Acknowledge the received data */
    tcp_recved(tpcb, p->tot_len);

    /* handle the received data */
    tcp_client_handle(tpcb, es);

    pbuf_free(p);

    ret_err = ERR_OK;
  }
  else if(es->state == ES_CLOSING)
  {
    /* odd case, remote side closing twice, trash data */
    tcp_recved(tpcb, p->tot_len);
    es->p = NULL;
    pbuf_free(p);
    ret_err = ERR_OK;
  }
  else
  {
    /* unknown es->state, trash data  */
    tcp_recved(tpcb, p->tot_len);
    es->p = NULL;
    pbuf_free(p);
    ret_err = ERR_OK;
  }
  return ret_err;
}


static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
{
  err_t ret_err;
  struct tcp_client_struct *es;

  es = (struct tcp_client_struct *)arg;
  if (es != NULL)
  {
    if (es->p != NULL)
    {
        // tcp_sent has already been initialized in the beginning.
//      tcp_sent(tpcb, tcp_client_sent);
      /* there is a remaining pbuf (chain) , try to send data */
//      tcp_client_send(tpcb, es);
    }
    else
    {
      /* no remaining pbuf (chain)  */
      if(es->state == ES_CLOSING)
      {
        /*  close tcp connection */
        tcp_client_connection_close(tpcb, es);
      }
    }
    ret_err = ERR_OK;
  }
  else
  {
    /* nothing to be done */
    tcp_abort(tpcb);
    ret_err = ERR_ABRT;
  }
  return ret_err;
}

/** This callback is called, when the server acknowledges the data sent by the client
 * If there is no more data left to sent, we will simply close the connection
  */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
  struct tcp_client_struct *es;

  LWIP_UNUSED_ARG(len);

  es = (struct tcp_client_struct *)arg;
  es->retries = 0;

  if(es->p != NULL)
  {
  // tcp_sent has already been initialized in the beginning.
    /* still got pbufs to send */
//    tcp_sent(tpcb, tcp_client_sent);


//    tcp_client_send(tpcb, es);
  }
  else
  {
    /* if no more data to send and client closed connection*/
    if(es->state == ES_CLOSING)
      tcp_client_connection_close(tpcb, es);
  }
  return ERR_OK;
}


/** A function to send the data to the server
  */
//static void tcp_client_send(struct tcp_pcb *tpcb, struct tcp_client_struct *es)
//{
//  struct pbuf *ptr;
//  err_t wr_err = ERR_OK;
//
//  while ((wr_err == ERR_OK) &&
//         (es->p != NULL) &&
//         (es->p->len <= tcp_sndbuf(tpcb)))
//  {
//
//    /* get pointer on pbuf from es structure */
//    ptr = es->p;
//
//    /* enqueue data for transmission */
//    wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
//
//    if (wr_err == ERR_OK)
//    {
//      u16_t plen;
//      u8_t freed;
//
//      plen = ptr->len;
//
//      /* continue with next pbuf in chain (if any) */
//      es->p = ptr->next;
//
//      if(es->p != NULL)
//      {
//        /* increment reference count for es->p */
//        pbuf_ref(es->p);
//      }
//
//     /* chop first pbuf from chain */
//      do
//      {
//        /* try hard to free pbuf */
//        freed = pbuf_free(ptr);
//      }
//      while(freed == 0);
//     /* we can read more data now */
////     tcp_recved(tpcb, plen);
//   }
//   else if(wr_err == ERR_MEM)
//   {
//      /* we are low on memory, try later / harder, defer to poll */
//     es->p = ptr;
//   }
//   else
//   {
//     /* other problem ?? */
//   }
//  }
//}





static void tcp_client_connection_close(struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{

  /* remove all callbacks */
  tcp_arg(tpcb, NULL);
  tcp_sent(tpcb, NULL);
  tcp_recv(tpcb, NULL);
  tcp_err(tpcb, NULL);
  tcp_poll(tpcb, NULL, 0);

  /* delete es structure */
  if (es != NULL)
  {
    mem_free(es);
  }

  /* close tcp connection */
  tcp_close(tpcb);
}

/* Handle the incoming TCP Data */

static void tcp_client_handle (struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
  /* get the Remote IP */
  ip4_addr_t inIP = tpcb->remote_ip;
  uint16_t inPort = tpcb->remote_port;

  /* Extract the IP */
  char *remIP = ipaddr_ntoa(&inIP);



//  esTx->state = es->state;
//  esTx->pcb = es->pcb;
//  esTx->p = es->p;

//  esTx = es;
  pcbTx = tpcb;

  counter++;

}

I am using this code so if you see i am facing some issue My debugger is reaching up to till here pbuf_take() function 

after my debugger is not going to next line

but i checked inside the function also i could't able to resolve ,so can you say any suggestion regarding this.

if my logic is wrong can you provide some example project regarding this.

Thank you,

  • Hi Aakash,

    Seems like you are running out of pbuf memory and pbuf_take is failing.

    pbuf_take() will return an error, but in your implementation I don't see the function return value being stored in any variable. Probably the error here is ERR_MEM (which means no pbufs left). Are you handling the pbuf_free() properly? Incase you are sending out large amounts of data and need more Pbufs, please try increasing the pbufs in lwipopts.h file and rebuild the lwip stack.

    You can also halt the core and check the "lwip_stats". They will show the pbuf memory related errors as well.

    Regards,
    Shaunak