Minimal RAW TCP Client

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Minimal RAW TCP Client

Jamie
G'day,

I'm looking to build a minimal RAW TCP client on a SAME70 (ATSAME70Q21) MCU.
Thankfully there is already a port for lwIP on the SAME70, which allows for
the basic TCP Echo to be run on the SAME70 Xplained evaluation board (which
I have done with no problem).

The problem I'm having at the moment is in modifying the TCP Echo example
code in the following ways:

1. The lwIP TCP needs to be a client, connecting to an external TCP server.
The example code is based on lwIP being a TCP server, so I'm struggling a
bit with replacing/removing the server-based code.

2. Simplifying the code. At the moment I only need to read data from the TCP
client, I don't need any tcp_send() functionality. Additionally, the entire
application on the SAME70 is predicated on this TCP connection, so a
blocking while loop until the connection is established is acceptable
(again, trying to keep it as simple as possible).

Here <https://www.avrfreaks.net/forum/ethernet-socket-communication-same70>  
is a link to a forum post where I got the TCP Echo example to work on the
SAME70 evaluation board. This is the code I'm currently trying to modify.
This is what I have so far (including some minimal commentary):

#include "sam_comms.h"

#if LWIP_TCP

static struct tcp_pcb *sam_tcp_pcb;

enum sam_tcp_states
{
        ES_NONE = 0,
        ES_CONNECTED,
        ES_RECEIVED,
        ES_CLOSING
};

struct sam_tcp_state
{
        u8_t state;
        u8_t retries;
        struct tcp_pcb *pcb;
        /* pbuf (chain) to recycle */
        struct pbuf *p;
};

void sam_tcp_init(void)
{
  sam_tcp_pcb = tcp_new();
  if (sam_tcp_pcb != NULL)
  {
    err_t err;
    struct sam_tcp_state *es;

    //No error handling for the time being
    tcp_err(sam_tcp_pcb, NULL);
    tcp_recv(sam_tcp_pcb, sam_tcp_recv);

    //No polling for the time being
    tcp_poll(sam_tcp_pcb, NULL, 0);
       
    //I don't believe I need to bind?
    //err = tcp_bind(sam_tcp_pcb, IP_ADDR_ANY, 9999);

    //I'm thinking that somewhere here I should have a while or do loop, to
keep trying
    //to connect until a connection is established?
    err = tcp_connect(sam_tcp_pcb, IP_ADDR_ANY, 9999, sam_tcp_connect);
    if (err != ERR_OK)
    {
        //Connection not successful, close the pcb
    tcp_close(sam_tcp_pcb);
        }
  }
  else
  {
    /* abort? output diagnostic? */
  }
}

err_t sam_tcp_connect(void *arg, struct tcp_pcb *connectedpcb, err_t err)
{
        err_t ret_err;
        struct sam_tcp_state *es;
       
        es = (struct sam_tcp_state *)mem_malloc(sizeof(struct sam_tcp_state));
        if (es != NULL)
        {
                es->state = ES_CONNECTED;
                es->pcb = connectedpcb;
                es->retries = 0;
                es->p = NULL;
        }
        else
        {
                ret_err = ERR_MEM;
        }
        return ret_err;
}

err_t sam_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t
err)
{
  struct sam_tcp_state *es;
  err_t ret_err;

  LWIP_ASSERT("arg != NULL",arg != NULL);
  es = (struct sam_tcp_state *)arg;
  if (p == NULL)
  {
    /* remote host closed connection */
    es->state = ES_CLOSING;
    if(es->p == NULL)
    {
       /* we're done sending, close it */
       //sam_tcp_close(tpcb, es);
    }
    else
    {
      /* we're not done yet */
        //The below is commented out as this isn't an echo program
        //sam_tcp_send(tpcb, es);
    }
    ret_err = ERR_OK;
  }
  else if(err != ERR_OK)
  {
    /* cleanup, for unknown reason */
    if (p != NULL)
    {
      es->p = NULL;
      pbuf_free(p);
    }
    ret_err = err;
  }
  else if(es->state == ES_CONNECTED)
  {
    /* first data chunk in p->payload */
    es->state = ES_RECEIVED;
    /* store reference to incoming pbuf (chain) */
    es->p = p;
    /* install send completion notifier */
    //The below is commented out as this isn't an echo program
    //tcp_sent(tpcb, echo_sent);
    //sam_tcp_send(tpcb, es);
    ret_err = ERR_OK;
  }
  else if (es->state == ES_RECEIVED)
  {
    /* read some more data */
    if(es->p == NULL)
    {
      es->p = p;
      //The below is commented out as this isn't an echo program
      //tcp_sent(tpcb, echo_sent);
      //sam_tcp_send(tpcb, es);
    }
    else
    {
      struct pbuf *ptr;

      /* chain pbufs to the end of what we recv'ed previously  */
      ptr = es->p;
      pbuf_chain(ptr,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;
  }
  if (!strncmp(p->payload, "Quit", 4))
  {
          //sam_tcp_close(tpcb, es);
  } else
  {
          //
  }
  return ret_err;
}
#endif /* LWIP_TCP */

Am I on the right track? How would I go about adding a loop to continue
trying to connect to the TCP server? In terms of global macros, I've only
changed LWIP_SOCKET to 0 (disable the sockets API) and have left NO_SYS as
0. Is this correct? Are there any other macros I need to change for a RAW
TCP client implementation?

Any input, guidance or example code would be very much appreciated!

Thanks!



--
Sent from: http://lwip.100.n7.nabble.com/lwip-users-f3.html

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: Minimal RAW TCP Client

Jamie
Hi Lee,

Thanks for your response (I've copied and pasted it below to ensure it's
part of this thread moving forward).

I was able to follow along with your code, thank you for sharing it.

At this stage it would be particularly helpful if anyone had any specific
lwIP code or advice as to how I should proceed. I will play with some
polling-based connection attempt approaches (using Lee's input as a
reference), but any additional guidance in the meantime would be greatly
appreciated!


> Hello I hope I am replying to this correctly or some one will tell me how.
>
> Regarding the TCP client development,
> I wrote both a server and client in Processing where I could rapidly
> develop
> them. I did it to help our development and test of an embedded product and
> the PC and Android applications that connect to and control our product
> through TCP socket.   My two Processing creations allowed us to test each
> independently.
>
> For client See my github at:
> https://github.com/ForrestErickson/Processing-ClientWithReconnect
> This is the server:
> https://github.com/ForrestErickson/Processing-ServerComeAndGoes
>
> You will have to read the code to see the keyboard commands to change
> server
> status, but only if you want to.
> I hope this is of use and feel free to explain any confusion you have.
>
> If Processing does not intimidate you (it's super easy) I advise you start
> by running on one Windows PC both the server and client and play with
> them.
> Run them in any order and close in any order.
>
> (Forrest) Lee Erickson
> P.S. If I have one supper power it would be called novel spelling.





--
Sent from: http://lwip.100.n7.nabble.com/lwip-users-f3.html

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