Browse Source
handle_close: tcp_abort→tcp_close + полное обнуление коллбэков (graceful FIN вместо RST) handle_error: отделён от handle_close, свой cleanup с tcp_abort (RST — нештатная ситуация) poll_cb/destroy: полное обнуление коллбэков перед tcp_abort (tcp_arg/recv/sent/err/poll) handle_fin: исправлен лог (shutdown write, не read) tx_waiter_cb: проверка pc->pcb перед tcp_recvedetcp-inflight-fix
2 changed files with 148 additions and 7 deletions
@ -0,0 +1,106 @@ |
|||||||
|
Как узнать, что TCP полностью завершился? |
||||||
|
|
||||||
|
В raw API прямого callback'а вида: |
||||||
|
|
||||||
|
on_connection_closed() |
||||||
|
|
||||||
|
нет. |
||||||
|
|
||||||
|
Обычно используют собственное состояние: |
||||||
|
|
||||||
|
struct conn { |
||||||
|
bool closing; |
||||||
|
}; |
||||||
|
|
||||||
|
и после успешного: |
||||||
|
|
||||||
|
tcp_close(pcb); |
||||||
|
conn->closing = true; |
||||||
|
|
||||||
|
считают соединение закрытым для приложения. |
||||||
|
|
||||||
|
Сам TCP завершится позже внутри lwIP. |
||||||
|
|
||||||
|
А вызовется ли err callback? |
||||||
|
|
||||||
|
Обычно нет. |
||||||
|
|
||||||
|
tcp_err() |
||||||
|
|
||||||
|
вызывается при ненормальном завершении: |
||||||
|
|
||||||
|
RST |
||||||
|
abort |
||||||
|
потеря PCB |
||||||
|
сетевые ошибки |
||||||
|
|
||||||
|
но не при штатном FIN/ACK закрытии. |
||||||
|
|
||||||
|
Нужно ли освобождать память после tcp_close()? |
||||||
|
|
||||||
|
Сам pcb освобождать нельзя: |
||||||
|
|
||||||
|
tcp_close(pcb); |
||||||
|
|
||||||
|
/* НЕЛЬЗЯ */ |
||||||
|
mem_free(pcb); |
||||||
|
|
||||||
|
lwIP сделает это самостоятельно. |
||||||
|
|
||||||
|
Но ваши данные (arg) нужно освободить самостоятельно. |
||||||
|
|
||||||
|
Обычно так: |
||||||
|
|
||||||
|
struct my_conn *c = arg; |
||||||
|
|
||||||
|
tcp_arg(pcb, NULL); |
||||||
|
|
||||||
|
free(c); |
||||||
|
|
||||||
|
tcp_close(pcb); |
||||||
|
|
||||||
|
или |
||||||
|
|
||||||
|
free(c); |
||||||
|
tcp_close(pcb); |
||||||
|
pcb = NULL; |
||||||
|
|
||||||
|
если после этого код больше не обращается к c. |
||||||
|
|
||||||
|
Безопасная практика |
||||||
|
|
||||||
|
После успешного tcp_close(): |
||||||
|
|
||||||
|
if (tcp_close(pcb) == ERR_OK) { |
||||||
|
pcb = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
и считать объект недействительным. |
||||||
|
|
||||||
|
Потому что lwIP может уничтожить его в любой момент по мере завершения TCP state machine. |
||||||
|
|
||||||
|
Типичный шаблон |
||||||
|
static void close_conn(struct my_conn *c) |
||||||
|
{ |
||||||
|
err_t err; |
||||||
|
|
||||||
|
err = tcp_close(c->pcb); |
||||||
|
|
||||||
|
if (err == ERR_OK) { |
||||||
|
c->pcb = NULL; |
||||||
|
free(c); |
||||||
|
} else { |
||||||
|
/* ERR_MEM -> попробуем позже */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Но если free(c) делается сразу, нужно быть уверенным, что никакие callback'и lwIP больше не используют arg. Часто перед закрытием сначала снимают callback'и: |
||||||
|
|
||||||
|
tcp_arg(pcb, NULL); |
||||||
|
tcp_recv(pcb, NULL); |
||||||
|
tcp_sent(pcb, NULL); |
||||||
|
tcp_err(pcb, NULL); |
||||||
|
|
||||||
|
а затем вызывают tcp_close(). |
||||||
|
|
||||||
|
Это особенно важно в сложных приложениях с собственными структурами соединений. |
||||||
Loading…
Reference in new issue