This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 746d68916f9 net/tcp: add support for the CLOSE_WAIT state
746d68916f9 is described below

commit 746d68916f94dbcb775bb300ad100bea4b1b9a9a
Author: zhanghongyu <[email protected]>
AuthorDate: Wed Aug 6 21:40:47 2025 +0800

    net/tcp: add support for the CLOSE_WAIT state
    
    CLOSE-WAIT - represents waiting for a connection termination request
                 from the local user.
          TCP A                                                TCP B
    
      1.  ESTABLISHED                                          ESTABLISHED
      2.  (Close)
          FIN-WAIT-1  --> <SEQ=100><ACK=300><CTL=FIN,ACK>  --> CLOSE-WAIT
      3.  FIN-WAIT-2  <-- <SEQ=300><ACK=101><CTL=ACK>      <-- CLOSE-WAIT
      4.                                                       (Close)
          TIME-WAIT   <-- <SEQ=300><ACK=101><CTL=FIN,ACK>  <-- LAST-ACK
      5.  TIME-WAIT   --> <SEQ=101><ACK=301><CTL=ACK>      --> CLOSED
      6.  (2 MSL)
          CLOSED
    
    in the current state, we can continue to send data until the user
    calls shutdown or close, then directly enter the TCP_LAST_ACK state
    
    Signed-off-by: zhanghongyu <[email protected]>
---
 Documentation/components/net/index.rst             |   1 +
 Documentation/components/net/tcp_state_machine.rst | 327 +++++++++++++++++++++
 include/nuttx/net/tcp.h                            |   7 +-
 net/tcp/tcp_appsend.c                              |   5 +-
 net/tcp/tcp_close.c                                |   6 +-
 net/tcp/tcp_devpoll.c                              |   3 +-
 net/tcp/tcp_input.c                                |  66 +++--
 net/tcp/tcp_send_buffered.c                        |  10 +-
 net/tcp/tcp_shutdown.c                             |   3 +-
 net/tcp/tcp_timer.c                                |   4 +-
 10 files changed, 395 insertions(+), 37 deletions(-)

diff --git a/Documentation/components/net/index.rst 
b/Documentation/components/net/index.rst
index 148880d8982..f1cd6010832 100644
--- a/Documentation/components/net/index.rst
+++ b/Documentation/components/net/index.rst
@@ -19,6 +19,7 @@ Network Support
   wqueuedeadlocks.rst
   tcp_network_perf.rst
   delay_act_and_tcp_perf.rst
+  tcp_state_machine.rst
 
 ``net`` Directory Structure ::
 
diff --git a/Documentation/components/net/tcp_state_machine.rst 
b/Documentation/components/net/tcp_state_machine.rst
new file mode 100644
index 00000000000..da4b6156136
--- /dev/null
+++ b/Documentation/components/net/tcp_state_machine.rst
@@ -0,0 +1,327 @@
+=============================
+NuttX TCP State Machine Notes
+=============================
+
+This document describes how the current NuttX TCP stack implements TCP
+state transitions. It is based on the in-tree implementation (primarily
+in ``net/tcp``) and focuses on *what the code does today* rather than a
+generic RFC 793 description.
+
+Scope
+=====
+
+* TCP connection state is tracked per ``struct tcp_conn_s``.
+* State transitions happen mainly in:
+
+       * ``net/tcp/tcp_input.c`` (incoming segments and most transitions)
+       * ``net/tcp/tcp_timer.c`` (timeouts and retransmissions)
+       * ``net/tcp/tcp_conn.c`` (connect/listen-side allocation and initial 
state)
+       * ``net/tcp/tcp_close.c`` (active close initiation)
+
+State Representation
+====================
+
+NuttX stores TCP state in ``tcp_conn_s::tcpstateflags``.
+
+* Bits 0-3 are the state (``TCP_STATE_MASK``).
+* Bit 4 is a flag (``TCP_STOPPED``) used by the socket layer to stop data flow.
+
+The state values are defined in ``include/nuttx/net/tcp.h``:
+
+* ``TCP_CLOSED``
+* ``TCP_ALLOCATED`` (NuttX-internal: allocated but not yet connected)
+* ``TCP_SYN_RCVD``
+* ``TCP_SYN_SENT``
+* ``TCP_ESTABLISHED``
+* ``TCP_FIN_WAIT_1``
+* ``TCP_FIN_WAIT_2``
+* ``TCP_CLOSE_WAIT``
+* ``TCP_CLOSING``
+* ``TCP_TIME_WAIT``
+* ``TCP_LAST_ACK``
+* ``TCP_STOPPED``
+
+Supported vs Unsupported (RFC State View)
+=========================================
+
+NuttX largely follows the classic TCP state machine, the table below maps the 
traditional RFC 793 state names to what exists in
+NuttX today.
+
+.. list-table:: RFC TCP states and their NuttX support
+        :header-rows: 1
+        :widths: auto
+
+        * - RFC state name
+                - NuttX representation
+                - Supported
+                - Notes
+        * - CLOSED
+                - ``TCP_CLOSED``
+                - Yes
+                - Connection is unused/available.
+        * - LISTEN
+                - No ``tcpstateflags`` state
+                - Partially
+                - Listening is implemented via the listener table in 
``net/tcp/tcp_listen.c``(``tcp_listenports[]``) rather than a per-connection 
LISTEN state.
+        * - SYN-SENT
+                - ``TCP_SYN_SENT``
+                - Yes
+                - Set by ``tcp_connect()`` in ``net/tcp/tcp_conn.c``.
+        * - SYN-RECEIVED
+                - ``TCP_SYN_RCVD``
+                - Yes
+                - Set when accepting an incoming SYN (new connection allocated 
for a listener).
+        * - ESTABLISHED
+                - ``TCP_ESTABLISHED``
+                - Yes
+                - Data transfer state.
+        * - FIN-WAIT-1
+                - ``TCP_FIN_WAIT_1``
+                - Yes
+                - Entered on active close (local FIN sent). However, it is 
currently unable to continue receiving data in this state
+        * - FIN-WAIT-2
+                - ``TCP_FIN_WAIT_2``
+                - Yes
+                - Entered after ACK for local FIN (when peer hasn't closed 
yet). However, it is currently unable to continue receiving data in this state
+        * - CLOSE-WAIT
+                - Not implemented
+                - Yes
+                - The TCP input path explicitly notes CLOSE_WAIT is not 
implemented; NuttX forces the application to close when FIN is received and 
moves directly toward ``TCP_LAST_ACK``.
+        * - CLOSING
+                - ``TCP_CLOSING``
+                - Yes
+                - Used for simultaneous close handling.
+        * - LAST-ACK
+                - ``TCP_LAST_ACK``
+                - Yes
+                - Used after receiving FIN and sending FIN in response.
+        * - TIME-WAIT
+                - ``TCP_TIME_WAIT``
+                - Yes
+                - Used after the close handshake; timer-driven cleanup.
+
+Note on ``TCP_ALLOCATED``
+-------------------------
+
+``TCP_ALLOCATED`` is NuttX-specific and has no direct RFC state name.
+It is the pre-connect/pre-accept state for a newly created socket connection.
+
+High-level Transition Summary
+=============================
+
+This section summarizes the most common state paths.
+
+Active open (connect)
+---------------------
+
+Typical client-side flow:
+
+::
+
+       TCP_ALLOCATED
+               -> TCP_SYN_SENT        (tcp_connect() prepares SYN)
+               -> TCP_ESTABLISHED     (tcp_input receives SYN|ACK and replies 
ACK)
+
+Passive open (listen/accept)
+----------------------------
+
+Listening sockets are registered in the listener table (not a LISTEN state).
+When a SYN arrives:
+
+::
+
+       listener in tcp_listenports[]
+               -> new conn: TCP_SYN_RCVD  (tcp_allocaccept() in tcp_conn.c)
+               -> TCP_ESTABLISHED         (tcp_input receives final ACK)
+               -> accept() wakes up       (tcp_accept_connection())
+
+Graceful close (active close)
+-----------------------------
+
+When the application initiates a close (or ``shutdown(SHUT_WR)``), the stack
+sends FIN and transitions:
+
+::
+
+       TCP_ESTABLISHED
+               -> TCP_FIN_WAIT_1
+               -> TCP_FIN_WAIT_2          (ACK of our FIN)
+               -> TCP_TIME_WAIT           (FIN from peer)
+               -> TCP_CLOSED              (timer expiry)
+
+Simultaneous close
+------------------
+
+If FIN is received while we are in ``TCP_FIN_WAIT_1`` and our FIN has not been
+fully ACKed, NuttX can enter ``TCP_CLOSING``:
+
+::
+
+       TCP_FIN_WAIT_1
+               -> TCP_CLOSING
+               -> TCP_TIME_WAIT           (ACK of our FIN)
+
+Passive close (peer closes first)
+---------------------------------
+
+When FIN is received in ESTABLISHED, the application is notified
+via callbacks. the stack sends ACK and goes to ``TCP_CLOSE_WAIT``:
+
+::
+
+       TCP_ESTABLISHED
+               -> TCP_CLOSE_WAIT          (FIN received)
+               -> TCP_CLOSED              (ACK of our FIN)
+
+Detailed State Handling
+=======================
+
+TCP_SYN_SENT
+------------
+
+* Entered by ``tcp_connect()`` (``net/tcp/tcp_conn.c``).
+* On receiving ``SYN|ACK`` with a valid ACK:
+
+       * Parses options (e.g., MSS).
+       * Sets ``TCP_ESTABLISHED``.
+       * Updates ``rcvseq`` and window tracking.
+       * Notifies the socket layer using ``TCP_CONNECTED``.
+
+* On unexpected control segments or failure:
+
+       * The connection is aborted (``TCP_ABORT`` callback) and a RST may be 
sent.
+
+TCP_SYN_RCVD
+------------
+
+* Entered for a newly accepted connection when a SYN matches a listener.
+       Allocation and initialization occur in ``tcp_allocaccept()``
+       (``net/tcp/tcp_conn.c``).
+* A SYN-ACK is sent. The retransmission is handled by ``tcp_timer.c``.
+* On receiving the final ACK (``TCP_ACKDATA``):
+
+       * Transition to ``TCP_ESTABLISHED``.
+       * ``tcp_accept_connection()`` is called to hand the connection to the
+               listening socket/accept logic.
+
+TCP_ESTABLISHED
+---------------
+
+* Normal data transfer occurs here.
+* Incoming data and ACK processing is handled in ``net/tcp/tcp_input.c``.
+* If a FIN is received:
+
+       * The application is notified (``TCP_CLOSE`` flag is included in 
callback).
+       * NuttX transitions to ``TCP_CLOSE_WAIT`` and sends ``ACK``.
+
+TCP_CLOSE_WAIT
+--------------
+
+* Only entered when a FIN is received in ESTABLISHED.
+* The application is notified (``TCP_CLOSE`` flag in callback).
+* NuttX can send data until the application initiates close.
+* On application close request:
+       * NuttX sends FIN and transitions to ``TCP_LAST_ACK``.
+
+TCP_FIN_WAIT_1
+--------------
+
+* Entered when the application requests a graceful close.
+       This is initiated in ``net/tcp/tcp_appsend.c`` when the callback result
+       contains ``TCP_CLOSE``.
+
+* On receiving FIN:
+
+       * If the FIN also ACKs our FIN and ``tx_unacked == 0``: transition to
+               ``TCP_TIME_WAIT``.
+       * Otherwise: transition to ``TCP_CLOSING``.
+       * In both cases, ACK the peer FIN.
+
+* On receiving an ACK that completes ACK of our FIN (and no FIN from peer):
+
+       * Transition to ``TCP_FIN_WAIT_2``.
+
+* Data received in FIN_WAIT_1:
+
+       * Current behavior is to send a RST and force ``TCP_CLOSED``.
+       * The implementation notes this as a TODO to improve shutdown behavior.
+
+TCP_FIN_WAIT_2
+--------------
+
+* Waiting for the peer FIN after our FIN was ACKed.
+* On receiving FIN:
+
+       * Transition to ``TCP_TIME_WAIT``.
+       * ACK the FIN and notify close.
+
+* Data received in FIN_WAIT_2:
+
+       * Current behavior is to send a RST and force ``TCP_CLOSED``.
+
+TCP_CLOSING
+-----------
+
+* Simultaneous close case.
+* When the ACK for our FIN is received (``TCP_ACKDATA``):
+
+       * Transition to ``TCP_TIME_WAIT``.
+
+TCP_LAST_ACK
+------------
+
+* Entered after FIN is received in ESTABLISHED and the application chooses
+       to close, causing the stack to send FIN.
+* On receiving ACK for our FIN (``TCP_ACKDATA``):
+
+       * Transition to ``TCP_CLOSED``.
+       * Notify close via callback.
+
+TCP_TIME_WAIT
+-------------
+
+* NuttX responds to segments by sending an ACK.
+* Cleanup is timer-driven (see ``tcp_timer.c``):
+
+       * ``TCP_TIME_WAIT`` are handled as "wait for timeout" states.
+       * When the per-connection timer expires, the state becomes 
``TCP_CLOSED``.
+
+Timers, Retransmissions, and Failure Handling
+=============================================
+
+The TCP timer handler in ``net/tcp/tcp_timer.c`` drives:
+
+* Retransmission for connections with ``tx_unacked > 0``.
+* State-specific retransmit behavior:
+
+       * ``TCP_SYN_RCVD``: retransmit SYN-ACK.
+       * ``TCP_SYN_SENT``: retransmit SYN.
+       * ``TCP_ESTABLISHED``: request retransmit via callback (``TCP_REXMIT``).
+       * ``TCP_FIN_WAIT_1``, ``TCP_CLOSING``, ``TCP_LAST_ACK``: retransmit 
FIN|ACK.
+
+* Timeout cleanup:
+
+       * ``TCP_SYN_RCVD``: if SYN-ACK retransmits exceed limit, the half-open
+               connection is closed and freed.
+       * ``TCP_SYN_SENT`` and established cases: if retransmits exceed limit, 
the
+               connection is closed, the socket is notified 
(``TCP_TIMEDOUT``), and a
+               RST may be sent.
+
+Deviations and Notable Simplifications
+======================================
+
+* LISTEN is not an explicit TCP state; it is represented by listener table 
entries.
+* FIN_WAIT_* data handling is currently strict: received payload data in
+       FIN_WAIT_1/2 results in sending RST and closing the connection.
+* RST processing is intentionally simple (accept RST and close).
+
+Where to Look in the Code
+=========================
+
+* State definitions: ``include/nuttx/net/tcp.h``
+* Incoming-segment state logic: ``net/tcp/tcp_input.c``
+* Retransmission/timeout logic: ``net/tcp/tcp_timer.c``
+* Connect path / SYN_SENT setup: ``net/tcp/tcp_conn.c``
+* Accept path / SYN_RCVD allocation: ``net/tcp/tcp_conn.c``
+* Active close initiation: ``net/tcp/tcp_close.c`` and 
``net/tcp/tcp_shutdown.c``
+* Listener table (LISTEN semantics): ``net/tcp/tcp_listen.c``
diff --git a/include/nuttx/net/tcp.h b/include/nuttx/net/tcp.h
index 5d921d6deb2..5804b19f5a0 100644
--- a/include/nuttx/net/tcp.h
+++ b/include/nuttx/net/tcp.h
@@ -88,9 +88,10 @@
 #  define TCP_ESTABLISHED 0x04
 #  define TCP_FIN_WAIT_1  0x05
 #  define TCP_FIN_WAIT_2  0x06
-#  define TCP_CLOSING     0x07
-#  define TCP_TIME_WAIT   0x08
-#  define TCP_LAST_ACK    0x09
+#  define TCP_CLOSE_WAIT  0x07
+#  define TCP_CLOSING     0x08
+#  define TCP_TIME_WAIT   0x09
+#  define TCP_LAST_ACK    0x0a
 #  define TCP_STOPPED     0x10 /* Bit 4: stopped */
                                /* Bit 5-7: Unused, but not available */
 
diff --git a/net/tcp/tcp_appsend.c b/net/tcp/tcp_appsend.c
index 97b1cf16d7a..bf1f2e6d6a8 100644
--- a/net/tcp/tcp_appsend.c
+++ b/net/tcp/tcp_appsend.c
@@ -194,13 +194,14 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn,
 
   else if ((result & TCP_CLOSE) != 0)
     {
-      conn->tcpstateflags = TCP_FIN_WAIT_1;
+      conn->tcpstateflags = conn->tcpstateflags == TCP_CLOSE_WAIT ?
+                            TCP_LAST_ACK : TCP_FIN_WAIT_1;
       conn->tx_unacked    = 1;
       conn->nrtx          = 0;
 #ifdef CONFIG_NET_TCP_WRITE_BUFFERS
       conn->sndseq_max    = tcp_getsequence(conn->sndseq) + 1;
 #endif
-      ninfo("TCP state: TCP_FIN_WAIT_1\n");
+      ninfo("TCP state: %d\n", conn->tcpstateflags);
 
       dev->d_sndlen       = 0;
       tcp_send(dev, conn, TCP_FIN | TCP_ACK, hdrlen);
diff --git a/net/tcp/tcp_close.c b/net/tcp/tcp_close.c
index d392d684b5d..d91e2b1227c 100644
--- a/net/tcp/tcp_close.c
+++ b/net/tcp/tcp_close.c
@@ -153,7 +153,8 @@ static uint16_t tcp_close_eventhandler(FAR struct 
net_driver_s *dev,
        *   TCP_CLOSE is handled above.
        */
 
-      DEBUGASSERT(conn->tcpstateflags == TCP_ESTABLISHED);
+      DEBUGASSERT(conn->tcpstateflags == TCP_ESTABLISHED ||
+                  conn->tcpstateflags == TCP_CLOSE_WAIT);
 
       /* Drop data received in this state and make sure that TCP_CLOSE
        * is set in the response
@@ -236,7 +237,8 @@ static inline int tcp_close_disconnect(FAR struct socket 
*psock)
    */
 
   if ((conn->tcpstateflags == TCP_ESTABLISHED ||
-       conn->tcpstateflags == TCP_LAST_ACK) &&
+       conn->tcpstateflags == TCP_LAST_ACK ||
+       conn->tcpstateflags == TCP_CLOSE_WAIT) &&
       (conn->clscb = tcp_callback_alloc(conn)) != NULL)
     {
       /* Free rx buffers of the connection immediately */
diff --git a/net/tcp/tcp_devpoll.c b/net/tcp/tcp_devpoll.c
index 741b13ff72c..b3e37dd4b5c 100644
--- a/net/tcp/tcp_devpoll.c
+++ b/net/tcp/tcp_devpoll.c
@@ -109,7 +109,8 @@ void tcp_poll(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
 
   /* Verify that the connection is established. */
 
-  if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
+  if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED ||
+      (conn->tcpstateflags & TCP_STATE_MASK) == TCP_CLOSE_WAIT)
     {
       /* Set up for the callback.  We can't know in advance if the
        * application is going to send a IPv4 or an IPv6 packet, so this
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index 1bb3378d258..07780467135 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -982,8 +982,7 @@ found:
                 g_netstats.tcp.drop, seq, TCP_SEQ_ADD(seq, dev->d_len),
                 dev->d_len);
 
-          dev->d_len = 0;
-          return;
+          goto drop;
         }
     }
 #endif
@@ -1046,7 +1045,8 @@ found:
            * bytes
            */
 
-          if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
+          if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED ||
+              (conn->tcpstateflags & TCP_STATE_MASK) == TCP_CLOSE_WAIT)
             {
               nwarn("WARNING: ackseq > unackseq\n");
               nwarn("sndseq=%" PRIu32 " tx_unacked=%" PRIu32
@@ -1109,7 +1109,8 @@ found:
 
       /* Check if no packet need to retransmission, clear timer. */
 
-      if (conn->tx_unacked == 0 && conn->tcpstateflags == TCP_ESTABLISHED)
+      if (conn->tx_unacked == 0 && (conn->tcpstateflags == TCP_ESTABLISHED ||
+                                    conn->tcpstateflags == TCP_CLOSE_WAIT))
         {
           timeout = 0;
         }
@@ -1397,8 +1398,6 @@ found:
              * has been closed.
              */
 
-            flags |= TCP_CLOSE;
-
             if (dev->d_len > 0)
               {
                 flags |= TCP_NEWDATA;
@@ -1406,23 +1405,10 @@ found:
 
             result = tcp_callback(dev, conn, flags);
 
-            if ((result & TCP_CLOSE) != 0)
-              {
-                conn->tcpstateflags = TCP_LAST_ACK;
-                conn->tx_unacked    = 1;
-                conn->nrtx          = 0;
-                net_incr32(conn->rcvseq, 1); /* ack FIN */
-#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
-                conn->sndseq_max    = tcp_getsequence(conn->sndseq) + 1;
-#endif
-                ninfo("TCP state: TCP_LAST_ACK\n");
-                tcp_send(dev, conn, TCP_FIN | TCP_ACK, tcpiplen);
-              }
-            else
-              {
-                ninfo("TCP: Dropped a FIN\n");
-                tcp_appsend(dev, conn, result);
-              }
+            conn->tcpstateflags = TCP_CLOSE_WAIT;
+            net_incr32(conn->rcvseq, 1); /* ack FIN */
+            ninfo("TCP state: TCP_CLOSE_WAIT\n");
+            tcp_appsend(dev, conn, result | TCP_SNDACK);
 
             return;
           }
@@ -1681,6 +1667,40 @@ found:
             ninfo("TCP state: TCP_TIME_WAIT\n");
           }
 
+        goto drop;
+
+      case TCP_CLOSE_WAIT:
+#ifdef CONFIG_NET_TCP_KEEPALIVE
+        /* If the established socket receives an ACK or any kind of data
+        * from the remote peer (whether we accept it or not), then reset
+        * the keep alive timer.
+        */
+
+        if (conn->keepalive && (tcp->flags & TCP_ACK) != 0)
+          {
+            /* Reset the "alive" timer. */
+
+            tcp_update_keeptimer(conn, conn->keepidle);
+            conn->keepretries = 0;
+          }
+#endif
+
+        if ((flags & TCP_ACKDATA) != 0)
+          {
+            dev->d_sndlen = 0;
+
+            /* Provide the packet to the application */
+
+            result = tcp_callback(dev, conn, flags);
+
+            /* Send the response, ACKing the data or not, as appropriate */
+
+            tcp_appsend(dev, conn, result);
+            return;
+          }
+
+        goto drop;
+
       default:
         break;
     }
diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c
index 1e47081aefb..bbf1fa6957f 100644
--- a/net/tcp/tcp_send_buffered.c
+++ b/net/tcp/tcp_send_buffered.c
@@ -197,8 +197,8 @@ static void retransmit_segment(FAR struct tcp_conn_s *conn,
        * retransmitted, and un-ACKed, if expired is not zero, the
        * connection will be closed.
        *
-       * field expired can only be updated at TCP_ESTABLISHED
-       * state
+       * field expired can only be updated at TCP_ESTABLISHED and
+       * TCP_CLOSE_WAIT state.
        */
 
       conn->expired++;
@@ -936,7 +936,8 @@ static uint16_t psock_send_eventhandler(FAR struct 
net_driver_s *dev,
                * retransmitted, and un-ACKed, if expired is not zero, the
                * connection will be closed.
                *
-               * field expired can only be updated at TCP_ESTABLISHED state
+               * field expired can only be updated at TCP_ESTABLISHED and
+               * TCP_CLOSE_WAIT state.
                */
 
               conn->expired++;
@@ -1012,7 +1013,8 @@ static uint16_t psock_send_eventhandler(FAR struct 
net_driver_s *dev,
    * will have to wait for the next polling cycle.
    */
 
-  if ((conn->tcpstateflags & TCP_ESTABLISHED) &&
+  if ((conn->tcpstateflags & TCP_ESTABLISHED ||
+       conn->tcpstateflags & TCP_CLOSE_WAIT) &&
       ((flags & TCP_NEWDATA) == 0) &&
       (flags & (TCP_POLL | TCP_REXMIT | TCP_ACKDATA)) &&
       !(sq_empty(&conn->write_q)) &&
diff --git a/net/tcp/tcp_shutdown.c b/net/tcp/tcp_shutdown.c
index 2f18a9814f2..840a2160eef 100644
--- a/net/tcp/tcp_shutdown.c
+++ b/net/tcp/tcp_shutdown.c
@@ -112,7 +112,8 @@ static inline int tcp_send_fin(FAR struct socket *psock)
 
   if ((conn->tcpstateflags == TCP_ESTABLISHED ||
        conn->tcpstateflags == TCP_SYN_SENT ||
-       conn->tcpstateflags == TCP_SYN_RCVD))
+       conn->tcpstateflags == TCP_SYN_RCVD ||
+       conn->tcpstateflags == TCP_CLOSE_WAIT))
     {
       if ((conn->shdcb = tcp_callback_alloc(conn)) == NULL)
         {
diff --git a/net/tcp/tcp_timer.c b/net/tcp/tcp_timer.c
index d178e2f51bc..76715c0471c 100644
--- a/net/tcp/tcp_timer.c
+++ b/net/tcp/tcp_timer.c
@@ -623,6 +623,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
                     goto done;
 
                   case TCP_ESTABLISHED:
+                  case TCP_CLOSE_WAIT:
 
                     /* In the ESTABLISHED state, we call upon the application
                      * to do the actual retransmit after which we jump into
@@ -673,7 +674,8 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
        * connection has been established.
        */
 
-      else if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
+      else if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED ||
+               (conn->tcpstateflags & TCP_STATE_MASK) == TCP_CLOSE_WAIT)
         {
 #ifdef CONFIG_NET_TCP_KEEPALIVE
           /* Is this an established connected with KeepAlive enabled? */

Reply via email to