STM32 ETHERNET #4. TCP SERVER

This it the fourth tutorial in the STM32 Ethernet series, and today we will see how to create TCP Server using STM32.I have already covered the UDP server and Client, and you can check them here.

CubeMX Setup

The cubeMX Setup will remain same as the previous Tutorials. I will leave some pictures below for you to refer. For the detailed setup, check out the Connection tutorial


Some Insight into the CODE

Below are the steps Given to implement the TCP Server with STM32

/* Impementation for the TCP Server
   1. Create TCP Block.
   2. Bind the Block to server address, and port.
   3. Listen for the  incoming requests by the client
   4. Accept the Request, and now the server is ready for the data transfer
 */

initialization of the TCP server

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

	tpcb = tcp_new();

	err_t err;

	/* 2. bind _pcb to port 7 ( protocol) */
	ip_addr_t myIPADDR;
	IP_ADDR4(&myIPADDR, 192, 168, 0, 111);
	err = tcp_bind(tpcb, &myIPADDR, 7);

	if (err == ERR_OK)
	{
		/* 3. start tcp listening for _pcb */
		tpcb = tcp_listen(tpcb);

		/* 4. initialize LwIP tcp_accept callback function */
		tcp_accept(tpcb, tcp_server_accept);
	}
	else
	{
		/* deallocate the pcb */
		memp_free(MEMP_TCP_PCB, tpcb);
	}
}
  • Here First of all, we will create a new TCP Control block. tpcb = tcp_new();
  • Then we will bind the Block to the local IP Address and Port. This will be the server IP and Port. tcp_bind(tpcb, &myIPADDR, 7)
  • The next step is to listen for the incoming traffic. tpcb = tcp_listen(tpcb);
  • And finally we will accept the request from the client. tcp_accept(tpcb, tcp_server_accept);

tcp_server_accept

It initializes few more things. The code for it is shown below

static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
  err_t ret_err;
  struct tcp_server_struct *es;

  LWIP_UNUSED_ARG(arg);
  LWIP_UNUSED_ARG(err);

  /* set priority for the newly accepted tcp connection newpcb */
  tcp_setprio(newpcb, TCP_PRIO_MIN);

  /* allocate structure es to maintain tcp connection information */
  es = (struct tcp_server_struct *)mem_malloc(sizeof(struct tcp_server_struct));
  if (es != NULL)
  {
    es->state = ES_ACCEPTED;
    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_server_recv);

    /* initialize lwip tcp_err callback function for newpcb  */
    tcp_err(newpcb, tcp_server_error);

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

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

You can see above that it initializes the callbacks like tcp_arg, tcp_recv and tcp_poll. But here are interested in tcp_recv only.



tcp_server_recv

tcp_recv initializes the callback tcp_server_recv, which will be called whenever the server receives some data from the client. The function is shown below

static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
  struct tcp_server_struct *es;
  err_t ret_err;

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

  es = (struct tcp_server_struct *)arg;

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

      /* send remaining data*/
      tcp_server_send(tpcb, es);
    }
    ret_err = ERR_OK;
  }
  /* else : a non empty frame was received from client 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_ACCEPTED)
  {
    /* first data chunk in p->payload */
    es->state = ES_RECEIVED;

    /* store reference to incoming pbuf (chain) */
    es->p = p;

    /* initialize LwIP tcp_sent callback function */
    tcp_sent(tpcb, tcp_server_sent);

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

    ret_err = ERR_OK;
  }
  else if (es->state == ES_RECEIVED)
  {
    /* more data received from client and previous data has been already sent*/
    if(es->p == NULL)
    {
      es->p = p;

      /* handle the received data */
      tcp_server_handle(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;
  }
  return ret_err;
}
  • Here few things are predefines, and we will leave them as it is. We will focus on line 42 onwards.
  • When the data is received for the first time, the state will be set to accepted.
  • This is to make sure that the tcp_sent callback is initialized, and it should only initialize once.
  • After this, the state will be changed to RECEIVED, and the code at line number 58 will execute.
  • here we will store the incoming data into our buffer (line 63), and call the server handle function (line 66) to handle the incoming data.
  • I have created this server handle function, so that there you can process the incoming data better. And you should leave the received callback as it is.


Server Handle

static void tcp_server_handle (struct tcp_pcb *tpcb, struct tcp_server_struct *es)
{
	struct tcp_server_struct *esTx;

	/* 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;

	char buf[100];
	memset (buf, '\0', 100);

	strncpy(buf, (char *)es->p->payload, es->p->tot_len);
	strcat (buf, "+ Hello from TCP SERVER\n");


	esTx->p->payload = (void *)buf;
	esTx->p->tot_len = (es->p->tot_len - es->p->len) + strlen (buf);
	esTx->p->len = strlen (buf);

	tcp_server_send(tpcb, esTx);

	pbuf_free(es->p);

}
  • Here we will get the IP address and the port for the client
  • The IP address is in the integer format, and we can use the function ipaddr_ntoa to convert the address in the proper format
  • The incoming data from the client is stored in the payload of the p buf.
  • I am mixing the incoming data with the new one, and storing it in the buffer.
  • Then we will update the p buf with the new payload, total length, and the length of the buffer to send.
  • Finally we will send the p buf to the client using tcp_server_send function.


RESULT

  • Here I have used the Hercules as the TCP Client
  • The Pink color is the data sent by the client, and in response, the Black color data is sent by the Server

Check out the Video Below




Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

Subscribe
Notify of

34 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×