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