From 9701a678bdf42f6282db5dd50febbbd69925a738 Mon Sep 17 00:00:00 2001 From: "chao.an" Date: Tue, 10 Aug 2021 20:42:35 +0800 Subject: [PATCH] net/tcp: add nonblock connect(2) support Signed-off-by: chao.an --- fs/vfs/fs_select.c | 2 +- net/tcp/tcp_connect.c | 73 ++++++++++++++++++++++++++----------------- net/tcp/tcp_monitor.c | 42 ++++++++++++++++++------- net/tcp/tcp_netpoll.c | 58 +++++++++++++++++++++++++++++++++- 4 files changed, 132 insertions(+), 43 deletions(-) diff --git a/fs/vfs/fs_select.c b/fs/vfs/fs_select.c index 1a3734aeff..908a7863d8 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 d64767fefa..d72f3660bc 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) + netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev); + + /* Non-blocking connection ? set the socket error + * and start the monitor + */ + + if (_SS_ISNONBLOCK(psock->s_flags)) { - /* Notify the device driver that new connection is available. */ - - netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev); - - /* 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. - */ - - ret = net_lockedwait(&state.tc_sem); - - /* Uninitialize the state structure */ - - nxsem_destroy(&state.tc_sem); - - /* If net_lockedwait failed, negated errno was returned. */ + ret = -EINPROGRESS; + } + else + { + /* Set up the callbacks in the connection */ + ret = psock_setup_callbacks(psock, &state); if (ret >= 0) { - /* If the wait succeeded, then get the new error value from - * the state structure + /* 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. */ - ret = state.tc_result; + ret = net_lockedwait(&state.tc_sem); + + /* Uninitialize the state structure */ + + nxsem_destroy(&state.tc_sem); + + /* If net_lockedwait failed, negated errno was returned. */ + + 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 */ + + psock_teardown_callbacks(&state, ret); } - - /* Make sure that no further events are processed */ - - 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 ab550188b2..3ee5f08456 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 8f28da48a4..4d64797df3 100644 --- a/net/tcp/tcp_netpoll.c +++ b/net/tcp/tcp_netpoll.c @@ -30,6 +30,7 @@ #include #include +#include #include #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