Hello!

I wrote a TCP client (on STM32 microprocessor) that should send a large amount 
of data to a Windows host PC (TCP server). However, this is quite slow because 
the client waits for the ACK message after each transmitted packet.

Data is sent like this:

Client connects to Server
Sends data with tcp_write
Waits for tcp_sent callback -> tcp_sent calls the next tcp_write

I thought that tcp_sent callback would also be triggered without an ACK, when 
there is enough free memory in the tcp_sndbuf. It seems like this is not the 
case.
Find my whole code attached below.

Please note that the following options are used:

#define TCP_WND_UPDATE_THRESHOLD 536
#define TCP_SND_BUF 5360

Thanks in advance,
Adrian


CODE:
#include "tcp_trx.h"
#include "tcp.h"
#include "lwip.h"
#include "dhcp.h"

struct netif *gnetifp;

err_t tcp_connect_callback(void *arg, struct tcp_pcb *tpcb, err_t error);
void  tcp_err_callback(void *arg, err_t err);
err_t tcp_poll_callback(void *arg, struct tcp_pcb *tpcb);

err_t conn_send_next(struct conn_state *cs);
err_t conn_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);

err_t tcp_frame_transmit(void *data, struct tcp_pcb *tpcb, uint32_t length);

err_t tcp_connect_callback(void *arg, struct tcp_pcb *tpcb, err_t error)
{
  // connection established
  struct conn_state *cs;

  cs = (struct conn_state *)arg;

  cs->state = CS_CONN_OK;

  tcp_nagle_disable(cs->pcb);

  return error;
}

err_t tcp_poll_callback(void *arg, struct tcp_pcb *tpcb)
{
  err_t ret_err = ERR_OK;
  struct conn_state *cs;

  cs = (struct conn_state *)arg;

  if (cs != NULL)
  {
    switch (cs->state)
    {
      case CS_CONN_ERR:
        /* Retry connection to server */
        tpcb = tcp_new();
        cs->pcb = tpcb;
        cs->retries++;
        cs->state = CS_CONN_RETRY;
        // TODO: Reconnect if server is down and back up
        tcp_arg(cs->pcb, cs);
        // tcp_recv(tpcb, echo_recv);
        tcp_err(cs->pcb, tcp_err_callback);
        tcp_poll(cs->pcb, tcp_poll_callback, 0);
        tcp_sent(cs->pcb, conn_sent);
        tcp_bind(cs->pcb, IP_ADDR_ANY, 0);
        tcp_connect(cs->pcb, &(cs->ip), cs->port, tcp_connect_callback);
        break;

      case CS_CONN_RETRY:
        /* Wait for connection retry. */
        /* Fall back to CS_CONN_ERR or continue with CS_CONN_OK */
        break;

      case CS_CONN_OK:
        cs->retries = 0;
        cs->state = CS_READY;
        break;

      case CS_READY:
      /* Check data availability */
        if (cs->state_dat == DS_SENT) {
          cs->state_dat = DS_NONE;
        }
        else if (cs->state_dat == DS_READY)
        {
          cs->state = CS_SEND;
        }
      break;

      case CS_SEND:
        conn_sent(cs, NULL, 0);
        cs->state = CS_SENDING;
        cs->state_dat = DS_SENDING;
        break;

      case CS_SENDING:
        /* Wait for transmission to finish */
        break;

      case CS_SENT:
        cs->state = CS_READY;
        cs->state_dat = DS_SENT;
        break;
    }
  }
  return ret_err;
}

err_t conn_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
  struct conn_state *cs;

  volatile uint16_t freebuffff;

  freebuffff = tcp_sndbuf(tpcb);

  LWIP_UNUSED_ARG(len);
  LWIP_UNUSED_ARG(tpcb);

  cs = (struct conn_state *)arg;

  err_t err = conn_send_next(cs);

  if (err == ERR_OK)
  {
    cs->state = CS_SENT;
  }

  return err;
}

err_t conn_send_next(struct conn_state *cs)
{
  struct dbuf *dtr;
  struct pbuf *ptr;

  err_t wr_err = ERR_INPROGRESS;

  // pointer to data structure
  dtr = cs->d;
  // pointer to pbuf structure
  ptr = cs->p;

  // TODO: tcp_sndbuf(tpcb) ???
  /* enqueue data for transmission */

  if(dtr->curr_dlen)  // buffer not yet written entirely
  {
    uint32_t len;
    uint32_t max_len = cs->pcb->mss;

    if(dtr->curr_dlen < max_len)
    { // last packet
      len = dtr->curr_dlen;
    }
    else
    { // full size packet
      len = max_len;
    }

    ptr->payload = dtr->curr_dp;
    ptr->len = len;

    wr_err = tcp_write(cs->pcb, ptr->payload, ptr->len, 0); // TODO: copy? 
buffer management

    if(wr_err == ERR_OK)
    {
      // update pointers
      dtr->curr_dlen -= len;
      dtr->curr_dp += len;

      wr_err = ERR_INPROGRESS;
    }
  }
  else
  {
    wr_err = ERR_OK;
  }

  return wr_err;
}

void tcp_err_callback(void *arg, err_t err)
{
  struct conn_state *cs;

  LWIP_UNUSED_ARG(err);

  cs = (struct conn_state *)arg;

  cs->state = CS_CONN_ERR;

  if (cs->pcb != NULL)
  {
    tcp_close(cs->pcb);
  }
}

struct conn_state * init_tcp_connection(uint16_t port, uint8_t a3, uint8_t a2, 
uint8_t a1, uint8_t a0)
{
  ip4_addr_t ip_dest;
  IP4_ADDR(&ip_dest, a3, a2, a1, a0);

  struct tcp_pcb *tpcb;
  struct conn_state *cs;

  while (0 == dhcp_supplied_address(gnetifp))
  {
    /* TODO: Improve dhcp error handling */
    // FIXME: use netif_set_status_callback()
    // wait
    MX_LWIP_Process();
  }

  tpcb = tcp_new();

  cs = (struct conn_state *)mem_malloc(sizeof(struct conn_state));
  if (cs != NULL)
  {
    cs->state = CS_INIT;
    cs->pcb = tpcb;
    cs->retries = 0;
    cs->ip = ip_dest;
   cs->port = port;
    cs->p = NULL;
    /* pass newly allocated cs to our callbacks */
    tcp_arg(cs->pcb, cs);
    // tcp_recv(tpcb, echo_recv);
    tcp_err(cs->pcb, tcp_err_callback);
    tcp_poll(cs->pcb, tcp_poll_callback, 0);
    tcp_sent(cs->pcb, conn_sent);
    tcp_bind(cs->pcb, IP_ADDR_ANY, 0);
    tcp_connect(cs->pcb, &(cs->ip), cs->port, tcp_connect_callback);
    // ret_err = ERR_OK;
  } // else ...

  return cs;
}

_______________________________________________
lwip-users mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/lwip-users

Reply via email to