RTL00_WEB_VS/RTLGDB/Project/tcpsrv/tcp_srv_conn.c
ADElectronics 764b020238 update
2017-11-28 22:31:40 +03:00

1013 lines
39 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************
* 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<b)?a:b)
// пред.описание...
static void tcpsrv_list_delete(TCP_SERV_CONN * ts_conn) TCP_SRV_CODE_ATTR;
static void tcpsrv_disconnect_successful(TCP_SERV_CONN * ts_conn) TCP_SRV_CODE_ATTR;
static void tcpsrv_server_close(TCP_SERV_CONN * ts_conn) TCP_SRV_CODE_ATTR;
static err_t tcpsrv_poll(void *arg, struct tcp_pcb *pcb) TCP_SRV_CODE_ATTR;
static void tcpsrv_error(void *arg, err_t err) TCP_SRV_CODE_ATTR;
//err_t tcpsrv_connected(void *arg, struct tcp_pcb *tpcb, err_t err) TCP_SRV_CODE_ATTR;
//err_t tcpsrv_client_connect(TCP_SERV_CONN * ts_conn) TCP_SRV_CODE_ATTR;
//void tcpsrv_client_reconnect(TCP_SERV_CONN * ts_conn) TCP_SRV_CODE_ATTR;
#ifndef LWIP_DEBUG
#include "lwip/init.h"
#if (LWIP_VERSION != 0x010401ff)
#error "Only LwIP version 1.4.1 !"
#endif
static const char *err_strerr[] = {
"Ok", /* ERR_OK 0 */
"Out of memory error", /* ERR_MEM -1 */
"Buffer error", /* ERR_BUF -2 */
"Timeout", /* ERR_TIMEOUT -3 */
"Routing problem", /* ERR_RTE -4 */
"Operation in progress", /* ERR_INPROGRESS -5 */
"Illegal value", /* ERR_VAL -6 */
"Operation would block", /* ERR_WOULDBLOCK -7 */
"Address in use", /* ERR_USE -8 */
"Already connected", /* ERR_ISCONN -9 */
"Connection aborted", /* ERR_ABRT -10 */
"Connection reset", /* ERR_RST -11 */
"Connection closed", /* ERR_CLSD -12 */
"Not connected", /* ERR_CONN -13 */
"Illegal argument", /* ERR_ARG -14 */
"Low-level netif error", /* ERR_IF -15 */
};
#endif
static const char srvContenErrX[] = "?";
/******************************************************************************
* FunctionName : tspsrv_error_msg
* Description : строка ошибки по номеру
* Parameters : LwIP err
* Returns : указатель на строку ошибки
*******************************************************************************/
const char * tspsrv_error_msg(err_t err)
{
if((err > -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;
}