TCP echo server with lwip

Posted on June 11, 2012 by Sanchayan Maity
Tags: lwip

LWIP stands for Lightweight Internet Protocol stack and was developed by Adum Dunkels at the Swedish Institute Of Computer Science. It is open source TCP/IP stack designed mainly for embedded systems and written in C.

Today, I will give you guys a sample code for implementing TCP/IP echo server. The assumption is that, you have lwip already ported to your controller. So, here is the code.

char tcp_buffer[1024];
static void close_conn (struct tcp_pcb *pcb)
{
    tcp_arg(pcb, NULL);
    tcp_sent(pcb, NULL);
    tcp_recv(pcb, NULL);
    tcp_close(pcb);
}

static err_t echo_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
    int i;
    int len;
    char *pc;

    if ( err == ERR_OK && p != NULL )
    {
        tcp_recved( pcb, p->tot_len );
        pc = (char *)p->payload;
        len =p->tot_len;

        for( i=0; i<len; i++ )
        {
            tcp_buffer[i] = pc[i];
        }

        if( tcp_buffer[0] == 'X' )
            close_conn( pcb );

        pbuf_free( p );

        if( len > tcp_sndbuf( pcb ) )
            len= tcp_sndbuf( pcb );

        tcp_write( pcb, tcp_buffer, len, 0 );
        tcp_sent( pcb, NULL );
    }
    else
    {
        pbuf_free( p );
    }

    if( err == ERR_OK && p == NULL )
    {
        close_conn( pcb );
    }

    return ERR_OK;
}

static err_t echo_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
    LWIP_UNUSED_ARG( arg );
    LWIP_UNUSED_ARG( err );
    tcp_setprio( pcb, TCP_PRIO_MIN );
    tcp_recv( pcb, echo_recv );
    tcp_err( pcb, NULL );
    tcp_poll( pcb, NULL, 4 );
    return ERR_OK;
}

void tcp_init(void)
{
    struct tcp_pcb *tcp_pcb;
    tcp_pcb = tcp_new();
    tcp_bind(tcp_pcb, IP_ADDR_ANY, 23);

    tcp_pcb = tcp_listen( tcp_pcb );
    tcp_accept( tcp_pcb, echo_accept );
}

Instructions for use:

  1. Set up lwip stack with a call to lwipInit function.

  1. Call tcp_init() before entering while(1). Everything will be running in an interrupt context, so if you want to test this application alone, your while(1) can be empty.

Let me explain how the code works.

First, on call to the tcp_init function, a pointer to tcp_pcb structure is created. pcb is the short form of Protocol Control Buffer. Then a new tcp_pcb structure is created, with a call to tcp_new function, which returns a pointer, which is assingned to the pointer we just declared. This is then bound to the pcb structure, by call to tcp_bind and passing the pointer to tcp_pcb structure as the first argument, binding it to any ip address, which the board is assigned or gets through dhcp, as the second argument and finally the tcp port number on which communication will take place as the third argument. A call to tcp_listen puts the connection in a listening state for any incoming connections. The tcp_accept function is passed the pointer we have been using till now, and the name of the function as the second argument, which will be called, when an incoming connection request arrives on the listening connection. This is an implementation of callback functions. Google it. Here is a link http://www.newty.de/fpt/callback.html.

When an incoming connection request arrives, the echo_accept function is called. The main line here is the fourth line. It passes the relevant pointer as the first argument and the function to be called when new data arrives.

On data arrival, the echo_recv function gets called. tcp_recved must be called when the application has received the data. The len argument indicates the length of the received data. After this, the data is taken (should be self explanatory on how the data is extracted and what happens when an “X” is sent) and send back with a call to tcp_write and tcp_sent.

In case, you are using it with Stellaris, to which it has already been ported to by Texas Instruments, go the StellarisWare folder. In it, go to third_party, lwip-1.3.1 (your lwip version may be different) and then docs. Open and read the rawapi document in it, to understand the use of these functions.

Hope, that helps.

N.B. Every function in lwip returns error codes. I have ignored the return codes in most of the cases above. To make a more robust application, perform your operation based on what return codes you get.