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 (pollset[ndx].revents & POLLOUT)
if (pollset[ndx].revents & (POLLOUT | POLLHUP))
{
FD_SET(pollset[ndx].fd, writefds);
ret++;

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -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