/****************************************************************************** * FileName: tcp_srv_conn.c * TCP сервачек для ESP8266 * pvvx ver1.0 20/12/2014 * Перекинут на RTL871X pvvx 2017 ******************************************************************************/ #include "user_config.h" #include "autoconf.h" #include "FreeRTOS.h" #include "task.h" #include "diag.h" #include "lwip/tcp.h" #include "lwip/tcp_impl.h" #include "lwip/memp.h" #include "lwip/ip_addr.h" #include "flash_eep.h" #include "tcpsrv/tcp_srv_conn.h" #include "rtl8195a/rtl_libc.h" #include "esp_comp.h" #ifdef CONFIG_DEBUG_LOG #undef DEBUGSOO #define DEBUGSOO 2 // уровень вывода отладочной инфы по умолчанию = 2, =1 только error #else #undef DEBUGSOO #define DEBUGSOO 0 #endif // Lwip funcs - http://www.ecoscentric.com/ecospro/doc/html/ref/lwip.html #define TCP_SRV_BSSDATA_ATTR #define TCP_SRV_RODATA_ATTR #define TCP_SRV_CODE_ATTR #define ts_printf(...) rtl_printf(__VA_ARGS__) #define system_get_free_heap_size xPortGetFreeHeapSize extern void *pvPortZalloc(size_t xWantedSize); extern void vPortFree(void *pv); extern void *pvPortMalloc(size_t xWantedSize); #undef os_free #define os_free(p) vPortFree(p) #undef os_malloc #define os_malloc(p) pvPortMalloc(p) #undef os_zalloc #define os_zalloc(p) pvPortZalloc(p) #undef os_realloc #define os_realloc(p,s) pvPortReAlloc(p,s) /* extern __rtl_memsetw_v1_00(void *, uint32, size_t); void *pvPortZalloc( size_t xWantedSize ) { void *pvReturn = pvPortMalloc(xWantedSize); if(pvReturn != NULL) __rtl_memsetw_v1_00(pvReturn, 0, (xWantedSize + 3)>>2); return pvReturn; } */ TCP_SERV_CFG *phcfg TCP_SRV_BSSDATA_ATTR; // = NULL; // начальный указатель в памяти на структуры открытых сервачков #if DEBUGSOO > 0 const uint8 txt_tcpsrv_NULL_pointer[] TCP_SRV_RODATA_ATTR = "tcpsrv: NULL pointer!\n"; const uint8 txt_tcpsrv_already_initialized[] TCP_SRV_RODATA_ATTR = "tcpsrv: already initialized!\n"; const uint8 txt_tcpsrv_out_of_mem[] TCP_SRV_RODATA_ATTR = "tcpsrv: out of mem!\n"; #endif //#define mMIN(a, b) ((a -16) && (err < 1)) { return lwip_strerr(err); } else return srvContenErrX; } extern const char * const tcp_state_str[]; /****************************************************************************** * FunctionName : tspsrv_tcp_state_msg * Description : строка tcp_state по номеру * Parameters : LwIP tcp_state * Returns : указатель на строку *******************************************************************************/ const char * tspsrv_tcp_state_msg(enum tcp_state state) { if(state > TIME_WAIT && state < CLOSED) return srvContenErrX; return tcp_state_str[state]; } /****************************************************************************** * FunctionName : tspsrv_tcp_state_msg * Description : строка tcp_state по номеру * Parameters : LwIP tcp_state * Returns : указатель на строку *******************************************************************************/ static const char *msg_srvconn_state[] = { "NONE", "CLOSEWAIT", "LISTEN", "CONNECT", "CLOSED" }; const char * tspsrv_srvconn_state_msg(enum srvconn_state state) { if(state > SRVCONN_CLOSED && state < SRVCONN_NONE) return (const char *) srvContenErrX; return (const char *) msg_srvconn_state[state]; } //#endif /****************************************************************************** * FunctionName : tcpsrv_print_remote_info * Description : выводит remote_ip:remote_port [conn_count] ts_printf("srv x.x.x.x:x [n] ") * Parameters : TCP_SERV_CONN * ts_conn * Returns : none *******************************************************************************/ void TCP_SRV_CODE_ATTR tcpsrv_print_remote_info(TCP_SERV_CONN *ts_conn) { //#if DEBUGSOO > 0 uint16 port; if(ts_conn->pcb != NULL) port = ts_conn->pcb->local_port; else port = ts_conn->pcfg->port; ts_printf("srv[%u] %d.%d.%d.%d:%d [%d] ", port, ts_conn->remote_ip.b[0], ts_conn->remote_ip.b[1], ts_conn->remote_ip.b[2], ts_conn->remote_ip.b[3], ts_conn->remote_port, ts_conn->pcfg->conn_count); //#endif } /****************************************************************************** * Default call back functions ******************************************************************************/ //------------------------------------------------------------------------------ void TCP_SRV_CODE_ATTR tcpsrv_disconnect_calback_default(TCP_SERV_CONN *ts_conn) { ts_conn->pcb = NULL; #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("disconnect\n"); #endif } //------------------------------------------------------------------------------ err_t TCP_SRV_CODE_ATTR tcpsrv_listen_default(TCP_SERV_CONN *ts_conn) { #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("listen\n"); #endif return ERR_OK; } //------------------------------------------------------------------------------ err_t TCP_SRV_CODE_ATTR tcpsrv_connected_default(TCP_SERV_CONN *ts_conn) { #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("connected\n"); #endif return ERR_OK; } //------------------------------------------------------------------------------ err_t TCP_SRV_CODE_ATTR tcpsrv_sent_callback_default(TCP_SERV_CONN *ts_conn) { #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("sent_cb\n"); #endif return ERR_OK; } //------------------------------------------------------------------------------ err_t TCP_SRV_CODE_ATTR tcpsrv_received_data_default(TCP_SERV_CONN *ts_conn) { #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("received, buffer %d bytes\n", ts_conn->sizei); #endif return ERR_OK; } /****************************************************************************** * FunctionName : tcpsrv_check_max_tm_tcp_pcb * Description : Ограничение неактивных pcb в списках lwip до MAX_TIME_WAIT_PCB * Parameters : none * Returns : none *******************************************************************************/ static void TCP_SRV_CODE_ATTR tcpsrv_check_max_tm_tcp_pcb(void) { struct tcp_pcb *pcb; int i = 0; for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) i++; #if DEBUGSOO > 4 ts_printf("tcpsrv: check %d tm pcb\n", i); #endif while(i-- > MAX_TIME_WAIT_PCB) { struct tcp_pcb *inactive = NULL; for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { inactive = pcb; } #if DEBUGSOO > 3 ts_printf("tcpsrv: kill %d tm pcb\n", i); #endif if(inactive != NULL) { tcp_pcb_remove(&tcp_tw_pcbs, inactive); memp_free(MEMP_TCP_PCB, inactive); } } } /****************************************************************************** * FunctionName : find_tcp_pcb * Description : поиск pcb в списках lwip * Parameters : TCP_SERV_CONN * ts_conn * Returns : *pcb or NULL *******************************************************************************/ struct tcp_pcb * TCP_SRV_CODE_ATTR find_tcp_pcb(TCP_SERV_CONN * ts_conn) { struct tcp_pcb *pcb = ts_conn->pcb; if (pcb) { uint16 remote_port = ts_conn->remote_port; uint16 local_port = ts_conn->pcfg->port; uint32 ip = ts_conn->remote_ip.dw; if ((pcb->remote_port == remote_port) && (pcb->local_port == local_port) && (pcb->remote_ip.addr == ip)) return pcb; for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { if ((pcb->remote_port == remote_port) && (pcb->local_port == local_port) && (pcb->remote_ip.addr == ip)) return pcb; }; for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { if ((pcb->remote_port == remote_port) && (pcb->local_port == local_port) && (pcb->remote_ip.addr == ip)) return pcb; }; } return NULL; } /****************************************************************************** * FunctionName : tcpsrv_disconnect * Description : disconnect * Parameters : TCP_SERV_CONN * ts_conn * Returns : none *******************************************************************************/ void TCP_SRV_CODE_ATTR tcpsrv_disconnect(TCP_SERV_CONN * ts_conn) { if (ts_conn == NULL || ts_conn->state == SRVCONN_CLOSEWAIT) return; // уже закрывается ts_conn->pcb = find_tcp_pcb(ts_conn); // ещё жива данная pcb ? if (ts_conn->pcb != NULL) tcpsrv_server_close(ts_conn); } /****************************************************************************** * FunctionName : internal fun: tcpsrv_int_sent_data * Description : передача данных (не буферизированная! только передача в tcp Lwip-у) * вызывать только из call back с текущим pcb! * Parameters : TCP_SERV_CONN * ts_conn * uint8* psent - буфер с данными * uint16 length - кол-во передаваемых байт * Returns : tcp error ******************************************************************************/ err_t TCP_SRV_CODE_ATTR tcpsrv_int_sent_data(TCP_SERV_CONN * ts_conn, uint8 *psent, uint16 length) { err_t err = ERR_ARG; if(ts_conn == NULL) return err; ts_conn->pcb = find_tcp_pcb(ts_conn); // ещё жива данная pcb ? struct tcp_pcb *pcb = ts_conn->pcb; if(pcb == NULL || ts_conn->state == SRVCONN_CLOSEWAIT) return ERR_CONN; ts_conn->flag.busy_bufo = 1; // буфер bufo занят if(tcp_sndbuf(pcb) < length) { #if DEBUGSOO > 1 ts_printf("sent_data length (%u) > sndbuf (%u)!\n", length, tcp_sndbuf(pcb)); #endif return err; } if (length) { if(ts_conn->flag.nagle_disabled) tcp_nagle_disable(pcb); err = tcp_write(pcb, psent, length, 1); if (err == ERR_OK) { ts_conn->ptrtx = psent + length; ts_conn->cntro -= length; ts_conn->flag.wait_sent = 1; // ожидать завершения передачи (sent_cb) err = tcp_output(pcb); // передать что влезло } else { // ts_conn->state = SRVCONN_CLOSE; #if DEBUGSOO > 1 ts_printf("tcp_write(%p, %p, %u) = %d! pbuf = %u\n", pcb, psent, length, err, tcp_sndbuf(pcb)); #endif ts_conn->flag.wait_sent = 0; tcpsrv_server_close(ts_conn); }; } else { // создать вызов tcpsrv_server_sent() tcp_nagle_enable(pcb); err = tcp_output(pcb); // передать пустое } ts_conn->flag.busy_bufo = 0; // буфер bufo свободен return err; } /****************************************************************************** * FunctionName : tcpsrv_server_sent * Description : Data has been sent and acknowledged by the remote host. * This means that more data can be sent. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb for which data has been acknowledged * len -- The amount of bytes acknowledged * Returns : ERR_OK: try to send some data by calling tcp_output * ERR_ABRT: if you have called tcp_abort from within the function! ******************************************************************************/ static err_t TCP_SRV_CODE_ATTR tcpsrv_server_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { sint8 ret_err = ERR_OK; TCP_SERV_CONN * ts_conn = arg; if (ts_conn == NULL || pcb == NULL) return ERR_ARG; ts_conn->state = SRVCONN_CONNECT; ts_conn->recv_check = 0; ts_conn->flag.wait_sent = 0; // блок передан if ((ts_conn->flag.tx_null == 0) && (ts_conn->pcfg->func_sent_cb != NULL)) { ret_err = ts_conn->pcfg->func_sent_cb(ts_conn); } return ret_err; } /****************************************************************************** * FunctionName : recv_trim_bufi * Description : увеличение размера буфера приема на newadd * Parameters : ts_conn, newadd - требуемое дополнение размера буфера * Returns : err_t ******************************************************************************/ static err_t TCP_SRV_CODE_ATTR recv_trim_bufi(TCP_SERV_CONN * ts_conn, uint32 newadd) { uint32 len = 0; // while(ts_conn->flag.busy_bufi) vTaskDelay(1); ts_conn->flag.busy_bufi = 1; // идет обработка bufi if(newadd) { // требуется дополнение размера буфера if(ts_conn->pbufi != NULL) { // уже есть какой-то буфер if ((ts_conn->flag.rx_buf) && ts_conn->cntri < ts_conn->sizei) { // буфер не пуст len = ts_conn->sizei - ts_conn->cntri; // размер необработанных данных if(ts_conn->cntri >= newadd) { // кол-во обработанных байт (пустого места в буфере) больше чем дополнение os_memcpy(ts_conn->pbufi, &ts_conn->pbufi[ts_conn->cntri], len ); // скопировать необработанную часть в начало буфера ts_conn->sizei = len; if(ts_conn->cntri != newadd) { // кол-во обработанных байт (пустого места в буфере) равно требуемому дополнению // уменьшение занимаемого буфера в памяти len += newadd; // ts_conn->pbufi = (uint8 *)os_realloc(ts_conn->pbufi, len + 1); //mem_trim(ts_conn->pbufi, len); if(ts_conn->pbufi == NULL) { #if DEBUGSOO > 2 ts_printf("memtrim err %p[%d] ", ts_conn->pbufi, len + 1); #endif return ERR_MEM; } #if DEBUGSOO > 2 ts_printf("memi%p[%d] ", ts_conn->pbufi, len); #endif } ts_conn->pbufi[len] = '\0'; // вместо os_zalloc; ts_conn->cntri = 0; ts_conn->flag.busy_bufi = 0; // обработка bufi окончена return ERR_OK; } } else { // буфер пуст или не тот режим // удалить буфер os_free(ts_conn->pbufi); ts_conn->pbufi = NULL; } } // подготовка нового буфера, если его нет или не лезет дополнение uint8 * newbufi = (uint8 *) os_malloc(len + newadd + 1); if (newbufi == NULL) { #if DEBUGSOO > 2 ts_printf("memerr %p[%d] ", newbufi, len + newadd); #endif ts_conn->flag.busy_bufi = 0; // обработка bufi окончена return ERR_MEM; } #if DEBUGSOO > 2 ts_printf("memi%p[%d] ", newbufi, len + newadd); #endif newbufi[len + newadd] = '\0'; // вместо os_zalloc if(len) { os_memcpy(newbufi, &ts_conn->pbufi[ts_conn->cntri], len); os_free(ts_conn->pbufi); }; ts_conn->pbufi = newbufi; ts_conn->sizei = len; } else { // оптимизация буфера (уменьшение занимаемого размера) if((!ts_conn->flag.rx_buf) || ts_conn->cntri >= ts_conn->sizei) { // буфер обработан ts_conn->sizei = 0; if (ts_conn->pbufi != NULL) { os_free(ts_conn->pbufi); // освободить буфер. ts_conn->pbufi = NULL; }; } else if(ts_conn->cntri) { // есть обработанные данные // уменьшение занимаемого размера на обработанные данные len = ts_conn->sizei - ts_conn->cntri; os_memcpy(ts_conn->pbufi, &ts_conn->pbufi[ts_conn->cntri], len ); ts_conn->sizei = len; ts_conn->pbufi = (uint8 *)os_realloc(ts_conn->pbufi, len + 1); //mem_trim(ts_conn->pbufi, len); if(ts_conn->pbufi == NULL) { #if DEBUGSOO > 2 ts_printf("memtrim err %p[%d] ", ts_conn->pbufi, len + 1); #endif ts_conn->flag.busy_bufi = 0; // обработка bufi окончена return ERR_MEM; } ts_conn->pbufi[len] = '\0'; // вместо os_zalloc; } } ts_conn->cntri = 0; ts_conn->flag.busy_bufi = 0; // обработка bufi окончена return ERR_OK; } /****************************************************************************** * FunctionName : tcpsrv_server_recv * Description : Data has been received on this pcb. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb which received data * p -- The received data (or NULL when the connection has been closed!) * err -- An error code if there has been an error receiving * Returns : ERR_ABRT: if you have called tcp_abort from within the function! ******************************************************************************/ static err_t TCP_SRV_CODE_ATTR tcpsrv_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { // Sets the callback function that will be called when new data arrives on the connection associated with pcb. // The callback function will be passed a NULL pbuf to indicate that the remote host has closed the connection. TCP_SERV_CONN * ts_conn = arg; if (ts_conn == NULL || pcb == NULL) return ERR_ARG; // if(syscfg.cfg.b.hi_speed_enable) set_cpu_clk(); if (p == NULL || err != ERR_OK) { // the remote host has closed the connection. tcpsrv_server_close(ts_conn); return err; }; // если нет функции обработки или ожидаем закрытия соединения, то принимаем данные в трубу if ((ts_conn->flag.rx_null != 0) || (ts_conn->pcfg->func_recv == NULL) || (ts_conn->state == SRVCONN_CLOSEWAIT)) { // соединение закрыто? нет. tcp_recved(pcb, p->tot_len + ts_conn->unrecved_bytes); // сообщает стеку, что съели len и можно посылать ACK и принимать новые данные. ts_conn->unrecved_bytes = 0; #if DEBUGSOO > 3 ts_printf("rec_null %d of %d\n", ts_conn->cntri, p->tot_len); #endif pbuf_free(p); // данные выели return ERR_OK; }; ts_conn->state = SRVCONN_CONNECT; // был прием ts_conn->recv_check = 0; // счет времени до авто-закрытия с нуля if(p->tot_len) { err = recv_trim_bufi(ts_conn, p->tot_len); // увеличить буфер if(err != ERR_OK) return err; // добавление всего что отдал Lwip в буфер uint32 len = pbuf_copy_partial(p, &ts_conn->pbufi[ts_conn->sizei], p->tot_len, 0); ts_conn->sizei += len; pbuf_free(p); // все данные выели if(!ts_conn->flag.rx_buf) { tcp_recved(pcb, len); // сообщает стеку, что съели len и можно посылать ACK и принимать новые данные. } else ts_conn->unrecved_bytes += len; #if DEBUGSOO > 3 ts_printf("rec %d of %d :\n", p->tot_len, ts_conn->sizei); #endif err = ts_conn->pcfg->func_recv(ts_conn); err_t err2 = recv_trim_bufi(ts_conn, 0); // оптимизировать размер занимаемый буфером if(err2 != ERR_OK) return err2; } return err; } /****************************************************************************** * FunctionName : tcpsrv_unrecved_win * Description : Update the TCP window. * This can be used to throttle data reception (e.g. when received data is * programmed to flash and data is received faster than programmed). * Parameters : TCP_SERV_CONN * ts_conn * Returns : none * После включения throttle, будет принято до 5840 (MAX WIN) + 1460 (MSS) байт? ******************************************************************************/ void TCP_SRV_CODE_ATTR tcpsrv_unrecved_win(TCP_SERV_CONN *ts_conn) { if (ts_conn->unrecved_bytes) { // update the TCP window #if DEBUGSOO > 3 ts_printf("recved_bytes=%d\n", ts_conn->unrecved_bytes); #endif #if 1 if(ts_conn->pcb != NULL) tcp_recved(ts_conn->pcb, ts_conn->unrecved_bytes); #else struct tcp_pcb *pcb = find_tcp_pcb(ts_conn); // уже закрыто? if(pcb != NULL) tcp_recved(ts_conn->pcb, ts_conn->unrecved_bytes); #endif } ts_conn->unrecved_bytes = 0; } /****************************************************************************** * FunctionName : tcpsrv_disconnect * Description : disconnect with host * Parameters : ts_conn * Returns : none ******************************************************************************/ static void TCP_SRV_CODE_ATTR tcpsrv_disconnect_successful(TCP_SERV_CONN * ts_conn) { ts_conn->pcb = NULL; tcpsrv_list_delete(ts_conn); // remove the node from the server's connection list } /****************************************************************************** * FunctionName : tcpsrv_server_close * Description : The connection shall be actively closed. * Parameters : ts_conn * Returns : none ******************************************************************************/ static void TCP_SRV_CODE_ATTR tcpsrv_server_close(TCP_SERV_CONN * ts_conn) { struct tcp_pcb *pcb = ts_conn->pcb; if(pcb == NULL) { #if DEBUGSOO > 3 ts_printf("tcpsrv_server_close, state: %s, pcb = NULL!\n", tspsrv_srvconn_state_msg(ts_conn->state)); #endif return; } #if DEBUGSOO > 3 ts_printf("tcpsrv_server_close[%d], state: %s\n", pcb->local_port, tspsrv_srvconn_state_msg(ts_conn->state)); #endif if(ts_conn->state != SRVCONN_CLOSEWAIT && ts_conn->state != SRVCONN_CLOSED) { #if DEBUGSOO > 2 tcpsrv_print_remote_info(ts_conn); ts_printf("start close...\n"); #endif ts_conn->state = SRVCONN_CLOSEWAIT; ts_conn->recv_check = 0; ts_conn->flag.wait_sent = 0; // блок передан ts_conn->flag.rx_null = 1; // отключение вызова func_received_data() и прием в null ts_conn->flag.tx_null = 1; // отключение вызова func_sent_callback() и передача в null // отключение функций приема, передачи и poll tcp_recv(pcb, NULL); // отключение приема tcp_sent(pcb, NULL); // отключение передачи tcp_poll(pcb, NULL, 0); // отключение poll tcp_err(pcb, NULL); // if(ts_conn->unrecved_bytes) { tcp_recved(pcb, TCP_WND); ts_conn->unrecved_bytes = 0; } // освободить буфера if (ts_conn->pbufo != NULL) { os_free(ts_conn->pbufo); ts_conn->pbufo = NULL; } ts_conn->sizeo = 0; ts_conn->cntro = 0; if (ts_conn->pbufi != NULL) { os_free(ts_conn->pbufi); ts_conn->pbufi = NULL; } ts_conn->sizei = 0; ts_conn->cntri = 0; } if(ts_conn->state == SRVCONN_CLOSEWAIT || ts_conn->state == SRVCONN_CLOSED) { if (pcb->state == CLOSED || pcb->state == TIME_WAIT) { /*remove the node from the server's active connection list*/ #if DEBUGSOO > 3 ts_printf("close[%d]\n", pcb->local_port); #endif tcpsrv_disconnect_successful(ts_conn); } else { if (ts_conn->recv_check > 3) { // счет до принудительного закрытия 3 раза по TCP_SRV_CLOSE_WAIT #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("tcp_abandon!\n"); #endif tcp_poll(pcb, NULL, 0); //?/ tcp_err(pcb, NULL); tcp_abandon(pcb, 0); // ts_conn->pcb = NULL; // remove the node from the server's active connection list tcpsrv_disconnect_successful(ts_conn); } else if (tcp_close(pcb) != ERR_OK) { // послать закрытие соединения, closing failed #if DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); ts_printf("+ncls+[%d]\n", pcb->local_port); #endif // closing failed, try again later tcp_poll(pcb, tcpsrv_poll, 2*(TCP_SRV_CLOSE_WAIT)); } else { #if DEBUGSOO > 3 ts_printf("tcp_close[%d] ok.\n", pcb->local_port); #endif tcpsrv_disconnect_successful(ts_conn); } } } #if DEBUGSOO > 2 else { tcpsrv_print_remote_info(ts_conn); ts_printf("already close!\n"); } #endif } /****************************************************************************** * FunctionName : tcpsrv_poll (server and client) * Description : The poll function is called every 1 second. * This could be increased, but we don't want to waste resources for bad connections. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb for which data has been acknowledged * Returns : ERR_OK: try to send some data by calling tcp_output * ERR_ABRT: if you have called tcp_abort from within the function! *******************************************************************************/ static err_t TCP_SRV_CODE_ATTR tcpsrv_poll(void *arg, struct tcp_pcb *pcb) { TCP_SERV_CONN * ts_conn = arg; if (ts_conn == NULL) { #if DEBUGSOO > 3 ts_printf("poll, ts_conn = NULL! - abandon\n"); #endif tcp_poll(pcb, NULL, 0); tcp_err(pcb, NULL); tcp_abandon(pcb, 0); return ERR_ABRT; } #if DEBUGSOO > 3 tcpsrv_print_remote_info(ts_conn); ts_printf("poll: %d %s#%s, %d,%d, pcb:%p\n", ts_conn->recv_check, tspsrv_srvconn_state_msg(ts_conn->state), tspsrv_tcp_state_msg(pcb->state), ts_conn->pcfg->time_wait_rec, ts_conn->pcfg->time_wait_cls), pcb; #endif if (ts_conn->pcb != NULL && ts_conn->state != SRVCONN_CLOSEWAIT) { ts_conn->recv_check++; if (pcb->state == ESTABLISHED) { if ((ts_conn->state == SRVCONN_LISTEN && (ts_conn->pcfg->time_wait_rec) && ts_conn->recv_check > ts_conn->pcfg->time_wait_rec) || (ts_conn->state == SRVCONN_CONNECT && (ts_conn->pcfg->time_wait_cls) && ts_conn->recv_check > ts_conn->pcfg->time_wait_cls)) { tcpsrv_server_close(ts_conn); } } else tcpsrv_server_close(ts_conn); } else tcpsrv_server_close(ts_conn); #ifdef SRV_WDGREFESH_IN_POOL WDGRefresh(); #endif return ERR_OK; } /****************************************************************************** * FunctionName : tcpsrv_list_delete * Description : remove the node from the connection list * Parameters : ts_conn * Returns : none *******************************************************************************/ static void TCP_SRV_CODE_ATTR tcpsrv_list_delete(TCP_SERV_CONN * ts_conn) { // if (ts_conn == NULL) return; if(ts_conn->state != SRVCONN_CLOSED) { ts_conn->state = SRVCONN_CLOSED; // исключить повторное вхождение из запросов в func_discon_cb() if (ts_conn->pcfg->func_discon_cb != NULL) ts_conn->pcfg->func_discon_cb(ts_conn); if(phcfg == NULL || ts_conn->pcfg == NULL) return; // если в func_discon_cb() было вызвано tcpsrv_close_all() и т.д. } #if DEBUGSOO > 3 ts_printf("tcpsrv_list_delete (%p)\n", ts_conn->pcb); #endif TCP_SERV_CONN ** p = &ts_conn->pcfg->conn_links; TCP_SERV_CONN *tcpsrv_cmp = ts_conn->pcfg->conn_links; while (tcpsrv_cmp != NULL) { if (tcpsrv_cmp == ts_conn) { *p = ts_conn->next; ts_conn->next = NULL; ts_conn->pcfg->conn_count--; if (ts_conn->linkd != NULL) { os_free(ts_conn->linkd); ts_conn->linkd = NULL; } if (ts_conn->pbufo != NULL) { os_free(ts_conn->pbufo); ts_conn->pbufo = NULL; } if (ts_conn->pbufi != NULL) { os_free(ts_conn->pbufi); ts_conn->pbufi = NULL; } os_free(ts_conn); return; // break; } p = &tcpsrv_cmp->next; tcpsrv_cmp = tcpsrv_cmp->next; }; } //----------------------------------------------------------------------------- static void tspsrv_delete_pcb(TCP_SERV_CONN * ts_conn) { struct tcp_pcb *pcb = find_tcp_pcb(ts_conn); if(pcb) { tcp_arg(pcb, NULL); tcp_recv(pcb, NULL); tcp_err(pcb, NULL); tcp_poll(pcb, NULL, 0); tcp_sent(pcb, NULL); tcp_recved(pcb, TCP_WND); tcp_close(pcb); ts_conn->pcb = NULL; } tcpsrv_list_delete(ts_conn); } /****************************************************************************** * FunctionName : tcpsrv_error (server and client) * Description : The pcb had an error and is already deallocated. * The argument might still be valid (if != NULL). * Parameters : arg -- Additional argument to pass to the callback function * err -- Error code to indicate why the pcb has been closed * Returns : none *******************************************************************************/ static void TCP_SRV_CODE_ATTR tcpsrv_error(void *arg, err_t err) { TCP_SERV_CONN * ts_conn = arg; // struct tcp_pcb *pcb = NULL; if (ts_conn != NULL) { #if DEBUGSOO > 2 tcpsrv_print_remote_info(ts_conn); ts_printf("error %d (%s)\n", err, tspsrv_error_msg(err)); #elif DEBUGSOO > 1 tcpsrv_print_remote_info(ts_conn); #ifdef LWIP_DEBUG ts_printf("error %d (%s)\n", err, tspsrv_error_msg(err)); #else ts_printf("error %d\n", err); #endif #endif if (ts_conn->state != SRVCONN_CLOSEWAIT) { if(ts_conn->pcb != NULL) { // && ts_conn->state != SRVCONN_CLOSED) { // && (ts_conn->state != SRVCONN_CONNECT || ts_conn->state == SRVCONN_LISTEN)) { #if DEBUGSOO > 1 ts_printf("eclose[%d]\n", ts_conn->pcfg->port); #endif tcpsrv_list_delete(ts_conn); // remove the node from the server's connection list }; }; } } /****************************************************************************** * FunctionName : tcpsrv_tcp_accept * Description : A new incoming connection has been accepted. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb which is accepted * err -- An unused error code, always ERR_OK currently * Returns : acception result *******************************************************************************/ static err_t TCP_SRV_CODE_ATTR tcpsrv_server_accept(void *arg, struct tcp_pcb *pcb, err_t err) { struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*) arg; TCP_SERV_CFG *p = tcpsrv_server_port2pcfg(pcb->local_port); TCP_SERV_CONN * ts_conn; if (p == NULL) return ERR_ARG; if (system_get_free_heap_size() < p->min_heap) { #if DEBUGSOO > 1 ts_printf("srv[%u] new listen - low heap size!\n", p->port); #endif return ERR_MEM; } if (p->conn_count >= p->max_conn) { if(p->flag.srv_reopen) { #if DEBUGSOO > 1 ts_printf("srv[%u] reconnect!\n", p->port); #endif if (p->conn_links != NULL) { tspsrv_delete_pcb(p->conn_links); }; } else { #if DEBUGSOO > 1 ts_printf("srv[%u] new listen - max connection!\n", p->port); #endif return ERR_CONN; } } ts_conn = (TCP_SERV_CONN *) os_zalloc(sizeof(TCP_SERV_CONN)); if (ts_conn == NULL) { #if DEBUGSOO > 1 ts_printf("srv[%u] new listen - out of mem!\n", ts_conn->pcfg->port); #endif return ERR_MEM; } ts_conn->pcfg = p; // перенести флаги по умолчанию на данное соединение ts_conn->flag = p->flag; tcp_accepted(lpcb); // Decrease the listen backlog counter // tcp_setprio(pcb, TCP_PRIO_MIN); // Set priority ? // init/copy data ts_conn ts_conn->pcb = pcb; ts_conn->remote_port = pcb->remote_port; ts_conn->remote_ip.dw = pcb->remote_ip.addr; // *(uint16 *) &ts_conn->flag = 0; //zalloc // ts_conn->recv_check = 0; //zalloc // ts_conn->linkd = NULL; //zalloc // Insert new ts_conn ts_conn->next = ts_conn->pcfg->conn_links; ts_conn->pcfg->conn_links = ts_conn; ts_conn->pcfg->conn_count++; ts_conn->state = SRVCONN_LISTEN; // Tell TCP that this is the structure we wish to be passed for our callbacks. tcp_arg(pcb, ts_conn); // Set up the various callback functions tcp_err(pcb, tcpsrv_error); tcp_sent(pcb, tcpsrv_server_sent); tcp_recv(pcb, tcpsrv_server_recv); tcp_poll(pcb, tcpsrv_poll, 2); /* every 1/2 seconds */ #if MEMP_MEM_MALLOC // tcp_alloc() уже управляет убийством TIME_WAIT если MEMP_MEM_MALLOC == 0 if(ts_conn->flag.pcb_time_wait_free) tcpsrv_check_max_tm_tcp_pcb(); // http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html #endif if (p->func_listen != NULL) return p->func_listen(ts_conn); return ERR_OK; } /****************************************************************************** * FunctionName : tcpsrv_server_port2pcfg * Description : поиск конфига servera по порту * Parameters : номер порта * Returns : указатель на TCP_SERV_CFG или NULL *******************************************************************************/ TCP_SERV_CFG * TCP_SRV_CODE_ATTR tcpsrv_server_port2pcfg(uint16 portn) { TCP_SERV_CFG * p; for (p = phcfg; p != NULL; p = p->next) if (p->port == portn) return p; return NULL; } /****************************************************************************** tcpsrv_init server. *******************************************************************************/ TCP_SERV_CFG * TCP_SRV_CODE_ATTR tcpsrv_init(uint16 portn) { // if (portn == 0) portn = 80; if (portn == 0) return NULL; TCP_SERV_CFG * p; for (p = phcfg; p != NULL; p = p->next) { if (p->port == portn) { #if DEBUGSOO > 0 ts_printf(txt_tcpsrv_already_initialized); #endif return NULL; } } p = (TCP_SERV_CFG *) os_zalloc(sizeof(TCP_SERV_CFG)); if (p == NULL) { #if DEBUGSOO > 0 ts_printf(txt_tcpsrv_out_of_mem); #endif return NULL; } p->port = portn; p->conn_count = 0; p->min_heap = TCP_SRV_MIN_HEAP_SIZE; p->time_wait_rec = TCP_SRV_RECV_WAIT; p->time_wait_cls = TCP_SRV_END_WAIT; // p->phcfg->conn_links = NULL; // zalloc // p->pcb = NULL; // zalloc // p->lnk = NULL; // zalloc p->max_conn = TCP_SRV_MAX_CONNECTIONS; p->func_listen = tcpsrv_listen_default; p->func_discon_cb = tcpsrv_disconnect_calback_default; p->func_sent_cb = tcpsrv_sent_callback_default; p->func_recv = tcpsrv_received_data_default; return p; } /****************************************************************************** tcpsrv_start *******************************************************************************/ err_t TCP_SRV_CODE_ATTR tcpsrv_start(TCP_SERV_CFG *p) { err_t err = ERR_OK; if (p == NULL) { #if DEBUGSOO > 0 ts_printf(txt_tcpsrv_NULL_pointer); #endif return ERR_ARG; } if (p->pcb != NULL) { #if DEBUGSOO > 0 ts_printf(txt_tcpsrv_already_initialized); #endif return ERR_USE; } p->pcb = tcp_new(); if (p->pcb != NULL) { tcp_setprio(p->pcb, TCP_SRV_PRIO); err = tcp_bind(p->pcb, IP_ADDR_ANY, p->port); // Binds pcb to a local IP address and port number. if (err == ERR_OK) { // If another connection is bound to the same port, the function will return ERR_USE, otherwise ERR_OK is returned. p->pcb = tcp_listen(p->pcb); // Commands pcb to start listening for incoming connections. // When an incoming connection is accepted, the function specified with the tcp_accept() function // will be called. pcb must have been bound to a local port with the tcp_bind() function. // The tcp_listen() function returns a new connection identifier, and the one passed as an // argument to the function will be deallocated. The reason for this behavior is that less // memory is needed for a connection that is listening, so tcp_listen() will reclaim the memory // needed for the original connection and allocate a new smaller memory block for the listening connection. // tcp_listen() may return NULL if no memory was available for the listening connection. // If so, the memory associated with pcb will not be deallocated. if (p->pcb != NULL) { tcp_arg(p->pcb, p->pcb); // insert new tcpsrv_config p->next = phcfg; phcfg = p; // initialize callback arg and accept callback tcp_accept(p->pcb, tcpsrv_server_accept); return err; } } tcp_abandon(p->pcb, 0); p->pcb = NULL; } else err = ERR_MEM; #if DEBUGSOO > 0 ts_printf("tcpsrv: not new tcp!\n"); #endif return err; } /****************************************************************************** tcpsrv_close *******************************************************************************/ err_t TCP_SRV_CODE_ATTR tcpsrv_close(TCP_SERV_CFG *p) { if (p == NULL) { #if DEBUGSOO > 0 ts_printf(txt_tcpsrv_NULL_pointer); #endif return ERR_ARG; }; TCP_SERV_CFG **pwr = &phcfg; TCP_SERV_CFG *pcmp = phcfg; while (pcmp != NULL) { if (pcmp == p) { *pwr = p->next; while (p->conn_links != NULL) { tspsrv_delete_pcb(p->conn_links); }; if(p->pcb != NULL) tcp_close(p->pcb); os_free(p); p = NULL; return ERR_OK; // break; } pwr = &pcmp->next; pcmp = pcmp->next; }; #if DEBUGSOO > 2 ts_printf("tcpsrv: srv_cfg not find!\n"); #endif return ERR_CONN; } /****************************************************************************** tcpsrv_close_port *******************************************************************************/ err_t TCP_SRV_CODE_ATTR tcpsrv_close_port(uint16 portn) { if(portn) return tcpsrv_close(tcpsrv_server_port2pcfg(portn)); else return ERR_ARG; } /****************************************************************************** tcpsrv_close_all_tcp_pcb *******************************************************************************/ int TCP_SRV_CODE_ATTR tcpsrv_close_all_tcp_pcb(void) { struct tcp_pcb *pcb; int ret = 0; for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { #if DEBUGSOO > 3 ts_printf("tcpsrv: abort %p pcb\n", pcb); #endif tcp_abort(pcb); ret = 1; } return ret; } /****************************************************************************** tcpsrv_delete_all_tm_tcp_pcb *******************************************************************************/ void TCP_SRV_CODE_ATTR tcpsrv_delete_all_tm_tcp_pcb(void) { struct tcp_pcb *pcb; for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { #if DEBUGSOO > 3 ts_printf("tcpsrv: del tm %p pcb\n", pcb); #endif tcp_pcb_remove(&tcp_tw_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } } /****************************************************************************** tcpsrv_close_all *******************************************************************************/ err_t TCP_SRV_CODE_ATTR tcpsrv_close_all(void) { err_t err = ERR_OK; while(phcfg != NULL && err == ERR_OK) err = tcpsrv_close(phcfg); if(tcpsrv_close_all_tcp_pcb()) vTaskDelay(50); tcpsrv_delete_all_tm_tcp_pcb(); return err; }