You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/08/20 02:19:13 UTC

[incubator-nuttx] branch master updated: net/tcp: add nonblock connect(2) support

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/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 9701a67  net/tcp: add nonblock connect(2) support
9701a67 is described below

commit 9701a678bdf42f6282db5dd50febbbd69925a738
Author: chao.an <an...@xiaomi.com>
AuthorDate: Tue Aug 10 20:42:35 2021 +0800

    net/tcp: add nonblock connect(2) support
    
    Signed-off-by: chao.an <an...@xiaomi.com>
---
 fs/vfs/fs_select.c    |  2 +-
 net/tcp/tcp_connect.c | 67 +++++++++++++++++++++++++++++++--------------------
 net/tcp/tcp_monitor.c | 42 +++++++++++++++++++++++---------
 net/tcp/tcp_netpoll.c | 58 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 129 insertions(+), 40 deletions(-)

diff --git a/fs/vfs/fs_select.c b/fs/vfs/fs_select.c
index 1a3734a..908a786 100644
--- a/fs/vfs/fs_select.c
+++ b/fs/vfs/fs_select.c
@@ -242,7 +242,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
 
           if (writefds)
             {
-              if (pollset[ndx].revents & POLLOUT)
+              if (pollset[ndx].revents & (POLLOUT | POLLHUP))
                 {
                   FD_SET(pollset[ndx].fd, writefds);
                   ret++;
diff --git a/net/tcp/tcp_connect.c b/net/tcp/tcp_connect.c
index d64767f..d72f366 100644
--- a/net/tcp/tcp_connect.c
+++ b/net/tcp/tcp_connect.c
@@ -318,52 +318,66 @@ int psock_tcp_connect(FAR struct socket *psock,
 
   if (ret >= 0)
     {
-      /* Set up the callbacks in the connection */
+      /* Notify the device driver that new connection is available. */
 
-      ret = psock_setup_callbacks(psock, &state);
-      if (ret >= 0)
-        {
-          /* Notify the device driver that new connection is available. */
+      netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev);
 
-          netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev);
+      /* Non-blocking connection ? set the socket error
+       * and start the monitor
+       */
 
-          /* Wait for either the connect to complete or for an error/timeout
-           * to occur. NOTES:  net_lockedwait will also terminate if a signal
-           * is received.
-           */
+      if (_SS_ISNONBLOCK(psock->s_flags))
+        {
+          ret = -EINPROGRESS;
+        }
+      else
+        {
+          /* Set up the callbacks in the connection */
 
-          ret = net_lockedwait(&state.tc_sem);
+          ret = psock_setup_callbacks(psock, &state);
+          if (ret >= 0)
+            {
+              /* Wait for either the connect to complete or for an
+               * error/timeout to occur.
+               * NOTES:  net_lockedwait will also terminate if a
+               * signal is received.
+               */
 
-          /* Uninitialize the state structure */
+              ret = net_lockedwait(&state.tc_sem);
 
-          nxsem_destroy(&state.tc_sem);
+              /* Uninitialize the state structure */
 
-          /* If net_lockedwait failed, negated errno was returned. */
+              nxsem_destroy(&state.tc_sem);
 
-          if (ret >= 0)
-            {
-              /* If the wait succeeded, then get the new error value from
-               * the state structure
-               */
+              /* If net_lockedwait failed, negated errno was returned. */
 
-              ret = state.tc_result;
-            }
+              if (ret >= 0)
+                {
+                  /* If the wait succeeded, then get the new error
+                   * value from the state structure
+                   */
+
+                  ret = state.tc_result;
+                }
 
-          /* Make sure that no further events are processed */
+              /* Make sure that no further events are processed */
 
-          psock_teardown_callbacks(&state, ret);
+              psock_teardown_callbacks(&state, ret);
+            }
         }
 
       /* Check if the socket was successfully connected. */
 
-      if (ret >= 0)
+      if (ret >= 0 || ret == -EINPROGRESS)
         {
+          int ret2;
+
           /* Yes... Now that we are connected, we need to set up to monitor
            * the state of the connection up the connection event monitor.
            */
 
-          ret = tcp_start_monitor(psock);
-          if (ret < 0)
+          ret2 = tcp_start_monitor(psock);
+          if (ret2 < 0)
             {
               /* tcp_start_monitor() can only fail on certain race
                * conditions where the connection was lost just before
@@ -372,6 +386,7 @@ int psock_tcp_connect(FAR struct socket *psock,
                */
 
               tcp_stop_monitor(psock->s_conn, TCP_ABORT);
+              ret = ret2;
             }
         }
     }
diff --git a/net/tcp/tcp_monitor.c b/net/tcp/tcp_monitor.c
index ab55018..3ee5f08 100644
--- a/net/tcp/tcp_monitor.c
+++ b/net/tcp/tcp_monitor.c
@@ -41,9 +41,9 @@
  ****************************************************************************/
 
 static void tcp_close_connection(FAR struct socket *psock, uint16_t flags);
-static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
-                                 FAR void *pvconn, FAR void *pvpriv,
-                                 uint16_t flags);
+static uint16_t tcp_monitor_event(FAR struct net_driver_s *dev,
+                                  FAR void *pvconn, FAR void *pvpriv,
+                                  uint16_t flags);
 
 /****************************************************************************
  * Private Functions
@@ -106,7 +106,7 @@ static void tcp_close_connection(FAR struct socket *psock, uint16_t flags)
 }
 
 /****************************************************************************
- * Name: tcp_disconnect_event
+ * Name: tcp_monitor_event
  *
  * Description:
  *   Some connection related event has occurred
@@ -124,9 +124,9 @@ static void tcp_close_connection(FAR struct socket *psock, uint16_t flags)
  *
  ****************************************************************************/
 
-static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
-                                     FAR void *pvconn, FAR void *pvpriv,
-                                     uint16_t flags)
+static uint16_t tcp_monitor_event(FAR struct net_driver_s *dev,
+                                  FAR void *pvconn, FAR void *pvpriv,
+                                  uint16_t flags)
 {
   FAR struct socket *psock = (FAR struct socket *)pvpriv;
 
@@ -164,9 +164,13 @@ static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
            * TODO: Implement this.
            */
 
+          /* Clear the socket error */
+
+          _SO_SETERRNO(psock, OK);
+
           /* Indicate that the socket is now connected */
 
-          psock->s_flags |= _SF_CONNECTED;
+          psock->s_flags |= (_SF_BOUND | _SF_CONNECTED);
           psock->s_flags &= ~_SF_CLOSED;
         }
     }
@@ -242,20 +246,27 @@ static void tcp_shutdown_monitor(FAR struct tcp_conn_s *conn, uint16_t flags)
 
 int tcp_start_monitor(FAR struct socket *psock)
 {
-  FAR struct tcp_conn_s *conn;
   FAR struct devif_callback_s *cb;
+  FAR struct tcp_conn_s *conn;
+  bool nonblock_conn;
 
   DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
   conn = (FAR struct tcp_conn_s *)psock->s_conn;
 
+  net_lock();
+
+  /* Non-blocking connection ? */
+
+  nonblock_conn = (conn->tcpstateflags == TCP_SYN_SENT &&
+                   _SS_ISNONBLOCK(psock->s_flags));
+
   /* Check if the connection has already been closed before any callbacks
    * have been registered. (Maybe the connection is lost before accept has
    * registered the monitoring callback.)
    */
 
-  net_lock();
   if (!(conn->tcpstateflags == TCP_ESTABLISHED ||
-        conn->tcpstateflags == TCP_SYN_RCVD))
+        conn->tcpstateflags == TCP_SYN_RCVD || nonblock_conn))
     {
       /* Invoke the TCP_CLOSE connection event now */
 
@@ -276,9 +287,16 @@ int tcp_start_monitor(FAR struct socket *psock)
   cb = devif_callback_alloc(conn->dev, &conn->connevents);
   if (cb != NULL)
     {
-      cb->event = tcp_disconnect_event;
+      cb->event = tcp_monitor_event;
       cb->priv  = (FAR void *)psock;
       cb->flags = TCP_DISCONN_EVENTS;
+
+      /* Monitor the connected event */
+
+      if (nonblock_conn)
+        {
+          cb->flags |= TCP_CONNECTED;
+        }
     }
 
   net_unlock();
diff --git a/net/tcp/tcp_netpoll.c b/net/tcp/tcp_netpoll.c
index 8f28da4..4d64797 100644
--- a/net/tcp/tcp_netpoll.c
+++ b/net/tcp/tcp_netpoll.c
@@ -30,6 +30,7 @@
 #include <debug.h>
 
 #include <nuttx/net/net.h>
+#include <nuttx/net/tcp.h>
 #include <nuttx/semaphore.h>
 
 #include "devif/devif.h"
@@ -67,6 +68,7 @@ static uint16_t tcp_poll_eventhandler(FAR struct net_driver_s *dev,
                                       FAR void *pvpriv, uint16_t flags)
 {
   FAR struct tcp_poll_s *info = (FAR struct tcp_poll_s *)pvpriv;
+  int reason;
 
   ninfo("flags: %04x\n", flags);
 
@@ -85,10 +87,50 @@ static uint16_t tcp_poll_eventhandler(FAR struct net_driver_s *dev,
           eventset |= POLLIN & info->fds->events;
         }
 
+      /* Non-blocking connection */
+
+      if ((flags & TCP_CONNECTED) != 0)
+        {
+          eventset |= POLLOUT & info->fds->events;
+        }
+
       /* Check for a loss of connection events. */
 
       if ((flags & TCP_DISCONN_EVENTS) != 0)
         {
+          /* TCP_TIMEDOUT: Connection aborted due to too many
+           *               retransmissions.
+           */
+
+          if ((flags & TCP_TIMEDOUT) != 0)
+            {
+              /* Indicate that the connection timedout?) */
+
+              reason = ETIMEDOUT;
+            }
+
+          else if ((flags & NETDEV_DOWN) != 0)
+            {
+              /* The network device went down.  Indicate that the remote host
+               * is unreachable.
+               */
+
+              reason = ENETUNREACH;
+            }
+
+          /* TCP_CLOSE: The remote host has closed the connection
+           * TCP_ABORT: The remote host has aborted the connection
+           */
+
+          else
+            {
+              /* Indicate that remote host refused the connection */
+
+              reason = ECONNREFUSED;
+            }
+
+          _SO_SETERRNO(info->psock, reason);
+
           /* Mark that the connection has been lost */
 
           tcp_lost_connection(info->psock, info->cb, flags);
@@ -156,6 +198,7 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
   FAR struct tcp_conn_s *conn = psock->s_conn;
   FAR struct tcp_poll_s *info;
   FAR struct devif_callback_s *cb;
+  bool nonblock_conn;
   int ret = OK;
 
   /* Sanity check */
@@ -171,6 +214,11 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
 
   net_lock();
 
+  /* Non-blocking connection ? */
+
+  nonblock_conn = (conn->tcpstateflags == TCP_SYN_SENT &&
+                   _SS_ISNONBLOCK(psock->s_flags));
+
   /* Find a container to hold the poll information */
 
   info = conn->pollinfo;
@@ -214,6 +262,13 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
                    | TCP_ACKDATA
 #endif
                    ;
+
+      /* Monitor the connected event */
+
+      if (nonblock_conn)
+        {
+          cb->flags |= TCP_CONNECTED;
+        }
     }
 
   if ((fds->events & POLLIN) != 0)
@@ -276,7 +331,8 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
    *    Action: Return with POLLHUP|POLLERR events
    */
 
-  if (!_SS_ISCONNECTED(psock->s_flags) && !_SS_ISLISTENING(psock->s_flags))
+  if (!nonblock_conn && !_SS_ISCONNECTED(psock->s_flags) &&
+      !_SS_ISLISTENING(psock->s_flags))
     {
       /* We were previously connected but lost the connection either due
        * to a graceful shutdown by the remote peer or because of some