diff --git a/hal/inc/hal_socket.h b/hal/inc/hal_socket.h index e1d02bb6..f1545167 100644 --- a/hal/inc/hal_socket.h +++ b/hal/inc/hal_socket.h @@ -64,6 +64,15 @@ typedef struct sSocket* Socket; /** Opaque reference for a set of server and socket handles */ typedef struct sHandleSet* HandleSet; +/** State of an asynchronous connect */ +typedef enum +{ + SOCKET_STATE_CONNECTING = 0, + SOCKET_STATE_FAILED = 1, + SOCKET_STATE_CONNECTED = 2 +} SocketState; + + /** * \brief Create a new connection handle set (HandleSet) * @@ -224,6 +233,12 @@ Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs); bool Socket_connect(Socket self, const char* address, int port); +bool +Socket_connectAsync(Socket self, const char* address, int port); + +SocketState +Socket_checkAsyncConnectState(Socket self); + /** * \brief read from socket to local buffer (non-blocking) * diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index 69fca6a6..1c0883a6 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -298,9 +298,8 @@ Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs) self->connectTimeout = timeoutInMs; } - bool -Socket_connect(Socket self, const char* address, int port) +Socket_connectAsync(Socket self, const char* address, int port) { struct sockaddr_in serverAddress; @@ -312,38 +311,96 @@ Socket_connect(Socket self, const char* address, int port) self->fd = socket(AF_INET, SOCK_STREAM, 0); - fd_set fdSet; - FD_ZERO(&fdSet); - FD_SET(self->fd, &fdSet); - activateTcpNoDelay(self); +#if (CONFIG_ACTIVATE_TCP_KEEPALIVE == 1) + activateKeepAlive(self->fd); +#endif + fcntl(self->fd, F_SETFL, O_NONBLOCK); if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) { - if (errno != EINPROGRESS) + + if (errno != EINPROGRESS) { + self->fd = -1; return false; + } } + return true; /* is connecting or already connected */ +} + +SocketState +Socket_checkAsyncConnectState(Socket self) +{ + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + fd_set fdSet; + FD_ZERO(&fdSet); + FD_SET(self->fd, &fdSet); + + int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout); + + if (selectVal == 1) { + + /* Check if connection is established */ + + int so_error; + socklen_t len = sizeof so_error; + + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) { + + if (so_error == 0) + return SOCKET_STATE_CONNECTED; + } + + return SOCKET_STATE_FAILED; + } + else if (selectVal == 0) { + return SOCKET_STATE_CONNECTING; + } + else { + return SOCKET_STATE_FAILED; + } +} + +bool +Socket_connect(Socket self, const char* address, int port) +{ + if (Socket_connectAsync(self, address, port) == false) + return false; + struct timeval timeout; timeout.tv_sec = self->connectTimeout / 1000; timeout.tv_usec = (self->connectTimeout % 1000) * 1000; + fd_set fdSet; + FD_ZERO(&fdSet); + FD_SET(self->fd, &fdSet); + if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) { + + /* Check if connection is established */ + int so_error; socklen_t len = sizeof so_error; - getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len); + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) { - if (so_error == 0) - return true; + if (so_error == 0) + return true; + } } close (self->fd); + self->fd = -1; return false; } + char* Socket_getPeerAddress(Socket self) { diff --git a/hal/socket/win32/socket_win32.c b/hal/socket/win32/socket_win32.c index c686ddc7..75c4f653 100644 --- a/hal/socket/win32/socket_win32.c +++ b/hal/socket/win32/socket_win32.c @@ -319,6 +319,81 @@ Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs) self->connectTimeout = timeoutInMs; } +bool +Socket_connectAsync(Socket self, const char* address, int port) +{ + if (DEBUG_SOCKET) + printf("Socket_connect: %s:%i\n", address, port); + + struct sockaddr_in serverAddress; + WSADATA wsa; + int ec; + + if ((ec = WSAStartup(MAKEWORD(2,0), &wsa)) != 0) { + if (DEBUG_SOCKET) + printf("WIN32_SOCKET: winsock error: code %i\n", ec); + return false; + } + + + + if (!prepareServerAddress(address, port, &serverAddress)) + return false; + + self->fd = socket(AF_INET, SOCK_STREAM, 0); + +#if CONFIG_ACTIVATE_TCP_KEEPALIVE == 1 + activateKeepAlive(self->fd); +#endif + + setSocketNonBlocking(self); + + if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + self->fd = INVALID_SOCKET; + return false; + } + } + + return true; /* is connecting or already connected */ +} + +SocketState +Socket_checkAsyncConnectState(Socket self) +{ + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + fd_set fdSet; + FD_ZERO(&fdSet); + FD_SET(self->fd, &fdSet); + + int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout); + + if (selectVal == 1) { + + /* Check if connection is established */ + + int so_error; + int len = sizeof so_error; + + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*) (&so_error), &len) >= 0) { + + if (so_error == 0) + return SOCKET_STATE_CONNECTED; + } + + return SOCKET_STATE_FAILED; + } + else if (selectVal == 0) { + return SOCKET_STATE_CONNECTING; + } + else { + return SOCKET_STATE_FAILED; + } +} + bool Socket_connect(Socket self, const char* address, int port) { diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 1cb2d246..53eca945 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -297,10 +297,12 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters CONFIG_TCP_KEEPALIVE_INTERVAL, CONFIG_TCP_KEEPALIVE_CNT); #endif - + // (1) Function blocks if (!Socket_connect(self->socket, params->hostname, params->tcpPort)) goto returnError; + // (2) Send connection request message + /* COTP (ISO transport) handshake */ CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer); @@ -331,6 +333,7 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters uint64_t timeout = Hal_getTimeInMs() + CONFIG_TCP_READ_TIMEOUT_MS; + // (3) Waiting for response (blocking) while (((packetState = CotpConnection_readToTpktBuffer(self->cotpConnection)) == TPKT_WAITING) && (Hal_getTimeInMs() < timeout)) { @@ -345,6 +348,8 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters if (cotpIndication != COTP_CONNECT_INDICATION) goto returnError; + // (4) Send ACSE Initiate request + /* Upper layers handshake */ struct sBufferChain sAcsePayload; BufferChain acsePayload = &sAcsePayload; @@ -416,6 +421,8 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters AcseIndication acseIndication; + // (5) Wait for ACSE initiate response message + acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); if (acseIndication != ACSE_ASSOCIATE) {