net/tcp: add nonblock connect(2) support
Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
parent
1a55d933ef
commit
9701a678bd
@ -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++;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user