This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 5255ef5b3beac214d82ad2be2926bf98f92fb1be Author: Marco Casaroli <[email protected]> AuthorDate: Tue Nov 3 01:16:15 2026 +0100 net/tcp: Return EPIPE from a send after the connection was closed. POSIX: a send on a connection-mode socket that is no longer connected fails with EPIPE, and the caller gets SIGPIPE unless MSG_NOSIGNAL is given. After the peer closed a TCP connection, the next send got a reset, and every send after it failed with ENOTCONN. So psock_sigpipe() did not raise SIGPIPE, and a program that writes to a closed connection did not end. The send paths now return -EPIPE when the socket is not connected and _SF_CLOSED is set (the connection was closed or reset by the peer), and -ENOTCONN as before for a socket that never was connected. This is TCP_NOTCONN_ERROR() in tcp.h, used in the buffered send, the unbuffered send and tcp_sendfile(), and in the event handlers of the unbuffered send and of tcp_sendfile(). A connection lost by a timeout or a network down event clears _SF_CLOSED too, so a send after it still fails with ENOTCONN. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- net/tcp/tcp.h | 8 ++++++++ net/tcp/tcp_send_buffered.c | 7 ++++--- net/tcp/tcp_send_unbuffered.c | 14 ++++++++------ net/tcp/tcp_sendfile.c | 8 +++++--- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 2bdb0f50665..22332f104e3 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -100,6 +100,14 @@ #define TCP_SEQ_ADD(a, b) ((uint32_t)((a) + (b))) #define TCP_SEQ_SUB(a, b) ((uint32_t)((a) - (b))) +/* The error of a send on a TCP socket that is not connected: -EPIPE if the + * connection was closed (POSIX: the socket is no longer connected), else + * -ENOTCONN. + */ + +#define TCP_NOTCONN_ERROR(conn) \ + (_SS_ISCLOSED((conn)->sconn.s_flags) ? -EPIPE : -ENOTCONN) + /* The TCP options flags */ #define TCP_WSCALE 0x01U /* Window Scale option enabled */ diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index b32076731a4..0e3edbccc86 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -1330,7 +1330,8 @@ static unsigned int tcp_send_gettimeout(clock_t start, unsigned int timeout) * ENOTSOCK * The argument s is not a socket. * EPIPE - * The local end has been shut down on a connection oriented socket. + * The local end has been shut down on a connection oriented socket, or + * the connection was closed. * In this case the process will also receive a SIGPIPE unless * MSG_NOSIGNAL is set. * @@ -1361,7 +1362,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, if (!_SS_ISCONNECTED(conn->sconn.s_flags)) { nerr("ERROR: Not connected\n"); - ret = -ENOTCONN; + ret = TCP_NOTCONN_ERROR(conn); goto errout; } @@ -1429,7 +1430,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, if (!_SS_ISCONNECTED(conn->sconn.s_flags)) { nerr("ERROR: No longer connected\n"); - ret = -ENOTCONN; + ret = TCP_NOTCONN_ERROR(conn); goto errout_with_lock; } diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c index 0113c729f61..8fe57eb63e7 100644 --- a/net/tcp/tcp_send_unbuffered.c +++ b/net/tcp/tcp_send_unbuffered.c @@ -322,7 +322,7 @@ static uint32_t tcpsend_eventhandler(FAR struct net_driver_s *dev, tcp_lost_connection(conn, pstate->snd_cb, flags); } - pstate->snd_sent = -ENOTCONN; + pstate->snd_sent = TCP_NOTCONN_ERROR(conn); goto end_wait; } @@ -473,7 +473,8 @@ end_wait: * ENOTSOCK * The argument s is not a socket. * EPIPE - * The local end has been shut down on a connection oriented socket. + * The local end has been shut down on a connection oriented socket, or + * the connection was closed. * In this case the process will also receive a SIGPIPE unless * MSG_NOSIGNAL is set. * @@ -500,14 +501,15 @@ ssize_t psock_tcp_send(FAR struct socket *psock, conn = psock->s_conn; /* Check early if this is an un-connected socket, if so, then - * return -ENOTCONN. Note, we will have to check this again, as we can't - * guarantee the state won't change until we have the network locked. + * return -ENOTCONN (-EPIPE after the connection was closed). Note, we + * will have to check this again, as we can't guarantee the state won't + * change until we have the network locked. */ if (!_SS_ISCONNECTED(conn->sconn.s_flags)) { nerr("ERROR: Not connected\n"); - ret = -ENOTCONN; + ret = TCP_NOTCONN_ERROR(conn); goto errout; } @@ -566,7 +568,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, { nerr("ERROR: No longer connected\n"); conn_dev_unlock(&conn->sconn, conn->dev); - ret = -ENOTCONN; + ret = TCP_NOTCONN_ERROR(conn); goto errout; } diff --git a/net/tcp/tcp_sendfile.c b/net/tcp/tcp_sendfile.c index edfb7ac259b..50fe745b449 100644 --- a/net/tcp/tcp_sendfile.c +++ b/net/tcp/tcp_sendfile.c @@ -310,7 +310,7 @@ static uint32_t sendfile_eventhandler(FAR struct net_driver_s *dev, /* Report not connected */ - pstate->snd_sent = -ENOTCONN; + pstate->snd_sent = TCP_NOTCONN_ERROR(conn); goto end_wait; } @@ -425,12 +425,14 @@ ssize_t tcp_sendfile(FAR struct socket *psock, FAR struct file *infile, conn = psock->s_conn; DEBUGASSERT(conn != NULL); - /* If this is an un-connected socket, then return ENOTCONN */ + /* If this is an un-connected socket, then return ENOTCONN (EPIPE after + * the connection was closed). + */ if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(conn->sconn.s_flags)) { nerr("ERROR: Not connected\n"); - return -ENOTCONN; + return TCP_NOTCONN_ERROR(conn); } /* Make sure that we have the IP address mapping */
