mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-11-04 23:29:25 +00:00
netconn/socket api: fixed bug #44225 "closing TCP socket should time out eventually", implemented task #6930 "Implement SO_LINGER": closing TCP sockets times out after 20 seconds or after the configured SND_TIMEOUT or depending on the linger settings; fixed that netconn_close/netconn_delete still used message passing for LWIP_TCPIP_CORE_LOCKING==1
This commit is contained in:
parent
ee833ea594
commit
6c3f6cfd89
10
CHANGELOG
10
CHANGELOG
@ -156,6 +156,16 @@ HISTORY
|
|||||||
|
|
||||||
++ Bugfixes:
|
++ Bugfixes:
|
||||||
|
|
||||||
|
2014-02-10: Simon Goldschmidt
|
||||||
|
* netconn API: fixed that netconn_close/netconn_delete still used message passing
|
||||||
|
for LWIP_TCPIP_CORE_LOCKING==1
|
||||||
|
|
||||||
|
2014-02-10: Simon Goldschmidt
|
||||||
|
* netconn/socket api: fixed bug #44225 "closing TCP socket should time out
|
||||||
|
eventually", implemented task #6930 "Implement SO_LINGER": closing TCP sockets
|
||||||
|
times out after 20 seconds or after the configured SND_TIMEOUT or depending
|
||||||
|
on the linger settings.
|
||||||
|
|
||||||
2014-01-27: Simon Goldschmidt
|
2014-01-27: Simon Goldschmidt
|
||||||
* api_msg.c: fixed that SHUT_RD followed by SHUT_WR was different to SHUT_RDWR,
|
* api_msg.c: fixed that SHUT_RD followed by SHUT_WR was different to SHUT_RDWR,
|
||||||
fixed return value of lwip_netconn_do_close on unconnected netconns
|
fixed return value of lwip_netconn_do_close on unconnected netconns
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "lwip/raw.h"
|
#include "lwip/raw.h"
|
||||||
#include "lwip/udp.h"
|
#include "lwip/udp.h"
|
||||||
#include "lwip/tcp.h"
|
#include "lwip/tcp.h"
|
||||||
|
#include "lwip/tcp_impl.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -122,9 +123,16 @@ netconn_delete(struct netconn *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
API_MSG_VAR_ALLOC(msg);
|
API_MSG_VAR_ALLOC(msg);
|
||||||
API_MSG_VAR_REF(msg).function = lwip_netconn_do_delconn;
|
|
||||||
API_MSG_VAR_REF(msg).msg.conn = conn;
|
API_MSG_VAR_REF(msg).msg.conn = conn;
|
||||||
err = tcpip_apimsg(&API_MSG_VAR_REF(msg));
|
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
|
||||||
|
/* get the time we started, which is later compared to
|
||||||
|
sys_now() + conn->send_timeout */
|
||||||
|
API_MSG_VAR_REF(msg).msg.msg.sd.time_started = sys_now();
|
||||||
|
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
API_MSG_VAR_REF(msg).msg.msg.sd.polls_left =
|
||||||
|
((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
|
||||||
|
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_delconn, err);
|
||||||
API_MSG_VAR_FREE(msg);
|
API_MSG_VAR_FREE(msg);
|
||||||
|
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
@ -728,13 +736,18 @@ netconn_close_shutdown(struct netconn *conn, u8_t how)
|
|||||||
LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
|
LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
|
||||||
|
|
||||||
API_MSG_VAR_ALLOC(msg);
|
API_MSG_VAR_ALLOC(msg);
|
||||||
API_MSG_VAR_REF(msg).function = lwip_netconn_do_close;
|
|
||||||
API_MSG_VAR_REF(msg).msg.conn = conn;
|
API_MSG_VAR_REF(msg).msg.conn = conn;
|
||||||
/* shutting down both ends is the same as closing */
|
/* shutting down both ends is the same as closing */
|
||||||
API_MSG_VAR_REF(msg).msg.msg.sd.shut = how;
|
API_MSG_VAR_REF(msg).msg.msg.sd.shut = how;
|
||||||
/* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close,
|
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
|
||||||
don't use TCPIP_APIMSG here */
|
/* get the time we started, which is later compared to
|
||||||
err = tcpip_apimsg(&API_MSG_VAR_REF(msg));
|
sys_now() + conn->send_timeout */
|
||||||
|
API_MSG_VAR_REF(msg).msg.msg.sd.time_started = sys_now();
|
||||||
|
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
API_MSG_VAR_REF(msg).msg.msg.sd.polls_left =
|
||||||
|
((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
|
||||||
|
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_close, err);
|
||||||
API_MSG_VAR_FREE(msg);
|
API_MSG_VAR_FREE(msg);
|
||||||
|
|
||||||
NETCONN_SET_SAFE_ERR(conn, err);
|
NETCONN_SET_SAFE_ERR(conn, err);
|
||||||
|
@ -55,6 +55,9 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/* netconns are polled once per second (e.g. continue write on memory error) */
|
||||||
|
#define NETCONN_TCP_POLL_INTERVAL 2
|
||||||
|
|
||||||
#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \
|
#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \
|
||||||
(conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
|
(conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
|
||||||
} else { \
|
} else { \
|
||||||
@ -64,7 +67,7 @@
|
|||||||
/* forward declarations */
|
/* forward declarations */
|
||||||
#if LWIP_TCP
|
#if LWIP_TCP
|
||||||
static err_t lwip_netconn_do_writemore(struct netconn *conn);
|
static err_t lwip_netconn_do_writemore(struct netconn *conn);
|
||||||
static void lwip_netconn_do_close_internal(struct netconn *conn);
|
static err_t lwip_netconn_do_close_internal(struct netconn *conn);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LWIP_RAW
|
#if LWIP_RAW
|
||||||
@ -285,6 +288,11 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
|
|||||||
if (conn->state == NETCONN_WRITE) {
|
if (conn->state == NETCONN_WRITE) {
|
||||||
lwip_netconn_do_writemore(conn);
|
lwip_netconn_do_writemore(conn);
|
||||||
} else if (conn->state == NETCONN_CLOSE) {
|
} else if (conn->state == NETCONN_CLOSE) {
|
||||||
|
#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
|
||||||
|
if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
|
||||||
|
conn->current_msg->msg.sd.polls_left--;
|
||||||
|
}
|
||||||
|
#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
|
||||||
lwip_netconn_do_close_internal(conn);
|
lwip_netconn_do_close_internal(conn);
|
||||||
}
|
}
|
||||||
/* @todo: implement connect timeout here? */
|
/* @todo: implement connect timeout here? */
|
||||||
@ -423,7 +431,7 @@ setup_tcp(struct netconn *conn)
|
|||||||
tcp_arg(pcb, conn);
|
tcp_arg(pcb, conn);
|
||||||
tcp_recv(pcb, recv_tcp);
|
tcp_recv(pcb, recv_tcp);
|
||||||
tcp_sent(pcb, sent_tcp);
|
tcp_sent(pcb, sent_tcp);
|
||||||
tcp_poll(pcb, poll_tcp, 4);
|
tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
|
||||||
tcp_err(pcb, err_tcp);
|
tcp_err(pcb, err_tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +474,7 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
|
|||||||
tcp_arg(pcb, NULL);
|
tcp_arg(pcb, NULL);
|
||||||
tcp_recv(pcb, NULL);
|
tcp_recv(pcb, NULL);
|
||||||
tcp_sent(pcb, NULL);
|
tcp_sent(pcb, NULL);
|
||||||
tcp_poll(pcb, NULL, 4);
|
tcp_poll(pcb, NULL, 0);
|
||||||
tcp_err(pcb, NULL);
|
tcp_err(pcb, NULL);
|
||||||
/* remove reference from to the pcb from this netconn */
|
/* remove reference from to the pcb from this netconn */
|
||||||
newconn->pcb.tcp = NULL;
|
newconn->pcb.tcp = NULL;
|
||||||
@ -647,6 +655,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
|
|||||||
conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
|
conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
|
||||||
conn->recv_avail = 0;
|
conn->recv_avail = 0;
|
||||||
#endif /* LWIP_SO_RCVBUF */
|
#endif /* LWIP_SO_RCVBUF */
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
conn->linger = -1;
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
conn->flags = 0;
|
conn->flags = 0;
|
||||||
return conn;
|
return conn;
|
||||||
free_and_return:
|
free_and_return:
|
||||||
@ -752,12 +763,16 @@ netconn_drain(struct netconn *conn)
|
|||||||
*
|
*
|
||||||
* @param conn the TCP netconn to close
|
* @param conn the TCP netconn to close
|
||||||
*/
|
*/
|
||||||
static void
|
static err_t
|
||||||
lwip_netconn_do_close_internal(struct netconn *conn)
|
lwip_netconn_do_close_internal(struct netconn *conn)
|
||||||
{
|
{
|
||||||
err_t err;
|
err_t err;
|
||||||
u8_t shut, shut_rx, shut_tx, close;
|
u8_t shut, shut_rx, shut_tx, close;
|
||||||
struct tcp_pcb* tpcb = conn->pcb.tcp;
|
u8_t close_finished = 0;
|
||||||
|
struct tcp_pcb* tpcb;
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
u8_t linger_wait_required = 0;
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
|
|
||||||
LWIP_ASSERT("invalid conn", (conn != NULL));
|
LWIP_ASSERT("invalid conn", (conn != NULL));
|
||||||
LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
|
LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
|
||||||
@ -765,6 +780,7 @@ lwip_netconn_do_close_internal(struct netconn *conn)
|
|||||||
LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
|
LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
|
||||||
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
|
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
|
||||||
|
|
||||||
|
tpcb = conn->pcb.tcp;
|
||||||
shut = conn->current_msg->msg.sd.shut;
|
shut = conn->current_msg->msg.sd.shut;
|
||||||
shut_rx = shut & NETCONN_SHUT_RD;
|
shut_rx = shut & NETCONN_SHUT_RD;
|
||||||
shut_tx = shut & NETCONN_SHUT_WR;
|
shut_tx = shut & NETCONN_SHUT_WR;
|
||||||
@ -799,49 +815,136 @@ lwip_netconn_do_close_internal(struct netconn *conn)
|
|||||||
tcp_sent(tpcb, NULL);
|
tcp_sent(tpcb, NULL);
|
||||||
}
|
}
|
||||||
if (close) {
|
if (close) {
|
||||||
tcp_poll(tpcb, NULL, 4);
|
tcp_poll(tpcb, NULL, 0);
|
||||||
tcp_err(tpcb, NULL);
|
tcp_err(tpcb, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Try to close the connection */
|
/* Try to close the connection */
|
||||||
if (close) {
|
if (close) {
|
||||||
err = tcp_close(tpcb);
|
#if LWIP_SO_LINGER
|
||||||
|
/* check linger possibilites before calling tcp_close */
|
||||||
|
err = ERR_OK;
|
||||||
|
/* linger enabled/required at all? (i.e. is there untransmitted data left?) */
|
||||||
|
if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
|
||||||
|
if ((conn->linger == 0)) {
|
||||||
|
/* data left but linger prevents waiting */
|
||||||
|
tcp_abort(tpcb);
|
||||||
|
tpcb = NULL;
|
||||||
|
} else if (conn->linger > 0) {
|
||||||
|
/* data left and linger says we should wait */
|
||||||
|
if (netconn_is_nonblocking(conn)) {
|
||||||
|
/* data left on a nonblocking netconn -> cannot linger */
|
||||||
|
err = ERR_WOULDBLOCK;
|
||||||
|
} else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
|
||||||
|
(conn->linger * 1000)) {
|
||||||
|
/* data left but linger timeout has expired (this happens on further
|
||||||
|
calls to this function through poll_tcp */
|
||||||
|
tcp_abort(tpcb);
|
||||||
|
tpcb = NULL;
|
||||||
|
} else {
|
||||||
|
/* data left -> need to wait for ACK after successful close */
|
||||||
|
linger_wait_required = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((err == ERR_OK) && (tpcb != NULL))
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
|
{
|
||||||
|
err = tcp_close(tpcb);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = tcp_shutdown(tpcb, shut_rx, shut_tx);
|
err = tcp_shutdown(tpcb, shut_rx, shut_tx);
|
||||||
}
|
}
|
||||||
if (err == ERR_OK) {
|
if (err == ERR_OK) {
|
||||||
/* Closing succeeded */
|
close_finished = 1;
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
if (linger_wait_required) {
|
||||||
|
/* wait for ACK of all unsent/unacked data by just getting called again */
|
||||||
|
close_finished = 0;
|
||||||
|
}
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
|
} else {
|
||||||
|
if(err == ERR_MEM) {
|
||||||
|
/* Closing failed because of memory shortage */
|
||||||
|
if (netconn_is_nonblocking(conn)) {
|
||||||
|
/* Nonblocking close failed */
|
||||||
|
close_finished = 1;
|
||||||
|
err = ERR_WOULDBLOCK;
|
||||||
|
} else {
|
||||||
|
/* Blocking close, check the timeout */
|
||||||
|
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
|
||||||
|
s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
|
||||||
|
/* this is kind of an lwip addition to the standard sockets: we wait
|
||||||
|
for some time when failing to allocate a segment for the FIN */
|
||||||
|
#if LWIP_SO_SNDTIMEO
|
||||||
|
if (conn->send_timeout >= 0) {
|
||||||
|
close_timeout = conn->send_timeout;
|
||||||
|
}
|
||||||
|
#endif /* LWIP_SO_SNDTIMEO */
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
if (conn->linger >= 0) {
|
||||||
|
/* use linger timeout (seconds) */
|
||||||
|
close_timeout = conn->linger * 1000U;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
|
||||||
|
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
if (conn->current_msg->msg.sd.polls_left == 0) {
|
||||||
|
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
close_finished = 1;
|
||||||
|
if (close) {
|
||||||
|
/* in this case, we want to RST the connection */
|
||||||
|
tcp_abort(tpcb);
|
||||||
|
err = ERR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Closing failed for a non-memory error: give up */
|
||||||
|
close_finished = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (close_finished) {
|
||||||
|
/* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
|
||||||
sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
|
sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
|
||||||
conn->current_msg->err = ERR_OK;
|
conn->current_msg->err = err;
|
||||||
conn->current_msg = NULL;
|
conn->current_msg = NULL;
|
||||||
conn->state = NETCONN_NONE;
|
conn->state = NETCONN_NONE;
|
||||||
if (close) {
|
if (err == ERR_OK) {
|
||||||
/* Set back some callback pointers as conn is going away */
|
if (close) {
|
||||||
conn->pcb.tcp = NULL;
|
/* Set back some callback pointers as conn is going away */
|
||||||
/* Trigger select() in socket layer. Make sure everybody notices activity
|
conn->pcb.tcp = NULL;
|
||||||
on the connection, error first! */
|
/* Trigger select() in socket layer. Make sure everybody notices activity
|
||||||
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
|
on the connection, error first! */
|
||||||
}
|
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
|
||||||
if (shut_rx) {
|
}
|
||||||
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
|
if (shut_rx) {
|
||||||
}
|
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
|
||||||
if (shut_tx) {
|
}
|
||||||
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
|
if (shut_tx) {
|
||||||
|
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* wake up the application task */
|
/* wake up the application task */
|
||||||
sys_sem_signal(op_completed_sem);
|
sys_sem_signal(op_completed_sem);
|
||||||
} else {
|
return ERR_OK;
|
||||||
/* Closing failed, restore some of the callbacks */
|
}
|
||||||
|
if (!close_finished) {
|
||||||
|
/* Closing failed and we want to wait: restore some of the callbacks */
|
||||||
/* Closing of listen pcb will never fail! */
|
/* Closing of listen pcb will never fail! */
|
||||||
LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
|
LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
|
||||||
tcp_sent(conn->pcb.tcp, sent_tcp);
|
if (shut_tx) {
|
||||||
tcp_poll(conn->pcb.tcp, poll_tcp, 4);
|
tcp_sent(tpcb, sent_tcp);
|
||||||
tcp_err(conn->pcb.tcp, err_tcp);
|
}
|
||||||
tcp_arg(conn->pcb.tcp, conn);
|
/* when waiting for close, set up poll interval to 500ms */
|
||||||
|
tcp_poll(tpcb, poll_tcp, 1);
|
||||||
|
tcp_err(tpcb, err_tcp);
|
||||||
|
tcp_arg(tpcb, conn);
|
||||||
/* don't restore recv callback: we don't want to receive any more data */
|
/* don't restore recv callback: we don't want to receive any more data */
|
||||||
}
|
}
|
||||||
/* If closing didn't succeed, we get called again either
|
/* If closing didn't succeed, we get called again either
|
||||||
from poll_tcp or from sent_tcp */
|
from poll_tcp or from sent_tcp */
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
#endif /* LWIP_TCP */
|
#endif /* LWIP_TCP */
|
||||||
|
|
||||||
@ -854,13 +957,12 @@ lwip_netconn_do_close_internal(struct netconn *conn)
|
|||||||
void
|
void
|
||||||
lwip_netconn_do_delconn(struct api_msg_msg *msg)
|
lwip_netconn_do_delconn(struct api_msg_msg *msg)
|
||||||
{
|
{
|
||||||
/* @todo TCP: abort running write/connect? */
|
enum netconn_state state = msg->conn->state;
|
||||||
if ((msg->conn->state != NETCONN_NONE) &&
|
LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
|
||||||
|
(state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
|
||||||
|
if ((msg->conn->state != NETCONN_NONE) &&
|
||||||
(msg->conn->state != NETCONN_LISTEN) &&
|
(msg->conn->state != NETCONN_LISTEN) &&
|
||||||
(msg->conn->state != NETCONN_CONNECT)) {
|
(msg->conn->state != NETCONN_CONNECT)) {
|
||||||
/* this only happens for TCP netconns */
|
|
||||||
LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP",
|
|
||||||
NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP);
|
|
||||||
msg->err = ERR_INPROGRESS;
|
msg->err = ERR_INPROGRESS;
|
||||||
} else {
|
} else {
|
||||||
LWIP_ASSERT("blocking connect in progress",
|
LWIP_ASSERT("blocking connect in progress",
|
||||||
@ -890,7 +992,17 @@ lwip_netconn_do_delconn(struct api_msg_msg *msg)
|
|||||||
msg->conn->state = NETCONN_CLOSE;
|
msg->conn->state = NETCONN_CLOSE;
|
||||||
msg->msg.sd.shut = NETCONN_SHUT_RDWR;
|
msg->msg.sd.shut = NETCONN_SHUT_RDWR;
|
||||||
msg->conn->current_msg = msg;
|
msg->conn->current_msg = msg;
|
||||||
|
#if LWIP_TCPIP_CORE_LOCKING
|
||||||
|
if (lwip_netconn_do_close_internal(msg->conn) != ERR_OK) {
|
||||||
|
LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
|
||||||
|
UNLOCK_TCPIP_CORE();
|
||||||
|
sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
|
||||||
|
LOCK_TCPIP_CORE();
|
||||||
|
LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
|
||||||
|
}
|
||||||
|
#else /* LWIP_TCPIP_CORE_LOCKING */
|
||||||
lwip_netconn_do_close_internal(msg->conn);
|
lwip_netconn_do_close_internal(msg->conn);
|
||||||
|
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||||
/* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
|
/* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
|
||||||
the application thread, so we can return at this point! */
|
the application thread, so we can return at this point! */
|
||||||
return;
|
return;
|
||||||
@ -1524,16 +1636,14 @@ void
|
|||||||
lwip_netconn_do_close(struct api_msg_msg *msg)
|
lwip_netconn_do_close(struct api_msg_msg *msg)
|
||||||
{
|
{
|
||||||
#if LWIP_TCP
|
#if LWIP_TCP
|
||||||
/* @todo: abort running write/connect? */
|
enum netconn_state state = msg->conn->state;
|
||||||
if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
|
/* First check if this is a TCP netconn and if it is in a correct state
|
||||||
/* this only happens for TCP netconns */
|
(LISTEN doesn't support half shutdown) */
|
||||||
LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP",
|
if ((msg->conn->pcb.tcp != NULL) &&
|
||||||
NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP);
|
(NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
|
||||||
msg->err = ERR_INPROGRESS;
|
((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
|
||||||
} else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)) {
|
if ((state == NETCONN_CONNECT) || (state == NETCONN_WRITE)) {
|
||||||
if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
|
msg->err = ERR_INPROGRESS;
|
||||||
/* LISTEN doesn't support half shutdown */
|
|
||||||
msg->err = ERR_CONN;
|
|
||||||
} else {
|
} else {
|
||||||
if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
|
if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
|
||||||
/* Drain and delete mboxes */
|
/* Drain and delete mboxes */
|
||||||
@ -1543,7 +1653,17 @@ lwip_netconn_do_close(struct api_msg_msg *msg)
|
|||||||
msg->conn->write_offset == 0);
|
msg->conn->write_offset == 0);
|
||||||
msg->conn->state = NETCONN_CLOSE;
|
msg->conn->state = NETCONN_CLOSE;
|
||||||
msg->conn->current_msg = msg;
|
msg->conn->current_msg = msg;
|
||||||
|
#if LWIP_TCPIP_CORE_LOCKING
|
||||||
|
if (lwip_netconn_do_close_internal(msg->conn) != ERR_OK) {
|
||||||
|
LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
|
||||||
|
UNLOCK_TCPIP_CORE();
|
||||||
|
sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
|
||||||
|
LOCK_TCPIP_CORE();
|
||||||
|
LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
|
||||||
|
}
|
||||||
|
#else /* LWIP_TCPIP_CORE_LOCKING */
|
||||||
lwip_netconn_do_close_internal(msg->conn);
|
lwip_netconn_do_close_internal(msg->conn);
|
||||||
|
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||||
/* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
|
/* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -558,6 +558,7 @@ lwip_close(int s)
|
|||||||
{
|
{
|
||||||
struct lwip_sock *sock;
|
struct lwip_sock *sock;
|
||||||
int is_tcp = 0;
|
int is_tcp = 0;
|
||||||
|
err_t err;
|
||||||
|
|
||||||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
|
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
|
||||||
|
|
||||||
@ -572,7 +573,11 @@ lwip_close(int s)
|
|||||||
LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
|
LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
netconn_delete(sock->conn);
|
err = netconn_delete(sock->conn);
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
sock_set_errno(sock, err_to_errno(err));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
free_socket(sock, is_tcp);
|
free_socket(sock, is_tcp);
|
||||||
set_errno(0);
|
set_errno(0);
|
||||||
@ -1196,9 +1201,6 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
|
|||||||
return nready;
|
return nready;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Processing exceptset is not yet implemented.
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
|
lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
|
||||||
struct timeval *timeout)
|
struct timeval *timeout)
|
||||||
@ -1781,6 +1783,23 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
|
|||||||
*(int *)optval = netconn_get_recvbufsize(sock->conn);
|
*(int *)optval = netconn_get_recvbufsize(sock->conn);
|
||||||
break;
|
break;
|
||||||
#endif /* LWIP_SO_RCVBUF */
|
#endif /* LWIP_SO_RCVBUF */
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
case SO_LINGER:
|
||||||
|
{
|
||||||
|
s16_t conn_linger;
|
||||||
|
struct linger* linger = (struct linger*)optval;
|
||||||
|
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
|
||||||
|
conn_linger = sock->conn->linger;
|
||||||
|
if (conn_linger >= 0) {
|
||||||
|
linger->l_onoff = 1;
|
||||||
|
linger->l_linger = (int)conn_linger;
|
||||||
|
} else {
|
||||||
|
linger->l_onoff = 0;
|
||||||
|
linger->l_linger = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
#if LWIP_UDP
|
#if LWIP_UDP
|
||||||
case SO_NO_CHECK:
|
case SO_NO_CHECK:
|
||||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
|
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
|
||||||
@ -2117,6 +2136,26 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
|
|||||||
netconn_set_recvbufsize(sock->conn, *(int*)optval);
|
netconn_set_recvbufsize(sock->conn, *(int*)optval);
|
||||||
break;
|
break;
|
||||||
#endif /* LWIP_SO_RCVBUF */
|
#endif /* LWIP_SO_RCVBUF */
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
case SO_LINGER:
|
||||||
|
{
|
||||||
|
struct linger* linger = (struct linger*)optval;
|
||||||
|
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
|
||||||
|
if (linger->l_onoff) {
|
||||||
|
int lingersec = linger->l_linger;
|
||||||
|
if (lingersec < 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (lingersec > 0xFFFF) {
|
||||||
|
lingersec = 0xFFFF;
|
||||||
|
}
|
||||||
|
sock->conn->linger = (s16_t)lingersec;
|
||||||
|
} else {
|
||||||
|
sock->conn->linger = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
#if LWIP_UDP
|
#if LWIP_UDP
|
||||||
case SO_NO_CHECK:
|
case SO_NO_CHECK:
|
||||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
|
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
|
||||||
|
@ -214,6 +214,10 @@ struct netconn {
|
|||||||
for UDP and RAW, used for FIONREAD */
|
for UDP and RAW, used for FIONREAD */
|
||||||
int recv_avail;
|
int recv_avail;
|
||||||
#endif /* LWIP_SO_RCVBUF */
|
#endif /* LWIP_SO_RCVBUF */
|
||||||
|
#if LWIP_SO_LINGER
|
||||||
|
/** values <0 mean linger is disabled, values > 0 are seconds to linger */
|
||||||
|
s16_t linger;
|
||||||
|
#endif /* LWIP_SO_LINGER */
|
||||||
/** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
|
/** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
|
||||||
u8_t flags;
|
u8_t flags;
|
||||||
#if LWIP_TCP
|
#if LWIP_TCP
|
||||||
|
@ -113,6 +113,11 @@ struct api_msg_msg {
|
|||||||
/** used for lwip_netconn_do_close (/shutdown) */
|
/** used for lwip_netconn_do_close (/shutdown) */
|
||||||
struct {
|
struct {
|
||||||
u8_t shut;
|
u8_t shut;
|
||||||
|
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
|
||||||
|
u32_t time_started;
|
||||||
|
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
|
u8_t polls_left;
|
||||||
|
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
|
||||||
} sd;
|
} sd;
|
||||||
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
|
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
|
||||||
/** used for lwip_netconn_do_join_leave_group */
|
/** used for lwip_netconn_do_join_leave_group */
|
||||||
|
@ -1573,6 +1573,13 @@
|
|||||||
#define LWIP_SO_RCVBUF 0
|
#define LWIP_SO_RCVBUF 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LWIP_SO_LINGER==1: Enable SO_LINGER processing.
|
||||||
|
*/
|
||||||
|
#ifndef LWIP_SO_LINGER
|
||||||
|
#define LWIP_SO_LINGER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize.
|
* If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize.
|
||||||
*/
|
*/
|
||||||
@ -1580,6 +1587,13 @@
|
|||||||
#define RECV_BUFSIZE_DEFAULT INT_MAX
|
#define RECV_BUFSIZE_DEFAULT INT_MAX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, TCP socket/netconn close waits 20 seconds max to send the FIN
|
||||||
|
*/
|
||||||
|
#ifndef LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT
|
||||||
|
#define LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT 20000
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SO_REUSE==1: Enable SO_REUSEADDR option.
|
* SO_REUSE==1: Enable SO_REUSEADDR option.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user