-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
socket: new socket api cross-platform layer (#112)
- Loading branch information
Showing
5 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#define ZPL_IMPL | ||
#define ZPL_NANO | ||
#define ZPL_ENABLE_SOCKET | ||
#include <zpl.h> | ||
|
||
int main(void) { | ||
zpl_socket_init(); | ||
|
||
zpl_socket sock = zpl_socket_create(ZPL_SOCKET_UDP, ZPL_SOCKET_BIND, 0, "127.0.0.1", "7800"); | ||
if (sock == -1) { | ||
zpl_printf_err("Failed to create socket\n"); | ||
return 1; | ||
} | ||
|
||
zpl_socket connect = zpl_socket_create(ZPL_SOCKET_UDP, ZPL_SOCKET_CONNECT, 0, "127.0.0.1", "7800"); | ||
if (connect == -1) { | ||
zpl_printf_err("Failed to create socket\n"); | ||
return 1; | ||
} | ||
|
||
char buffer[64] = {0}; | ||
zpl_snprintf(buffer, zpl_size_of(buffer), "%s", "Hello, World!\n"); | ||
zpl_isize len = zpl_socket_send(connect, buffer, zpl_size_of(buffer)); | ||
zpl_printf("Sent %d bytes\n", len); | ||
zpl_socket_close(connect); | ||
|
||
zpl_zero_item(buffer); | ||
len = zpl_socket_receive(sock, buffer, zpl_size_of(buffer)); | ||
zpl_printf("Received %d bytes, data: %s", len, buffer); | ||
|
||
zpl_socket_close(sock); | ||
|
||
zpl_socket_terminate(); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// file: header/socket.h | ||
|
||
ZPL_BEGIN_C_DECLS | ||
|
||
typedef int zpl_socket; | ||
|
||
typedef struct zpl_socket_addr { | ||
char data[128]; | ||
} zpl_socket_addr; | ||
|
||
typedef enum zpl_socket_protocol { | ||
ZPL_SOCKET_TCP = 0, | ||
ZPL_SOCKET_UDP = 1, | ||
} zpl_socket_protocol; | ||
|
||
typedef enum zpl_socket_mode { | ||
ZPL_SOCKET_BIND = 0, | ||
ZPL_SOCKET_CONNECT = 1, | ||
} zpl_socket_mode; | ||
|
||
typedef enum zpl_socket_flags { | ||
ZPL_SOCKET_DEFAULT = 0, | ||
ZPL_SOCKET_NON_BLOCKING = 0x01, | ||
ZPL_SOCKET_NO_DELAY = 0x02, | ||
} zpl_socket_flags; | ||
|
||
/** | ||
* Initializes socket functionality | ||
* @return 0 on success | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_init(void); | ||
|
||
/** | ||
* Creates a new socket configured according to the given parameters | ||
* @param protocol Protocol of the socket, either ZPL_SOCKET_TCP or ZPL_SOCKET_UDP for TCP or UDP respectively | ||
* @param mode Mode of the socket (bind or connect), either ZPL_SOCKET_BIND or ZPL_SOCKET_CONNECT | ||
* @param flags Configuration flags, either ZPL_SOCKET_DEFAULT or a bitwise combination of flags | ||
* @param host Host/address as a string, can be IPv4, IPv6, etc... | ||
* @param service Service/port as a string, e.g. "1728" or "http" | ||
* @return socket handle, or -1 on failure | ||
*/ | ||
ZPL_DEF zpl_socket zpl_socket_create(zpl_i32 protocol, zpl_i32 mode, char flags, const char *host, const char *service); | ||
|
||
/** | ||
* Closes the given socket | ||
* @param socket Socket handle | ||
*/ | ||
ZPL_DEF void zpl_socket_close(zpl_socket socket); | ||
|
||
/** | ||
* Terminates socket functionality | ||
*/ | ||
ZPL_DEF void zpl_socket_terminate(void); | ||
|
||
/** | ||
* Configures the given socket (must be ZPL_SOCKET_TCP + ZPL_SOCKET_BIND) to listen for new connections with the given backlog | ||
* @param socket Socket handle | ||
* @param backlog Size of the backlog | ||
* @return 0 on success, non-zero on failure | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_listen(zpl_socket socket, zpl_i32 backlog); | ||
|
||
/** | ||
* Uses the given socket (must be zpl_socket_listen) to accept a new incoming connection, optionally returning its address | ||
* @param socket Socket handle | ||
* @param addr Pointer to zpl_socket_addr to store the address | ||
* @return a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections) | ||
*/ | ||
ZPL_DEF zpl_socket zpl_socket_accept(zpl_socket socket, zpl_socket_addr *addr); | ||
|
||
/** | ||
* Writes the address the given socket is bound to into the given address pointer, useful when automatically assigning a port | ||
* @param socket Socket handle | ||
* @param addr Pointer to zpl_socket_addr to store the address | ||
* @return 0 on success, non-zero on failure | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_get_address(zpl_socket socket, zpl_socket_addr *addr); | ||
|
||
/** | ||
* Writes the host/address and service/port of the given address into given buffers (pointer + size), one buffer may be NULL | ||
* @param addr Pointer to zpl_socket_addr containing the address | ||
* @param host Buffer to store the host/address string | ||
* @param host_size Size of the host buffer | ||
* @param service Buffer to store the service/port string | ||
* @param service_size Size of the service buffer | ||
* @return 0 on success, non-zero on failure | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_get_address_info(zpl_socket_addr *addr, char *host, zpl_i32 host_size, char *service, zpl_i32 service_size); | ||
|
||
/** | ||
* Uses the given socket (either ZPL_SOCKET_CONNECT or returned by zpl_socket_accept) to send the given data | ||
* @param socket Socket handle | ||
* @param data Pointer to the data to be sent | ||
* @param size Size of the data | ||
* @return how much data was actually sent (may be less than data size), or -1 on failure | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_send(zpl_socket socket, const char *data, zpl_i32 size); | ||
|
||
/** | ||
* Receives data using the given socket (either ZPL_SOCKET_CONNECT or returned by zpl_socket_accept) into the given buffer | ||
* @param socket Socket handle | ||
* @param buffer Pointer to the buffer to receive the data | ||
* @param size Size of the buffer | ||
* @return the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_receive(zpl_socket socket, char *buffer, zpl_i32 size); | ||
|
||
/** | ||
* Uses the given socket to send the given data to the given zpl_socket_addr | ||
* @param socket Socket handle | ||
* @param addr Pointer to zpl_socket_addr containing the address to send the data to | ||
* @param data Pointer to the data to be sent | ||
* @param size Size of the data | ||
* @return how much data was actually sent (may be less than data size), or -1 on failure | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_send_to(zpl_socket socket, zpl_socket_addr *addr, const char *data, zpl_i32 size); | ||
|
||
/** | ||
* Receives data using the given socket into the given buffer, optionally returning the sender's address | ||
* @param socket Socket handle | ||
* @param addr Pointer to zpl_socket_addr to store the sender's address | ||
* @param buffer Pointer to the buffer to receive the data | ||
* @param size Size of the buffer | ||
* @return the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_receive_from(zpl_socket socket, zpl_socket_addr *addr, char *buffer, zpl_i32 size); | ||
|
||
/** | ||
* Waits either until the given socket has new data to receive or the given time (in seconds) has passed | ||
* @param socket Socket handle | ||
* @param time Time to wait in seconds | ||
* @return 1 if new data is available, 0 if timeout was reached, and -1 on error | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_select(zpl_socket socket, zpl_f64 time); | ||
|
||
/** | ||
* Waits either until a socket in the given list has new data to receive or the given time (in seconds) has passed | ||
* @param sockets Array of socket handles | ||
* @param count Number of sockets in the array | ||
* @param time Time to wait in seconds | ||
* @return 1 or more if new data is available, 0 if timeout was reached, and -1 on error | ||
*/ | ||
ZPL_DEF zpl_i32 zpl_socket_multi_select(zpl_socket *sockets, zpl_i32 count, zpl_f64 time); | ||
|
||
ZPL_END_C_DECLS | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// file: source/socket.c | ||
|
||
ZPL_BEGIN_C_DECLS | ||
|
||
#if defined(ZPL_SYSTEM_WINDOWS) | ||
# include <ws2tcpip.h> | ||
# pragma comment(lib, "ws2_32.lib") | ||
typedef int socklen_t; | ||
#else //unix | ||
# include <sys/socket.h> | ||
# include <netdb.h> | ||
# include <fcntl.h> | ||
# include <unistd.h> | ||
#ifndef TCP_NODELAY | ||
# include <netinet/in.h> | ||
# include <netinet/tcp.h> | ||
# endif | ||
#endif | ||
|
||
ZPL_DEF zpl_socket zpl_socket_init(void) { | ||
# if defined(ZPL_SYSTEM_WINDOWS) | ||
WSADATA winsock_data = {0}; | ||
return WSAStartup(MAKEWORD(2, 2), &winsock_data) != NO_ERROR; | ||
# endif | ||
return 0; | ||
} | ||
|
||
ZPL_DEF zpl_socket zpl_socket_create(zpl_i32 protocol, zpl_i32 mode, char flags, const char *host, const char *service) { | ||
struct addrinfo *result, hints = { | ||
(mode == ZPL_SOCKET_BIND) ? AI_PASSIVE : 0, | ||
AF_UNSPEC, | ||
(protocol == ZPL_SOCKET_UDP) ? SOCK_DGRAM : SOCK_STREAM, | ||
0, 0, 0, 0, 0 | ||
}; | ||
|
||
if (getaddrinfo(host, service, &hints, &result) != 0) { | ||
return -1; | ||
} | ||
# if defined(ZPL_SYSTEM_WINDOWS) | ||
zpl_socket sock = (zpl_socket)socket(result->ai_family, result->ai_socktype, result->ai_protocol); | ||
if (sock == INVALID_SOCKET) { | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
if (sock > INT_MAX) { | ||
closesocket(sock); | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
# else | ||
zpl_socket sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); | ||
if (sock == -1) { | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
# endif | ||
|
||
if (result->ai_family == AF_INET6) { | ||
zpl_i32 no = 0; | ||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&no, sizeof(no)); | ||
} | ||
|
||
if (protocol == ZPL_SOCKET_TCP) { | ||
int nodelay = (flags & ZPL_SOCKET_NO_DELAY); | ||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); | ||
} | ||
|
||
if (mode == ZPL_SOCKET_BIND) { | ||
if (bind(sock, result->ai_addr, (int)result->ai_addrlen) != 0) { | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
} | ||
|
||
if (flags & ZPL_SOCKET_NON_BLOCKING) { | ||
# if defined(ZPL_SYSTEM_WINDOWS) | ||
DWORD non_blocking = 1; | ||
if (ioctlsocket(sock, FIONBIO, &non_blocking)) { | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
# else | ||
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
# endif | ||
} | ||
|
||
if (mode == ZPL_SOCKET_CONNECT) { | ||
if (connect(sock, result->ai_addr, (int)result->ai_addrlen) != 0 && !(flags & ZPL_SOCKET_NON_BLOCKING)) { | ||
freeaddrinfo(result); | ||
return -1; | ||
} | ||
} | ||
|
||
freeaddrinfo(result); | ||
return sock; | ||
} | ||
|
||
ZPL_DEF void zpl_socket_close(zpl_socket socket) { | ||
# if defined(ZPL_SYSTEM_WINDOWS) | ||
closesocket(socket); | ||
# else | ||
close(socket); | ||
# endif | ||
} | ||
|
||
ZPL_DEF void zpl_socket_terminate(void) { | ||
# if defined(ZPL_SYSTEM_WINDOWS) | ||
WSACleanup(); | ||
# endif | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_listen(zpl_socket socket, zpl_i32 backlog) { | ||
return listen(socket, backlog); | ||
} | ||
|
||
ZPL_DEF zpl_socket zpl_socket_accept(zpl_socket socket, zpl_socket_addr *addr) { | ||
# if defined(ZPL_SYSTEM_WINDOWS) | ||
int len = sizeof(*addr); | ||
zpl_socket sock = (zpl_socket)accept(socket, (struct sockaddr *)addr, &len); | ||
if (sock == INVALID_SOCKET) { | ||
return -1; | ||
} | ||
if (sock > INT_MAX) { | ||
closesocket(sock); | ||
return -1; | ||
} | ||
# else | ||
socklen_t len = sizeof(*addr); | ||
zpl_socket sock = accept(socket, (struct sockaddr *)addr, &len); | ||
if (sock == -1) { | ||
return -1; | ||
} | ||
# endif | ||
return sock; | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_get_address(zpl_socket socket, zpl_socket_addr *addr) { | ||
return getsockname(socket, (struct sockaddr *)addr, (socklen_t *)sizeof(*addr)); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_get_address_info(zpl_socket_addr *addr, char *host, zpl_i32 host_size, char *service, zpl_i32 service_size) { | ||
return getnameinfo((struct sockaddr *)addr, (socklen_t)sizeof(*addr), host, host_size, service, service_size, 0); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_send(zpl_socket socket, const char *data, zpl_i32 size) { | ||
return send(socket, data, size, 0); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_receive(zpl_socket socket, char *buffer, zpl_i32 size) { | ||
return recv(socket, buffer, size, 0); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_send_to(zpl_socket socket, zpl_socket_addr *addr, const char *data, zpl_i32 size) { | ||
return sendto(socket, data, size, 0, (struct sockaddr *)addr, (socklen_t)sizeof(*addr)); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_receive_from(zpl_socket socket, zpl_socket_addr *addr, char *buffer, zpl_i32 size) { | ||
return recvfrom(socket, buffer, size, 0, (struct sockaddr *)addr, (socklen_t *)sizeof(*addr)); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_select(zpl_socket socket, zpl_f64 time) { | ||
fd_set fds; | ||
struct timeval tv; | ||
|
||
FD_ZERO(&fds); | ||
if (socket > -1) FD_SET(socket, &fds); | ||
|
||
tv.tv_sec = (long)time; | ||
tv.tv_usec = (long)((time - tv.tv_sec) * 1000000); | ||
|
||
return select(socket + 1, &fds, 0, 0, &tv); | ||
} | ||
|
||
ZPL_DEF zpl_i32 zpl_socket_multi_select(zpl_socket *sockets, zpl_i32 count, zpl_f64 time) { | ||
fd_set fds; | ||
struct timeval tv; | ||
zpl_i32 i, max = -1; | ||
|
||
FD_ZERO(&fds); | ||
for (i = 0; i < count; ++i) { | ||
if (sockets[i] > max) max = sockets[i]; | ||
if (sockets[i] > -1) FD_SET(sockets[i], &fds); | ||
} | ||
|
||
tv.tv_sec = (long)time; | ||
tv.tv_usec = (long)((time - tv.tv_sec) * 1000000); | ||
|
||
return select(max + 1, &fds, 0, 0, &tv); | ||
} | ||
|
||
ZPL_END_C_DECLS | ||
|
Oops, something went wrong.