Fixed bug #10088: Correctly implemented shutdown at socket level.

This commit is contained in:
goldsimon
2010-06-24 19:33:14 +00:00
parent f61b80ca6a
commit 6929a786aa
7 changed files with 139 additions and 25 deletions

View File

@@ -600,13 +600,14 @@ netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apifl
}
/**
* Close a TCP netconn (doesn't delete it).
* Close ot shutdown a TCP netconn (doesn't delete it).
*
* @param conn the TCP netconn to close
* @param conn the TCP netconn to close or shutdown
* @param how fully close or only shutdown one side?
* @return ERR_OK if the netconn was closed, any other err_t on error
*/
err_t
netconn_close(struct netconn *conn)
static err_t
netconn_close_shutdown(struct netconn *conn, u8_t how)
{
struct api_msg msg;
err_t err;
@@ -615,6 +616,8 @@ netconn_close(struct netconn *conn)
msg.function = do_close;
msg.msg.conn = conn;
/* shutting down both ends is the same as closing */
msg.msg.msg.sd.shut = how;
/* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
don't use TCPIP_APIMSG here */
err = tcpip_apimsg(&msg);
@@ -623,6 +626,31 @@ netconn_close(struct netconn *conn)
return err;
}
/**
* Close a TCP netconn (doesn't delete it).
*
* @param conn the TCP netconn to close
* @return ERR_OK if the netconn was closed, any other err_t on error
*/
err_t
netconn_close(struct netconn *conn)
{
/* shutting down both ends is the same as closing */
return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
}
/**
* Shut down one or both sides of a TCP netconn (doesn't delete it).
*
* @param conn the TCP netconn to shut down
* @return ERR_OK if the netconn was closed, any other err_t on error
*/
err_t
netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
{
return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
}
#if LWIP_IGMP
/**
* Join multicast groups for UDP netconns.

View File

@@ -729,6 +729,7 @@ static void
do_close_internal(struct netconn *conn)
{
err_t err;
u8_t shut, shut_rx, shut_tx, close;
LWIP_ASSERT("invalid conn", (conn != NULL));
LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
@@ -736,20 +737,38 @@ do_close_internal(struct netconn *conn)
LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
shut = conn->current_msg->msg.sd.shut;
shut_rx = shut & NETCONN_SHUT_RD;
shut_tx = shut & NETCONN_SHUT_WR;
/* shutting down both ends is the same as closing */
close = shut == NETCONN_SHUT_RDWR;
/* Set back some callback pointers */
tcp_arg(conn->pcb.tcp, NULL);
if (close) {
tcp_arg(conn->pcb.tcp, NULL);
}
if (conn->pcb.tcp->state == LISTEN) {
tcp_accept(conn->pcb.tcp, NULL);
} else {
tcp_recv(conn->pcb.tcp, NULL);
tcp_accept(conn->pcb.tcp, NULL);
/* some callbacks have to be reset if tcp_close is not successful */
tcp_sent(conn->pcb.tcp, NULL);
tcp_poll(conn->pcb.tcp, NULL, 4);
tcp_err(conn->pcb.tcp, NULL);
if (shut_rx) {
tcp_recv(conn->pcb.tcp, NULL);
tcp_accept(conn->pcb.tcp, NULL);
}
if (shut_tx) {
tcp_sent(conn->pcb.tcp, NULL);
}
if (close) {
tcp_poll(conn->pcb.tcp, NULL, 4);
tcp_err(conn->pcb.tcp, NULL);
}
}
/* Try to close the connection */
err = tcp_close(conn->pcb.tcp);
if (shut == NETCONN_SHUT_RDWR) {
err = tcp_close(conn->pcb.tcp);
} else {
err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR);
}
if (err == ERR_OK) {
/* Closing succeeded */
conn->current_msg->err = ERR_OK;
@@ -759,9 +778,15 @@ do_close_internal(struct netconn *conn)
conn->pcb.tcp = NULL;
/* Trigger select() in socket layer. Make sure everybody notices activity
on the connection, error first! */
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
if (close) {
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
}
if (shut_rx) {
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
}
if (shut_tx) {
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
}
/* wake up the application task */
sys_sem_signal(&conn->op_completed);
} else {
@@ -772,6 +797,7 @@ do_close_internal(struct netconn *conn)
tcp_poll(conn->pcb.tcp, poll_tcp, 4);
tcp_err(conn->pcb.tcp, err_tcp);
tcp_arg(conn->pcb.tcp, conn);
/* don't restore recv callback: we don't want to receive any more data */
}
/* If closing didn't succeed, we get called again either
from poll_tcp or from sent_tcp */
@@ -1401,15 +1427,22 @@ do_close(struct api_msg_msg *msg)
LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
msg->err = ERR_INPROGRESS;
} else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
/* Drain and delete mboxes */
netconn_drain(msg->conn);
LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
msg->conn->write_offset == 0);
msg->conn->state = NETCONN_CLOSE;
msg->conn->current_msg = msg;
do_close_internal(msg->conn);
/* for tcp netconns, do_close_internal ACKs the message */
return;
if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
/* LISTEN doesn't support half shutdown */
msg->err = ERR_CONN;
} else {
if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
/* Drain and delete mboxes */
netconn_drain(msg->conn);
}
LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
msg->conn->write_offset == 0);
msg->conn->state = NETCONN_CLOSE;
msg->conn->current_msg = msg;
do_close_internal(msg->conn);
/* for tcp netconns, do_close_internal ACKs the message */
return;
}
} else
#endif /* LWIP_TCP */
{

View File

@@ -1344,9 +1344,42 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
int
lwip_shutdown(int s, int how)
{
LWIP_UNUSED_ARG(how);
struct lwip_sock *sock;
err_t err;
u8_t shut_rx = 0, shut_tx = 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
return lwip_close(s); /* XXX temporary hack until proper implementation */
sock = get_socket(s);
if (!sock) {
return -1;
}
if (sock->conn != NULL) {
if (netconn_type(sock->conn) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
return EOPNOTSUPP;
}
} else {
sock_set_errno(sock, ENOTCONN);
return ENOTCONN;
}
if (how == SHUT_RD) {
shut_rx = 1;
} else if (how == SHUT_WR) {
shut_tx = 1;
} else if(how == SHUT_RDWR) {
shut_rx = 1;
shut_tx = 1;
} else {
sock_set_errno(sock, EINVAL);
return EINVAL;
}
err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
sock_set_errno(sock, err_to_errno(err));
return (err == ERR_OK ? 0 : -1);
}
static int