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:
sg 2015-02-10 22:15:54 +01:00
parent ee833ea594
commit 6c3f6cfd89
7 changed files with 258 additions and 53 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
} }

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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.
*/ */