STM32 Ethernet Sever
前言
上篇章節初步介紹ST MCU 在Ethernet 的設定與ping ID,本章節會介紹2種Ethernet常見的UDP 和TCP Sever and Client都會介紹到。並比較2者不同處與注意事項,下一篇章節就會在更進一步討論LWIP等問題
UDP Vs TCP
2者基本上都會使用到UDP比較常使用在無線傳輸,TCP比較常用在有線傳輸比較表如下,基本上可靠度TCP會比UDP好,但傳輸效率上TCP會比較慢
STM32CubeMX setting
這邊基本上跟之前設定相同,並沒有特別不論UDP或TCP都是一樣的
Coding For SERVER
這邊可以發現大部分都有建立好Function可以使用,除了一開始IP宣告主要的要點就是接收
UDP
void udpServer_init(void)
{
// UDP Control Block structure
struct udp_pcb *upcb;
err_t err;
/* 1. Create a new UDP control block */
upcb = udp_new();
/* 2. Bind the upcb to the local port */
ip_addr_t myIPADDR;
IP_ADDR4(&myIPADDR, 192, 168, 0, 111);
err = udp_bind(upcb, &myIPADDR, 7); // 7 is the server UDP port
/* 3. Set a receive callback for the upcb */
if(err == ERR_OK)
{
udp_recv(upcb, udp_receive_callback, NULL);
}
else
{
udp_remove(upcb);
}
}
TCP
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);
}
}
UDP主要接收是寫在callback
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
struct pbuf *txBuf;
/* Get the IP of the Client */
char *remoteIP = ipaddr_ntoa(addr);
char buf[100];
int len = sprintf (buf,"Hello %s From UDP SERVER\n", (char*)p->payload);
/* allocate pbuf from RAM*/
txBuf = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);
/* copy the data into the buffer */
pbuf_take(txBuf, buf, len);
/* Connect to the remote client */
udp_connect(upcb, addr, port);
/* Send a Reply to the Client */
udp_send(upcb, txBuf);
/* free the UDP connection, so we can accept new clients */
udp_disconnect(upcb);
/* Free the p_tx buffer */
pbuf_free(txBuf);
/* Free the p buffer */
pbuf_free(p);
}
TCP是額外寫
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;
}
UDP的部分是比較簡單,TCP部分會需要額外寫除了接收外還要寫處理流程等等比較嚴謹
tcp_server_recv
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;
}
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);
}