net/tcp: add nonblock connect(2) support

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2021-08-10 20:42:35 +08:00 committed by Xiang Xiao
parent 1a55d933ef
commit 9701a678bd
4 changed files with 132 additions and 43 deletions

View File

@ -242,7 +242,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
if (writefds) if (writefds)
{ {
if (pollset[ndx].revents & POLLOUT) if (pollset[ndx].revents & (POLLOUT | POLLHUP))
{ {
FD_SET(pollset[ndx].fd, writefds); FD_SET(pollset[ndx].fd, writefds);
ret++; ret++;

View File

@ -318,52 +318,66 @@ int psock_tcp_connect(FAR struct socket *psock,
if (ret >= 0) 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); netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev);
if (ret >= 0)
/* 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. */ ret = -EINPROGRESS;
}
netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev); else
{
/* Wait for either the connect to complete or for an error/timeout /* Set up the callbacks in the connection */
* 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 = psock_setup_callbacks(psock, &state);
if (ret >= 0) if (ret >= 0)
{ {
/* If the wait succeeded, then get the new error value from /* Wait for either the connect to complete or for an
* the state structure * 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. */ /* 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 /* Yes... Now that we are connected, we need to set up to monitor
* the state of the connection up the connection event monitor. * the state of the connection up the connection event monitor.
*/ */
ret = tcp_start_monitor(psock); ret2 = tcp_start_monitor(psock);
if (ret < 0) if (ret2 < 0)
{ {
/* tcp_start_monitor() can only fail on certain race /* tcp_start_monitor() can only fail on certain race
* conditions where the connection was lost just before * 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); tcp_stop_monitor(psock->s_conn, TCP_ABORT);
ret = ret2;
} }
} }
} }

View File

@ -41,9 +41,9 @@
****************************************************************************/ ****************************************************************************/
static void tcp_close_connection(FAR struct socket *psock, uint16_t flags); static void tcp_close_connection(FAR struct socket *psock, uint16_t flags);
static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev, static uint16_t tcp_monitor_event(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv, FAR void *pvconn, FAR void *pvpriv,
uint16_t flags); uint16_t flags);
/**************************************************************************** /****************************************************************************
* Private Functions * 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: * Description:
* Some connection related event has occurred * 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, static uint16_t tcp_monitor_event(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv, FAR void *pvconn, FAR void *pvpriv,
uint16_t flags) uint16_t flags)
{ {
FAR struct socket *psock = (FAR struct socket *)pvpriv; 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. * TODO: Implement this.
*/ */
/* Clear the socket error */
_SO_SETERRNO(psock, OK);
/* Indicate that the socket is now connected */ /* Indicate that the socket is now connected */
psock->s_flags |= _SF_CONNECTED; psock->s_flags |= (_SF_BOUND | _SF_CONNECTED);
psock->s_flags &= ~_SF_CLOSED; 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) int tcp_start_monitor(FAR struct socket *psock)
{ {
FAR struct tcp_conn_s *conn;
FAR struct devif_callback_s *cb; FAR struct devif_callback_s *cb;
FAR struct tcp_conn_s *conn;
bool nonblock_conn;
DEBUGASSERT(psock != NULL && psock->s_conn != NULL); DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
conn = (FAR struct tcp_conn_s *)psock->s_conn; 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 /* Check if the connection has already been closed before any callbacks
* have been registered. (Maybe the connection is lost before accept has * have been registered. (Maybe the connection is lost before accept has
* registered the monitoring callback.) * registered the monitoring callback.)
*/ */
net_lock();
if (!(conn->tcpstateflags == TCP_ESTABLISHED || if (!(conn->tcpstateflags == TCP_ESTABLISHED ||
conn->tcpstateflags == TCP_SYN_RCVD)) conn->tcpstateflags == TCP_SYN_RCVD || nonblock_conn))
{ {
/* Invoke the TCP_CLOSE connection event now */ /* 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); cb = devif_callback_alloc(conn->dev, &conn->connevents);
if (cb != NULL) if (cb != NULL)
{ {
cb->event = tcp_disconnect_event; cb->event = tcp_monitor_event;
cb->priv = (FAR void *)psock; cb->priv = (FAR void *)psock;
cb->flags = TCP_DISCONN_EVENTS; cb->flags = TCP_DISCONN_EVENTS;
/* Monitor the connected event */
if (nonblock_conn)
{
cb->flags |= TCP_CONNECTED;
}
} }
net_unlock(); net_unlock();

View File

@ -30,6 +30,7 @@
#include <debug.h> #include <debug.h>
#include <nuttx/net/net.h> #include <nuttx/net/net.h>
#include <nuttx/net/tcp.h>
#include <nuttx/semaphore.h> #include <nuttx/semaphore.h>
#include "devif/devif.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 void *pvpriv, uint16_t flags)
{ {
FAR struct tcp_poll_s *info = (FAR struct tcp_poll_s *)pvpriv; FAR struct tcp_poll_s *info = (FAR struct tcp_poll_s *)pvpriv;
int reason;
ninfo("flags: %04x\n", flags); 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; 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. */ /* Check for a loss of connection events. */
if ((flags & TCP_DISCONN_EVENTS) != 0) 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 */ /* Mark that the connection has been lost */
tcp_lost_connection(info->psock, info->cb, flags); 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_conn_s *conn = psock->s_conn;
FAR struct tcp_poll_s *info; FAR struct tcp_poll_s *info;
FAR struct devif_callback_s *cb; FAR struct devif_callback_s *cb;
bool nonblock_conn;
int ret = OK; int ret = OK;
/* Sanity check */ /* Sanity check */
@ -171,6 +214,11 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
net_lock(); 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 */ /* Find a container to hold the poll information */
info = conn->pollinfo; info = conn->pollinfo;
@ -214,6 +262,13 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
| TCP_ACKDATA | TCP_ACKDATA
#endif #endif
; ;
/* Monitor the connected event */
if (nonblock_conn)
{
cb->flags |= TCP_CONNECTED;
}
} }
if ((fds->events & POLLIN) != 0) 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 * 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 /* We were previously connected but lost the connection either due
* to a graceful shutdown by the remote peer or because of some * to a graceful shutdown by the remote peer or because of some