/* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of and a contribution to the lwIP TCP/IP stack. * * Credits go to Adam Dunkels (and the current maintainers) of this software. * * Christiaan Simons rewrote this file to get a more stable echo example. */ /** * @file * TCP echo server example using raw API. * * Echos all bytes sent by connecting client, * and passively closes when client is done. * */ #include "lwip/opt.h" #include "lwip/debug.h" #include "lwip/stats.h" #include "lwip/tcp.h" #include "tcpecho_raw.h" #include #if LWIP_TCP && LWIP_CALLBACK_API #define MAX_TCP_RECV_SIZE (56000) #define MAX_TCP_SHOW_SIZE 80 #define TCP_ACK_MSG_SIZE 20 #define TCP_EOF_CH '\n' static struct tcp_pcb *tcpecho_raw_pcb; enum tcpecho_raw_states { ES_NONE = 0, ES_ACCEPTED, ES_RECEIVED, ES_CLOSING }; struct tcpecho_raw_state { u8_t state; u8_t retries; struct tcp_pcb *pcb; /* pbuf (chain) to recycle */ struct pbuf *p; }; static void tcpecho_raw_free(struct tcpecho_raw_state *es) { if (es != NULL) { if (es->p) { /* free the buffer chain if present */ pbuf_free(es->p); } mem_free(es); } } static void tcpecho_raw_close(struct tcp_pcb *tpcb, struct tcpecho_raw_state *es) { tcp_arg(tpcb, NULL); tcp_sent(tpcb, NULL); tcp_recv(tpcb, NULL); tcp_err(tpcb, NULL); tcp_poll(tpcb, NULL, 0); tcpecho_raw_free(es); tcp_close(tpcb); } static void tcpecho_raw_error(void *arg, err_t err) { struct tcpecho_raw_state *es; LWIP_UNUSED_ARG(err); es = (struct tcpecho_raw_state *)arg; tcpecho_raw_free(es); } static void tcpecho_raw_ack_size(struct tcp_pcb *tpcb, int ack_len) { struct pbuf *ack_buf = NULL; // ack message ack_buf = pbuf_alloc(PBUF_TRANSPORT, TCP_ACK_MSG_SIZE, PBUF_RAM); snprintf(ack_buf->payload, TCP_ACK_MSG_SIZE, "%d\n\0", ack_len); ack_buf->len = strlen(ack_buf->payload); ack_buf->tot_len = strlen(ack_buf->payload); ack_buf->next = NULL; tcp_write(tpcb, ack_buf->payload, ack_buf->len, 1); pbuf_free(ack_buf); } // compute received message length until '\n' static void tcpecho_raw_ack(struct tcp_pcb *tpcb, struct tcpecho_raw_state* es){ struct pbuf *ptr = es->p; char *recv_buf = ptr->payload; int recv_len = ptr->len; static char *g_buf = NULL; //global received buffer static int g_buf_size = 0; lw_print("lw: [%s] recv %d tot %d next %p ref %d tye %d id %d %s\n", __func__, ptr->len, ptr->tot_len, ptr->next, ptr->ref, ptr->type_internal, ptr->if_idx, ptr->payload); if(g_buf == NULL) { g_buf = (char *)malloc(MAX_TCP_RECV_SIZE); memset(g_buf, 0, MAX_TCP_RECV_SIZE); memcpy(g_buf, recv_buf, recv_len); } else { if(g_buf_size + recv_len <= MAX_TCP_RECV_SIZE) { memcpy(g_buf + g_buf_size, recv_buf, recv_len); } } g_buf_size += recv_len; if((recv_len != TCP_MSS) || (recv_buf[recv_len - 1] == TCP_EOF_CH)) { if(g_buf_size < MAX_TCP_SHOW_SIZE){ lw_notice("Received: %s\n", g_buf); }else{ lw_notice("Received a string of length %d\n", g_buf_size); } tcpecho_raw_ack_size(tpcb, g_buf_size); free(g_buf); g_buf = NULL; g_buf_size = 0; } es->p = ptr->next; if(es->p != NULL) { /* new reference! */ pbuf_ref(es->p); } pbuf_free(ptr); tcp_recved(tpcb, recv_len); } static err_t tcpecho_raw_poll(void *arg, struct tcp_pcb *tpcb) { err_t ret_err; struct tcpecho_raw_state *es; es = (struct tcpecho_raw_state *)arg; if (es != NULL) { if (es->p != NULL) { /* there is a remaining pbuf (chain) */ tcpecho_raw_ack(tpcb, es); } else { /* no remaining pbuf (chain) */ if(es->state == ES_CLOSING) { tcpecho_raw_close(tpcb, es); } } ret_err = ERR_OK; } else { /* nothing to be done */ tcp_abort(tpcb); ret_err = ERR_ABRT; } return ret_err; } static err_t tcpecho_raw_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { struct tcpecho_raw_state *es; LWIP_UNUSED_ARG(len); es = (struct tcpecho_raw_state *)arg; es->retries = 0; es->p = NULL; // the sent reply from server never have successors if(es->state == ES_CLOSING) { tcpecho_raw_close(tpcb, es); } return ERR_OK; } static err_t tcpecho_raw_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { struct tcpecho_raw_state *es; err_t ret_err; LWIP_ASSERT("arg != NULL",arg != NULL); es = (struct tcpecho_raw_state *)arg; if (p == NULL) { /* remote host closed connection */ es->state = ES_CLOSING; if(es->p == NULL) { /* we're done sending, close it */ tcpecho_raw_close(tpcb, es); } else { /* we're not done yet */ tcpecho_raw_ack(tpcb, es); } ret_err = ERR_OK; } else if(err != ERR_OK) { /* cleanup, for unknown reason */ if (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; tcpecho_raw_ack(tpcb, es); ret_err = ERR_OK; } else if (es->state == ES_RECEIVED) { /* read some more data */ if(es->p == NULL) { es->p = p; tcpecho_raw_ack(tpcb, es); } else { struct pbuf *ptr; /* chain pbufs to the end of what we recv'ed previously */ ptr = es->p; pbuf_cat(ptr,p); } ret_err = ERR_OK; } else { /* unkown es->state, trash data */ tcp_recved(tpcb, p->tot_len); pbuf_free(p); ret_err = ERR_OK; } return ret_err; } static err_t tcpecho_raw_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { err_t ret_err; struct tcpecho_raw_state *es; LWIP_UNUSED_ARG(arg); if ((err != ERR_OK) || (newpcb == NULL)) { return ERR_VAL; } /* Unless this pcb should have NORMAL priority, set its priority now. When running out of pcbs, low priority pcbs can be aborted to create new pcbs of higher priority. */ tcp_setprio(newpcb, TCP_PRIO_MIN); es = (struct tcpecho_raw_state *)mem_malloc(sizeof(struct tcpecho_raw_state)); if (es != NULL) { es->state = ES_ACCEPTED; es->pcb = newpcb; es->retries = 0; es->p = NULL; /* pass newly allocated es to our callbacks */ tcp_arg(newpcb, es); tcp_recv(newpcb, tcpecho_raw_recv); tcp_err(newpcb, tcpecho_raw_error); tcp_poll(newpcb, tcpecho_raw_poll, 0); tcp_sent(newpcb, tcpecho_raw_sent); ret_err = ERR_OK; } else { ret_err = ERR_MEM; } return ret_err; } void tcpecho_raw_init(void) { tcpecho_raw_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); if (tcpecho_raw_pcb != NULL) { err_t err; err = tcp_bind(tcpecho_raw_pcb, IP_ANY_TYPE, LWIP_TEST_TCP_PORT); if (err == ERR_OK) { tcpecho_raw_pcb = tcp_listen(tcpecho_raw_pcb); tcp_accept(tcpecho_raw_pcb, tcpecho_raw_accept); } else { /* abort? output diagnostic? */ } } else { /* abort? output diagnostic? */ } } #endif /* LWIP_TCP && LWIP_CALLBACK_API */