Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/teosock #5

Merged
merged 3 commits into from Mar 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions libteol0/teonet_l0_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <string.h>
#include <stdlib.h>

#if defined(TEONET_OS_LINUX)
#if defined(TEONET_OS_LINUX) || defined(TEONET_OS_MACOS) || defined(TEONET_OS_IOS)
#include <netdb.h>
#include <unistd.h>
#endif
Expand Down Expand Up @@ -518,10 +518,10 @@ int teoLNullReadEventLoop(teoLNullConnectData *con, int timeout) {

int rv, retval = 1;

rv = teosockSelectRead(con->fd, timeout);
rv = teosockSelect(con->fd, TEOSOCK_SELECT_MODE_READ, timeout);

// Error
if (rv == TEOSOCK_SELECT_READ_ERROR) {
if (rv == TEOSOCK_SELECT_ERROR) {
int error = errno;
if (error == EINTR) {
// just an interrupted system call
Expand All @@ -531,7 +531,7 @@ int teoLNullReadEventLoop(teoLNullConnectData *con, int timeout) {
}

// Timeout
else if(rv == TEOSOCK_SELECT_READ_TIMEOUT) { // Idle or Timeout event
else if(rv == TEOSOCK_SELECT_TIMEOUT) { // Idle or Timeout event

send_l0_event(con, EV_L_IDLE, NULL, 0);
}
Expand Down Expand Up @@ -607,7 +607,7 @@ teoLNullConnectData* teoLNullConnectE(const char *server, uint16_t port,
printf("Connecting to the server %s at port %" PRIu16 " ...\n", server, port);
#endif

result = teosockConnect(con->fd, server, port);
result = teosockConnectTimeout(con->fd, server, port, 5000);

if (result == TEOSOCK_CONNECT_HOST_NOT_FOUND) {
printf("HOST NOT FOUND --> h_errno = %" PRId32 "\n", h_errno);
Expand All @@ -632,9 +632,6 @@ teoLNullConnectData* teoLNullConnectE(const char *server, uint16_t port,
#endif
}

// Set non block mode
teosockSetNonblock(con->fd);

// Set TCP_NODELAY option
teosockSetTcpNodelay(con->fd);

Expand Down
7 changes: 7 additions & 0 deletions libteol0/teonet_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
#elif defined(_WIN32)
// Defined if target OS is Windows.
#define TEONET_OS_WINDOWS
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define TEONET_OS_IOS
#else
#define TEONET_OS_MACOS
#endif
#else
#error Unsupported target OS.
#endif
Expand Down
147 changes: 133 additions & 14 deletions libteol0/teonet_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#include <unistd.h>
#endif

#if defined(TEONET_OS_IOS)
#include <sys/time.h> // To be able to use select() function.
#endif

// Creates a TCP socket.
teonetSocket teosockCreateTcp() {
return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
Expand Down Expand Up @@ -73,6 +77,97 @@ teosockConnectResult teosockConnect(teonetSocket socket, const char* server, uin
return TEOSOCK_CONNECT_SUCCESS;
}

// Establishes a connection to a specified server.
teosockConnectResult teosockConnectTimeout(teonetSocket socket, const char* server, uint16_t port, int timeout) {
struct sockaddr_in serveraddr;

memset(&serveraddr, 0, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);

#if !defined(TEONET_COMPILER_MINGW)
int result = inet_pton(AF_INET, server, &serveraddr.sin_addr);

// Resolve host address if needed.
if (result != 1) {
struct hostent* hostp = gethostbyname(server);
if (hostp == NULL) {
return TEOSOCK_CONNECT_HOST_NOT_FOUND;
}

memcpy(&serveraddr.sin_addr, hostp->h_addr_list[0], sizeof(serveraddr.sin_addr));
}
#else
// MinGW compiler still doest not support inet_pton().
serveraddr.sin_addr.s_addr = inet_addr(server);

// Resolve host address if needed.
if (serveraddr.sin_addr.s_addr == (unsigned long)INADDR_NONE) {
struct hostent* hostp = gethostbyname(server);
if (hostp == NULL) {
return TEOSOCK_CONNECT_HOST_NOT_FOUND;
}

memcpy(&serveraddr.sin_addr, hostp->h_addr_list[0], sizeof(serveraddr.sin_addr));
}
#endif

int set_non_locking_result = teosockSetBlockingMode(socket, TEOSOCK_NON_BLOCKING_MODE);

if (set_non_locking_result == TEOSOCK_SOCKET_ERROR) {
teosockClose(socket);
return TEOSOCK_CONNECT_FAILED;
}

// Connect to server.
int connect_result = connect(socket, (struct sockaddr*)&serveraddr, sizeof(serveraddr));

if (connect_result == 0) {
return TEOSOCK_CONNECT_SUCCESS;
}
else {
int in_progress = 0;

#if defined(TEONET_OS_WINDOWS)
int error_code = WSAGetLastError();

if (error_code == WSAEWOULDBLOCK) {
in_progress = 1;
}
#else
int error_code = errno;

if (error_code == EINPROGRESS) {
in_progress = 1;
}
#endif

if (in_progress != 1) {
teosockClose(socket);
return TEOSOCK_CONNECT_FAILED;
}
}

int select_result = teosockSelect(socket, TEOSOCK_SELECT_MODE_WRITE | TEOSOCK_SELECT_MODE_ERROR, timeout);

if (select_result == TEOSOCK_SELECT_TIMEOUT || select_result == TEOSOCK_SELECT_ERROR) {
teosockClose(socket);
return TEOSOCK_CONNECT_FAILED;
}

int error = 0;
socklen_t error_len = sizeof(error);

int getsockopt_result = getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&error, &error_len);

if (getsockopt_result == TEOSOCK_SOCKET_ERROR || error != 0) {
teosockClose(socket);
return TEOSOCK_CONNECT_FAILED;
}

return TEOSOCK_CONNECT_SUCCESS;
}

// Receives data from a connected socket.
ssize_t teosockRecv(teonetSocket socket, char* data, size_t length) {
#if defined(TEONET_OS_WINDOWS)
Expand Down Expand Up @@ -101,28 +196,34 @@ ssize_t teosockSend(teonetSocket socket, const char* data, size_t length) {
#endif
}

// Determines the status of the socket, waiting if necessary, to perform synchronous read.
teosockSelectReadResult teosockSelectRead(teonetSocket socket, int timeout) {
fd_set readfds;
// Determines the status of the socket, waiting if necessary, to perform synchronous operation.
teosockSelectResult teosockSelect(teonetSocket socket, int status_mask, int timeout) {
fd_set socket_fd_set;
struct timeval timeval_timeout;

memset(&readfds, 0, sizeof(readfds));
memset(&socket_fd_set, 0, sizeof(socket_fd_set));
memset(&timeval_timeout, 0, sizeof(timeval_timeout));
timeval_timeout.tv_usec = timeout * 1000;

timeval_timeout.tv_sec = timeout / 1000;
timeval_timeout.tv_usec = (timeout % 1000) * 1000;

// Create a descriptor set with specified socket.
FD_ZERO(&readfds);
FD_SET(socket, &readfds);
FD_ZERO(&socket_fd_set);
FD_SET(socket, &socket_fd_set);

fd_set* read_fd_set = (status_mask & TEOSOCK_SELECT_MODE_READ) ? &socket_fd_set : NULL;
fd_set* write_fd_set = (status_mask & TEOSOCK_SELECT_MODE_WRITE) ? &socket_fd_set : NULL;
fd_set* error_fd_set = (status_mask & TEOSOCK_SELECT_MODE_ERROR) ? &socket_fd_set : NULL;

#if defined(TEONET_OS_WINDOWS)
int result = select(0, &readfds, NULL, NULL, &timeval_timeout);
int result = select(0, read_fd_set, write_fd_set, error_fd_set, &timeval_timeout);
#else
int result = select(socket + 1, &readfds, NULL, NULL, &timeval_timeout);
int result = select(socket + 1, read_fd_set, write_fd_set, error_fd_set, &timeval_timeout);
#endif

// Make sure that return value is correct.
if (result > 0) {
result = TEOSOCK_SELECT_READ_READY;
result = TEOSOCK_SELECT_READY;
}

return result;
Expand All @@ -142,10 +243,10 @@ int teosockShutdown(teonetSocket socket, teosockShutdownMode mode) {
return shutdown(socket, mode);
}

// Enables nonblocking mode on specified socket.
int teosockSetNonblock(teonetSocket socket) {
// Sets blocking or non-blocking mode for specified socket.
int teosockSetBlockingMode(teonetSocket socket, teosockBlockingMode blocking_mode) {
#if defined(TEONET_OS_WINDOWS)
u_long mode = 1; // != 0 to enable non-blocking mode.
u_long mode = (u_long)blocking_mode;

return ioctlsocket(socket, FIONBIO, &mode);
#else
Expand All @@ -154,7 +255,25 @@ int teosockSetNonblock(teonetSocket socket) {
if (flags == -1) {
return TEOSOCK_SOCKET_ERROR;
} else {
return fcntl(socket, F_SETFL, flags | O_NONBLOCK);
int new_flags;
if (blocking_mode == TEOSOCK_BLOCKING_MODE) {
new_flags = flags & ~O_NONBLOCK;
} else {
new_flags = flags | O_NONBLOCK;
}

int result;
if (new_flags == flags) {
result = TEOSOCK_SOCKET_SUCCESS;
} else {
result = fcntl(socket, F_SETFL, new_flags);

if (result != TEOSOCK_SOCKET_ERROR) {
result = TEOSOCK_SOCKET_SUCCESS;
}
}

return result;
}
#endif
}
Expand Down
61 changes: 48 additions & 13 deletions libteol0/teonet_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@ typedef enum teosockConnectResult {
*/
teosockConnectResult teosockConnect(teonetSocket socket, const char* server, uint16_t port);

/**
* Establishes a connection to a specified server.
*
* @param socket Socket descriptor obtained using teosockCreateTcp() function.
* @param server Server IP address or domain name.
* @param port Port to connect to.
* @param timeout Maximum amount of time to wait before returning error, in milliseconds.
*
* @returns Result of operation.
*
* @retval TEOSOCK_CONNECT_SUCCESS if connection successfully established.
* @retval TEOSOCK_CONNECT_HOST_NOT_FOUND if failed to resolve host address.
* @retval TEOSOCK_CONNECT_FAILED if failed to connect to server.
*
* @note Socket will be left in non-blocking mode.
*/
teosockConnectResult teosockConnectTimeout(teonetSocket socket, const char* server, uint16_t port, int timeout);

/**
* Receives data from a connected socket.
*
Expand All @@ -96,26 +114,34 @@ ssize_t teosockRecv(teonetSocket socket, char* data, size_t length);
*/
ssize_t teosockSend(teonetSocket socket, const char* data, size_t length);

/// Result enumeration for teosockSelectRead() function.
typedef enum teosockSelectReadResult {
TEOSOCK_SELECT_READ_READY = 1, ///< Socket have data ready to be read.
TEOSOCK_SELECT_READ_TIMEOUT = 0, ///< No data was received before reaching timeout.
TEOSOCK_SELECT_READ_ERROR = -1, ///< An error occurred.
} teosockSelectReadResult;
/// Enumeration with bit flags for status masks for teosockSelect function.
typedef enum teosockSelectMode {
TEOSOCK_SELECT_MODE_READ = 1 << 0, ///< Check socket for readability.
TEOSOCK_SELECT_MODE_WRITE = 1 << 1, ///< Check socket for writability.
TEOSOCK_SELECT_MODE_ERROR = 1 << 2, ///< Check socket for errors.
} teosockSelectMode;

/// Result enumeration for teosockSelect() function.
typedef enum teosockSelectResult {
TEOSOCK_SELECT_READY = 1, ///< Socket is ready or have data to be read.
TEOSOCK_SELECT_TIMEOUT = 0, ///< Socket is not ready or no data was received before reaching timeout.
TEOSOCK_SELECT_ERROR = -1, ///< An error occurred.
} teosockSelectResult;

/**
* Determines the status of the socket, waiting if necessary, to perform synchronous read.
* Determines the status of the socket, waiting if necessary, to perform synchronous operation.
*
* @param socket Socket descriptor obtained using teosockCreateTcp() function.
* @param status_mask A combination of teosockSelectMode flags defining modes to check.
* @param timeout The amount of time to wait before returning timeout, in milliseconds.
*
* @returns Result of operation.
*
* @retval TEOSOCK_SELECT_READ_READY if socket have data ready to be read.
* @retval TEOSOCK_SELECT_READ_TIMEOUT if no data was received before reaching timeout.
* @retval TEOSOCK_SELECT_READ_ERROR if an error occurred.
* @retval TEOSOCK_SELECT_READY if socket have data ready to be read.
* @retval TEOSOCK_SELECT_TIMEOUT if no data was received before reaching timeout.
* @retval TEOSOCK_SELECT_ERROR if an error occurred.
*/
teosockSelectReadResult teosockSelectRead(teonetSocket socket, int timeout);
teosockSelectResult teosockSelect(teonetSocket socket, int status_mask, int timeout);

/**
* Closes a socket.
Expand Down Expand Up @@ -153,17 +179,26 @@ typedef enum teosockShutdownMode {
*/
int teosockShutdown(teonetSocket socket, teosockShutdownMode mode);

/// Enumeration for specifying socket blocking mode in teosockShutdown() function.
typedef enum teosockBlockingMode {
/// Set socket to blocking mode.
TEOSOCK_BLOCKING_MODE = 0,
/// Set socket to non-blocking mode.
TEOSOCK_NON_BLOCKING_MODE = 1,
} teosockBlockingMode;

/**
* Enables nonblocking mode on a socket.
* Set blocking or non-blocking mode on a socket.
*
* @param socket Socket descriptor obtained using teosockCreateTcp() function.
* @param blocking_mode Blocking mode to set socket to.
*
* @returns Result of operation.
*
* @retval TEOSOCK_SOCKET_SUCCESS if operation completed successfully.
* @retval TEOSOCK_SOCKET_ERROR if operation failed.
*/
int teosockSetNonblock(teonetSocket socket);
int teosockSetBlockingMode(teonetSocket socket, teosockBlockingMode blocking_mode);

/**
* Set TCP_NODELAY option on a socket.
Expand Down