net/tcp: add TCP_RXCLOSE to handle rx is shutdown

notify the local waiting recv process; otherwise, after the peer TX
is closed, the wait of the recv process will continue to wait.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
This commit is contained in:
zhanghongyu 2025-08-11 17:23:28 +08:00 committed by Xiang Xiao
parent 2ad01e2aab
commit fa47ab7bd2
8 changed files with 71 additions and 32 deletions

View file

@ -62,8 +62,8 @@
/* The following flags may be set in the set of flags by the lower, device-
* interfacing layer before calling through the socket layer callback. The
* TCP_ACKDATA, XYZ_NEWDATA, and TCP_CLOSE flags may be set at the same time,
* whereas the others are mutually exclusive.
* TCP_ACKDATA, XYZ_NEWDATA, TCP_CLOSE, TCP_RXCLOSE and TCP_TXCLOSE flags
* may be set at the same time, whereas the others are mutually exclusive.
*
* Connection Specific Events: These are events that may be notified
* through callback lists residing in TCP, UDP, or PKT port connection
@ -123,6 +123,19 @@
* retransmissions. (TCP only)
* OUT: Not used
*
* TCP_RXCLOSE IN: The remote host has closed the connection, thus the
* connection has gone away. (TCP only)
* OUT: The socket layer signals that it wants to close the
* connection. (TCP only)
*
* TCP_TXCLOSE IN: The local host has closed the connection, thus the
* connection has gone away. (TCP only)
* OUT: The socket layer signals that it wants to close the
* connection. (TCP only)
*
* NETDEV_DOWN: IN: The network device has been taken down.
* OUT: Not used
*
* Device Specific Events: These are events that may be notified through
* callback lists residing in the network device structure.
*
@ -159,12 +172,9 @@
* event, not associated with a socket. The appdata
* pointer is not used in this case.
* OUT: Not used
*
* NETDEV_DOWN: IN: The network device has been taken down.
* OUT: Not used
*/
/* Bits 0-11: Connection specific event bits */
/* Bits 0-12: Connection specific event bits */
#define TCP_ACKDATA (1 << 0)
#define TCP_NEWDATA (1 << 1)
@ -193,18 +203,19 @@
#define TCP_TIMEDOUT (1 << 9)
#define TCP_WAITALL (1 << 10)
#define TCP_TXCLOSE (1 << 11)
#define TCP_RXCLOSE (1 << 12)
/* Bit 12: Device specific event bits */
/* Bit 13: Device specific event bits */
#define NETDEV_DOWN (1 << 12)
#define NETDEV_DOWN (1 << 13)
/* Bits 13-15: Encoded device specific poll events. Unlike connection
/* Bits 14-16: Encoded device specific poll events. Unlike connection
* oriented poll events, device related poll events must distinguish
* between what is being polled for since the callbacks all reside in
* the same list in the network device structure.
*/
#define DEVPOLL_SHIFT (13)
#define DEVPOLL_SHIFT (14)
#define DEVPOLL_MASK (7 << DEVPOLL_SHIFT)
# define DEVPOLL_NONE (0 << DEVPOLL_SHIFT)
# define ARP_POLL (1 << DEVPOLL_SHIFT)
@ -216,7 +227,7 @@
#define TCP_CONN_EVENTS \
(TCP_CLOSE | TCP_ABORT | TCP_CONNECTED | TCP_TIMEDOUT | NETDEV_DOWN | \
TCP_TXCLOSE)
TCP_TXCLOSE | TCP_RXCLOSE)
#define TCP_DISCONN_EVENTS \
(TCP_CLOSE | TCP_ABORT | TCP_TIMEDOUT | NETDEV_DOWN)

View file

@ -189,9 +189,9 @@ struct tcp_conn_s
* When an callback is executed from 'list', the input flags are normally
* returned, however, the implementation may set one of the following:
*
* TCP_RXCLOSE - Gracefully close the current connection (RX)
* TCP_TXCLOSE - Gracefully close the current connection (TX)
* TCP_ABORT - Abort (reset) the current connection on an error that
* prevents TCP_CLOSE from working.
* TCP_ABORT - Abort (reset) the current connection
*
* And/Or set/clear the following:
*

View file

@ -271,10 +271,10 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
return 0;
}
/* Preserve the TCP_ACKDATA, TCP_TXCLOSE, and TCP_ABORT in the response.
* These is needed by the network to handle responses and buffer state.
* The TCP_NEWDATA indication will trigger the ACK response, but must be
* explicitly set in the callback.
/* Preserve the TCP_ACKDATA, TCP_CLOSE, TCP_RXCLOSE, TCP_TXCLOSE, and
* TCP_ABORT in the response. These is needed by the network to handle
* responses and buffer state. The TCP_NEWDATA indication will trigger
* the ACK response, but must be explicitly set in the callback.
*/
ninfo("flags: %04x\n", flags);
@ -283,9 +283,9 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
* 'list', the input flags are normally returned, however, the
* implementation may set one of the following:
*
* TCP_RXCLOSE - Gracefully close the current connection (RX)
* TCP_TXCLOSE - Gracefully close the current connection (TX)
* TCP_ABORT - Abort (reset) the current connection on an error that
* prevents TCP_CLOSE from working.
* TCP_ABORT - Abort (reset) the current connection
*
* And/Or set/clear the following:
*

View file

@ -1398,6 +1398,7 @@ found:
* has been closed.
*/
flags |= TCP_RXCLOSE;
if (dev->d_len > 0)
{
flags |= TCP_NEWDATA;
@ -1577,7 +1578,7 @@ found:
}
net_incr32(conn->rcvseq, 1); /* ack FIN */
tcp_callback(dev, conn, TCP_CLOSE);
tcp_callback(dev, conn, TCP_RXCLOSE);
tcp_send(dev, conn, TCP_ACK, tcpiplen);
return;
}
@ -1606,7 +1607,7 @@ found:
ninfo("TCP state: TCP_TIME_WAIT\n");
net_incr32(conn->rcvseq, 1); /* ack FIN */
tcp_callback(dev, conn, TCP_CLOSE);
tcp_callback(dev, conn, TCP_RXCLOSE);
tcp_send(dev, conn, TCP_ACK, tcpiplen);
return;
}

View file

@ -87,6 +87,10 @@ static void tcp_close_connection(FAR struct tcp_conn_s *conn, uint16_t flags)
* _SF_CONNECTED==0 && _SF_CLOSED==0 - the socket was rudely disconnected
*/
/* The loss of connection was less than graceful. This will
* (eventually) be reported as an ENOTCONN error.
*/
if ((flags & TCP_CLOSE) != 0)
{
/* The peer gracefully closed the connection. Marking the
@ -174,6 +178,10 @@ static uint16_t tcp_monitor_event(FAR struct net_driver_s *dev,
conn->sconn.s_flags |= (_SF_BOUND | _SF_CONNECTED);
conn->sconn.s_flags &= ~_SF_CLOSED;
}
else if ((flags & TCP_RXCLOSE) != 0)
{
conn->shutdown |= SHUT_RD;
}
}
return flags;
@ -294,7 +302,7 @@ int tcp_start_monitor(FAR struct socket *psock)
{
cb->event = tcp_monitor_event;
cb->priv = (FAR void *)conn;
cb->flags = TCP_DISCONN_EVENTS;
cb->flags = TCP_DISCONN_EVENTS | TCP_RXCLOSE;
/* Monitor the connected event */

View file

@ -83,7 +83,7 @@ static uint16_t tcp_poll_eventhandler(FAR struct net_driver_s *dev,
/* Check for data or connection availability events. */
if ((flags & (TCP_NEWDATA | TCP_BACKLOG)) != 0)
if ((flags & (TCP_NEWDATA | TCP_BACKLOG | TCP_RXCLOSE)) != 0)
{
eventset |= POLLIN;
}
@ -301,7 +301,7 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
if ((fds->events & POLLIN) != 0)
{
cb->flags |= TCP_NEWDATA | TCP_BACKLOG;
cb->flags |= TCP_NEWDATA | TCP_BACKLOG | TCP_RXCLOSE;
}
/* Save the reference in the poll info structure as fds private as well
@ -312,7 +312,8 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
/* Check for read data or backlogged connection availability now */
if (conn->readahead != NULL || tcp_backlogpending(conn))
if (conn->readahead != NULL || tcp_backlogpending(conn) ||
(conn->shutdown & SHUT_RD) != 0)
{
/* Normal data may be read without blocking. */

View file

@ -209,10 +209,11 @@ static inline uint16_t tcp_newdata(FAR struct net_driver_s *dev,
if (recvlen < dev->d_len)
{
/* Clear the TCP_CLOSE because we effectively dropped the FIN as well.
/* Clear the TCP_RXCLOSE because we effectively dropped the FIN as
* well.
*/
flags &= ~TCP_CLOSE;
flags &= ~TCP_RXCLOSE;
}
net_incr32(conn->rcvseq, recvlen);
@ -522,6 +523,12 @@ static uint16_t tcp_recvhandler(FAR struct net_driver_s *dev,
/* Wake up the waiting thread */
nxsem_post(&pstate->ir_sem);
}
else if ((flags & TCP_RXCLOSE) != 0)
{
ninfo("TCP_RXCLOSE\n");
pstate->ir_result = 0;
nxsem_post(&pstate->ir_sem);
}
}
@ -719,7 +726,8 @@ static ssize_t tcp_recvfrom_one(FAR struct tcp_conn_s *conn, FAR void *buf,
/* Verify that the SOCK_STREAM has been and still is connected */
if (!_SS_ISCONNECTED(conn->sconn.s_flags))
if (!_SS_ISCONNECTED(conn->sconn.s_flags) ||
(conn->shutdown & SHUT_RD) != 0)
{
/* Was any data transferred from the readahead buffer after we were
* disconnected? If so, then return the number of bytes received.
@ -732,7 +740,8 @@ static ssize_t tcp_recvfrom_one(FAR struct tcp_conn_s *conn, FAR void *buf,
* recvfrom() will get an end-of-file indication.
*/
if (ret <= 0 && !_SS_ISCLOSED(conn->sconn.s_flags))
if (ret <= 0 && !_SS_ISCLOSED(conn->sconn.s_flags) &&
(conn->shutdown & SHUT_RD) == 0)
{
/* Nothing was previously received from the read-ahead buffers.
* The SOCK_STREAM must be (re-)connected in order to receive
@ -794,7 +803,8 @@ static ssize_t tcp_recvfrom_one(FAR struct tcp_conn_s *conn, FAR void *buf,
state.ir_cb = tcp_callback_alloc(conn);
if (state.ir_cb)
{
state.ir_cb->flags = (TCP_NEWDATA | TCP_DISCONN_EVENTS);
state.ir_cb->flags = (TCP_NEWDATA | TCP_RXCLOSE |
TCP_DISCONN_EVENTS);
state.ir_cb->flags |= (flags & MSG_WAITALL) ? TCP_WAITALL : 0;
state.ir_cb->priv = (FAR void *)&state;
state.ir_cb->event = tcp_recvhandler;

View file

@ -71,7 +71,15 @@ static uint16_t tcp_shutdown_eventhandler(FAR struct net_driver_s *dev,
#endif
dev->d_len = 0;
flags |= TCP_TXCLOSE;
if ((conn->shutdown & SHUT_WR) != 0)
{
flags |= TCP_TXCLOSE;
}
if ((conn->shutdown & SHUT_RD) != 0)
{
flags |= TCP_RXCLOSE;
}
if (conn->shdcb != NULL)
{
@ -165,7 +173,7 @@ int tcp_shutdown(FAR struct socket *psock, int how)
conn = psock->s_conn;
DEBUGASSERT(conn != NULL);
if (!(how & SHUT_WR))
if (!(how & SHUT_RDWR))
{
return -EOPNOTSUPP;
}