From df85b736cea32bd04e4a80042b9b3c67642d57ba Mon Sep 17 00:00:00 2001 From: Trumeet Date: Sat, 23 Jul 2022 13:29:24 -0700 Subject: refactor(acronc): rename acronc to helloworld Signed-off-by: Trumeet --- client/libacron/CMakeLists.txt | 15 +- client/libacron/README.md | 10 +- client/libacron/acronc/main.c | 370 --------------------------------- client/libacron/acronc/net.c | 181 ---------------- client/libacron/acronc/net.h | 45 ---- client/libacron/apps/helloworld/main.c | 370 +++++++++++++++++++++++++++++++++ client/libacron/apps/helloworld/net.c | 181 ++++++++++++++++ client/libacron/apps/helloworld/net.h | 45 ++++ 8 files changed, 609 insertions(+), 608 deletions(-) delete mode 100644 client/libacron/acronc/main.c delete mode 100644 client/libacron/acronc/net.c delete mode 100644 client/libacron/acronc/net.h create mode 100644 client/libacron/apps/helloworld/main.c create mode 100644 client/libacron/apps/helloworld/net.c create mode 100644 client/libacron/apps/helloworld/net.h diff --git a/client/libacron/CMakeLists.txt b/client/libacron/CMakeLists.txt index 223b259..c87063e 100644 --- a/client/libacron/CMakeLists.txt +++ b/client/libacron/CMakeLists.txt @@ -89,18 +89,19 @@ target_link_libraries(ac ${LIBAC_DEPS}) target_include_directories(ac-static PUBLIC ${LIBAC_INCLUDES}) target_link_libraries(ac-static ${LIBAC_DEPS}) -add_executable(acronc - acronc/main.c - acronc/net.c - acronc/net.h) +add_executable(helloworld + apps/helloworld/main.c + apps/helloworld/net.c + apps/helloworld/net.h + ) if(WIN32) set(ACRONC_DEPS ws2_32) endif() set(ACRONC_DEPS ${ACRON_DEPS} ac) -target_link_libraries(acronc ac) -target_include_directories(acronc PUBLIC "${PROJECT_BINARY_DIR}" include/) +target_link_libraries(helloworld ac) +target_include_directories(helloworld PUBLIC "${PROJECT_BINARY_DIR}" include/) -install(TARGETS ac ac-static acronc +install(TARGETS ac ac-static helloworld EXPORT ${PROJECT_NAME}-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/client/libacron/README.md b/client/libacron/README.md index d52a4c3..4514e21 100644 --- a/client/libacron/README.md +++ b/client/libacron/README.md @@ -177,8 +177,8 @@ The client also needs to supply its own `on_send` function. libac will call that needs to write anything to the socket. When calling that function, the socket pointer will be passed as-is. -No worries, `acronc/net.c` provides a ready-to-use example of cross-platform (Unix and Windows) blocking -socket client. See `acronc/main.c` for more details. +No worries, `apps/helloworld/net.c` provides a ready-to-use example of cross-platform (Unix and Windows) blocking +socket client. See `apps/helloworld/main.c` for more details. ```c void *connection = NULL; @@ -231,7 +231,7 @@ The program can make requests using `ac_request()`: ```c ac_config_t conf = { - .name = "acronc" + .name = "helloworld" }; ac_request_cmd_t req = { .type = AC_REQUEST_CMD, @@ -252,7 +252,7 @@ The client must manually close its socket after calling `ac_disconnect()`. > on the roadmap. -Read more: `acronc/main.c` example client. +Read more: `apps/helloworld/main.c` example client. #### Connection Threading @@ -261,7 +261,7 @@ it is the caller's responsibility to guard the whole connection with a mutex. li provide that because this may limit the flexibility. After locking, it is safe to call the connection-related functions from any threads. -Example `acronc/main.c` has a naive cross-platform example of how to guard the connection using +Example `apps/helloworld/main.c` has a naive cross-platform example of how to guard the connection using a mutex. ## Roadmap diff --git a/client/libacron/acronc/main.c b/client/libacron/acronc/main.c deleted file mode 100644 index dfd1668..0000000 --- a/client/libacron/acronc/main.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Created by yuuta on 7/20/22. - */ - -#include "libac.h" -#include "net.h" - -#include - -#ifndef WIN32 - -#include -#include -#include - -#endif - -#ifdef __STDC_NO_THREADS__ -# ifdef WIN32 -#define THREAD_WIN32 -#include -static HANDLE mtx_conn; - -int lock(void) { \ -DWORD _dwlck = WaitForSingleObject(mtx_conn, 0); \ -if (_dwlck == WAIT_FAILED) { _dwlck = GetLastError(); } \ -if (_dwlck) { fprintf(stderr, "Cannot lock the mutex: %d.\n", _dwlck); return _dwlck; } \ -return 0; \ -} - -int unlock(void) { \ -if (!ReleaseMutex(mtx_conn)) { \ -int r = GetLastError(); \ -fprintf(stderr, "Cannot release the mutex: %d.\n", r); \ -return r; \ -} \ -return 0; \ -} - -# else -#error "Either C11 threading or Win32 API is required for concurrency." -# endif -#else - -#define THREAD_C11 - -#include - -static mtx_t mtx_conn; - -int lock(void) { - \ -if (mtx_lock(&mtx_conn) != thrd_success) { - \ -fprintf(stderr, "Cannot lock the mutex.\n"); \ -return 1; \ - - } \ -return 0; \ - -} - -int unlock(void) { - \ -if (mtx_unlock(&mtx_conn) != thrd_success) { - \ -fprintf(stderr, "Cannot release the mutex.\n"); \ -return 1; \ - - } \ -return 0; \ - -} - -#endif - -static const char *world_name(const enum ac_world world) { - switch (world) { - case overworld: - return "overworld"; - case nether: - return "nether"; - case end: - return "end"; - default: - return "unknown world"; - } -} - -static void handle_event(const ac_event_t *event) { - switch (event->type) { - case AC_EVENT_LAGGING: { - ac_event_lagging_t *o = (ac_event_lagging_t *) event; - printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", - o->ms, - o->ticks); - break; - } - case AC_EVENT_ENTITY_DEATH: { - ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; - printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", - o->entity.name, - world_name(o->entity.world), - o->entity.pos.x, - o->entity.pos.y, - o->entity.pos.z, - o->message); - break; - } - case AC_EVENT_PLAYER_MESSAGE: { - ac_event_player_message_t *o = (ac_event_player_message_t *) event; - printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z, - o->text); - break; - } - case AC_EVENT_PLAYER_DISCONNECT: { - ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; - printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z, - o->reason); - break; - } - case AC_EVENT_PLAYER_JOIN: { - ac_event_player_join_t *o = (ac_event_player_join_t *) event; - printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z); - break; - } - default: { - printf("Received an unrecognized event of type '%u'.\n", - event->type); - } - } -} - -static void handle_response(const ac_response_t *response) { - switch (response->type) { - case AC_RESPONSE_OK: { - ac_response_ok_t *o = (ac_response_ok_t *) response; - printf("Request %d OK.\n", - o->id); - break; - } - case AC_RESPONSE_ERROR: { - ac_response_error_t *o = (ac_response_error_t *) response; - printf("Request %d failed: %s (%d).\n", - o->id, - o->message, - o->code); - break; - } - case AC_RESPONSE_CMD_OUT: { - ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; - printf("Request %d output by %s: %s.\n", - o->id, - o->sender, - o->out); - break; - } - case AC_RESPONSE_CMD_RESULT: { - ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; - printf("Request %d is done: %s (%d).\n", - o->id, - o->success ? "Success" : "Failed", - o->result); - break; - } - default: { - printf("Received an unrecognized response of type '%u'.\n", - response->type); - } - } -} - -#ifndef WIN32 - -static void intr(int signum) {} - -#endif - -static int on_send(const void *sock, - const void *buffer, - const size_t len) { - int r; - printf("Writing %lu bytes.\n", len); - if ((r = net_send(sock, buffer, len))) { - fprintf(stderr, "Cannot write %lu bytes to server: %s (%d).\n", - len, - net_strerror(r), - r); - } - return r; -} - -static int say(void *connection) { - int r; - ac_config_t conf = { - .name = "acronc" - }; - ac_request_cmd_t req = { - .type = AC_REQUEST_CMD, - .id = 100, - .config = &conf, - .cmd = "say Hi" - }; - if ((r = ac_request(connection, (ac_request_t *) &req))) { - return r; - } - return 0; -} - -int main(int argc, char **argv) { - int r; - if ((r = net_init())) { - return r; - } -#if defined(THREAD_WIN32) - if (!(mtx_conn = CreateMutex(NULL, TRUE, NULL))) { - r = GetLastError(); - fprintf(stderr, "Cannot create mutex: %d.\n", r); - return r; - } -#elif defined(THREAD_C11) - if (mtx_init(&mtx_conn, mtx_plain) != thrd_success) { - fprintf(stderr, "Cannot create mutex.\n"); - return 1; - } -#endif - -#ifdef WIN32 - fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); -#else - struct sigaction sa; - sa.sa_handler = intr; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGINT, &sa, NULL) || - sigaction(SIGTERM, &sa, NULL)) { - const int e = errno; - fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", - strerror(e), - e); - return e; - } -#endif - libac_config_t config = { -#ifdef DEBUG - .out = stderr, -#else - .out = NULL, -#endif - .tok = NULL - }; - if ((r = ac_init(&config))) return r; - void *connection; - SOCKET sock; - ac_connection_parameters_t parameters = { - .host = "localhost", - .port = 25575, - .id = "1", - .token = "123", - .version = 0, - .sock = NULL, - .on_send = on_send - }; - if ((r = net_connect(parameters.host, parameters.port, &sock))) { - fprintf(stderr, "Cannot connect to server: %s (%d).\n", - net_strerror(r), - r); - ac_free(); - net_free(); - return r; - } - parameters.sock = &sock; - if ((r = ac_connect(parameters, &connection))) { - net_close(&sock); - ac_free(); - net_free(); - return r; - } - ac_obj_t *obj; - int nr; - uint8_t buffer[1000U]; - size_t bytes; - enum ac_connection_state state; - printf("Waiting until the connection is established.\n"); - bool ready = false; - while (1) { - if ((nr = net_read(&sock, buffer, sizeof(buffer), &bytes, 10))) { - if (nr == NET_TIMEOUT) { - printf("Receive timeout.\n"); - continue; - } else { - goto end; - } - } - if ((r = lock())) { goto end; } - if ((r = ac_receive(connection, buffer, bytes, &obj))) { - if ((r = unlock())) { goto end; } - goto end; - } - if (!ready) { - /* Wait until ready. */ - if ((r = ac_get_state(connection, &state))) { - if ((r = unlock())) { goto end; } - goto end; - } - switch (state) { - case AC_STATE_INIT: - continue; - case AC_STATE_READY: - printf("Connection is established.\n"); - ready = true; - say(connection); - if ((r = unlock())) { goto end; } - continue; - default: - fprintf(stderr, "Unexpected state.\n"); - if ((r = unlock())) { goto end; } - goto end; - } - } - if ((r = unlock())) { goto end; } - - if (!obj) { - continue; - } - if (AC_IS_EVENT(obj->type)) { - handle_event((ac_event_t *) obj); - } else if (AC_IS_RESPONSE(obj->type)) { - handle_response((ac_response_t *) obj); - } - ac_object_free(obj); - } - end: - if (nr) { - if (nr == NET_CLOSED) { - fprintf(stderr, "Connection is closed while reading from the server.\n"); - } else { - fprintf(stderr, "Cannot receive from server: %s (%d).\n", - net_strerror(nr), - nr); - } - } - if ((r = lock())) { goto end; } - ac_disconnect(connection); - if ((r = unlock())) { goto end; } - net_close(&sock); - ac_free(); - net_free(); -#if defined(THREAD_WIN32) - CloseHandle(mtx_conn); -#elif defined(THREAD_C11) - mtx_destroy(&mtx_conn); -#endif - return r; -} \ No newline at end of file diff --git a/client/libacron/acronc/net.c b/client/libacron/acronc/net.c deleted file mode 100644 index 83192e0..0000000 --- a/client/libacron/acronc/net.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Created by yuuta on 7/22/22. - * - * A simple cross-platform (Unix and Windows) implementation of a socket client. - */ - -#include "net.h" - -#include -#include -#include -#include - -#ifdef WIN32 -#include /* For DWORD */ -#include -#include - -#define errno_sock WSAGetLastError() - -#else - -#include -#include -#include -#include - -#define closesocket(socket) close(socket) -#define errno_sock errno - -#endif - -int net_connect(const char *host, - const uint16_t port, - SOCKET *sock) { - int r; - struct addrinfo *res; - char service[6]; - - snprintf(service, 6, "%u", port); - if ((r = getaddrinfo(host, service, NULL, &res))) { - return r; - } - - SOCKET fd; - if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { - const int e = errno_sock; - freeaddrinfo(res); - return e; - } - if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { - const int e = errno_sock; - closesocket(fd); - freeaddrinfo(res); - return e; - } - freeaddrinfo(res); - *sock = fd; - return 0; -} - -int net_read(const SOCKET *sock, - void *buffer, - const size_t length, - size_t *read, - const unsigned int timeout) { - -#ifdef WIN32 - DWORD to = timeout * 1000; - if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { -#else - struct timeval tv; - tv.tv_sec = timeout; - tv.tv_usec = 0; - if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { -#endif - return errno_sock; - } - - int bytes; - if ((bytes = (int) recv(*sock, buffer, length, 0)) <= 0) { - if (bytes < 0) { - const int e = errno_sock; -#ifdef WIN32 - if (e == WSAETIMEDOUT) { -#else - if (e == EAGAIN) { -#endif - return NET_TIMEOUT; - } - return e; - } - return NET_CLOSED; - } - *read = bytes; - return 0; -} - -int net_send(const SOCKET *sock, - const void *buffer, - size_t len) { - size_t pos; - const uint8_t *ptr = buffer; - int retval; - - for (pos = 0U; pos < len; pos += retval) { - size_t n = len - pos; - retval = (int) send(*sock, &ptr[pos], (int) n, 0); - if (retval <= 0) { - return errno_sock; - } - } - return 0; -} - -int net_init(void) { -#ifdef WIN32 - int r; - WORD wVersionRequested; - WSADATA wsaData; - wVersionRequested = MAKEWORD(2, 2); - if ((r = WSAStartup(wVersionRequested, &wsaData))) { - fprintf(stderr, "WSAStartup failed with error: %d\n", r); - return r; - } - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { - fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); - WSACleanup(); - return 1; - } -#endif - return 0; -} - -void net_close(const SOCKET *sock) { - closesocket(*sock); -} - -#ifdef WIN32 -static __declspec( thread ) LPTSTR err_buf = NULL; -#else -static _Thread_local char err_buf[1024]; -#endif - -void net_free(void) { -#ifdef WIN32 - WSACleanup(); - if (err_buf) { - LocalFree(err_buf); - err_buf = NULL; - } -#endif -} - -char *net_strerror(int errnum) { -#ifdef WIN32 - if (err_buf) { - LocalFree(err_buf); - err_buf = NULL; - } - FormatMessage( - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - errnum, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &err_buf, - 0, - NULL); - if (!err_buf) { - return "Unknown error"; - } -#else - int r; - if ((r = strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) { - snprintf(err_buf, 1024, "%d (%d)", errnum, r); - } -#endif - return err_buf; -} \ No newline at end of file diff --git a/client/libacron/acronc/net.h b/client/libacron/acronc/net.h deleted file mode 100644 index 1a49709..0000000 --- a/client/libacron/acronc/net.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Created by yuuta on 7/23/22. - */ - -#ifndef ACRONC_NET_H -#define ACRONC_NET_H - -#include -#include - -#ifdef WIN32 -#include - -#else - -#define SOCKET int - -#endif - -#define NET_CLOSED -2 -#define NET_TIMEOUT -1 - -int net_connect(const char *host, - uint16_t port, - SOCKET *sock); - -int net_read(const SOCKET *sock, - void *buffer, - size_t length, - size_t *read, - unsigned int timeout); - -int net_send(const SOCKET *sock, - const void *buffer, - size_t len); - -void net_close(const SOCKET *sock); - -int net_init(void); - -void net_free(void); - -char *net_strerror(int errnum); - -#endif /* ACRONC_NET_H */ diff --git a/client/libacron/apps/helloworld/main.c b/client/libacron/apps/helloworld/main.c new file mode 100644 index 0000000..6201fac --- /dev/null +++ b/client/libacron/apps/helloworld/main.c @@ -0,0 +1,370 @@ +/* + * Created by yuuta on 7/20/22. + */ + +#include "libac.h" +#include "net.h" + +#include + +#ifndef WIN32 + +#include +#include +#include + +#endif + +#ifdef __STDC_NO_THREADS__ +# ifdef WIN32 +#define THREAD_WIN32 +#include +static HANDLE mtx_conn; + +int lock(void) { \ +DWORD _dwlck = WaitForSingleObject(mtx_conn, 0); \ +if (_dwlck == WAIT_FAILED) { _dwlck = GetLastError(); } \ +if (_dwlck) { fprintf(stderr, "Cannot lock the mutex: %d.\n", _dwlck); return _dwlck; } \ +return 0; \ +} + +int unlock(void) { \ +if (!ReleaseMutex(mtx_conn)) { \ +int r = GetLastError(); \ +fprintf(stderr, "Cannot release the mutex: %d.\n", r); \ +return r; \ +} \ +return 0; \ +} + +# else +#error "Either C11 threading or Win32 API is required for concurrency." +# endif +#else + +#define THREAD_C11 + +#include + +static mtx_t mtx_conn; + +int lock(void) { + \ +if (mtx_lock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot lock the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +int unlock(void) { + \ +if (mtx_unlock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot release the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +#endif + +static const char *world_name(const enum ac_world world) { + switch (world) { + case overworld: + return "overworld"; + case nether: + return "nether"; + case end: + return "end"; + default: + return "unknown world"; + } +} + +static void handle_event(const ac_event_t *event) { + switch (event->type) { + case AC_EVENT_LAGGING: { + ac_event_lagging_t *o = (ac_event_lagging_t *) event; + printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", + o->ms, + o->ticks); + break; + } + case AC_EVENT_ENTITY_DEATH: { + ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; + printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", + o->entity.name, + world_name(o->entity.world), + o->entity.pos.x, + o->entity.pos.y, + o->entity.pos.z, + o->message); + break; + } + case AC_EVENT_PLAYER_MESSAGE: { + ac_event_player_message_t *o = (ac_event_player_message_t *) event; + printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->text); + break; + } + case AC_EVENT_PLAYER_DISCONNECT: { + ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; + printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->reason); + break; + } + case AC_EVENT_PLAYER_JOIN: { + ac_event_player_join_t *o = (ac_event_player_join_t *) event; + printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z); + break; + } + default: { + printf("Received an unrecognized event of type '%u'.\n", + event->type); + } + } +} + +static void handle_response(const ac_response_t *response) { + switch (response->type) { + case AC_RESPONSE_OK: { + ac_response_ok_t *o = (ac_response_ok_t *) response; + printf("Request %d OK.\n", + o->id); + break; + } + case AC_RESPONSE_ERROR: { + ac_response_error_t *o = (ac_response_error_t *) response; + printf("Request %d failed: %s (%d).\n", + o->id, + o->message, + o->code); + break; + } + case AC_RESPONSE_CMD_OUT: { + ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; + printf("Request %d output by %s: %s.\n", + o->id, + o->sender, + o->out); + break; + } + case AC_RESPONSE_CMD_RESULT: { + ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; + printf("Request %d is done: %s (%d).\n", + o->id, + o->success ? "Success" : "Failed", + o->result); + break; + } + default: { + printf("Received an unrecognized response of type '%u'.\n", + response->type); + } + } +} + +#ifndef WIN32 + +static void intr(int signum) {} + +#endif + +static int on_send(const void *sock, + const void *buffer, + const size_t len) { + int r; + printf("Writing %lu bytes.\n", len); + if ((r = net_send(sock, buffer, len))) { + fprintf(stderr, "Cannot write %lu bytes to server: %s (%d).\n", + len, + net_strerror(r), + r); + } + return r; +} + +static int say(void *connection) { + int r; + ac_config_t conf = { + .name = "helloworld" + }; + ac_request_cmd_t req = { + .type = AC_REQUEST_CMD, + .id = 100, + .config = &conf, + .cmd = "say Hi" + }; + if ((r = ac_request(connection, (ac_request_t *) &req))) { + return r; + } + return 0; +} + +int main(int argc, char **argv) { + int r; + if ((r = net_init())) { + return r; + } +#if defined(THREAD_WIN32) + if (!(mtx_conn = CreateMutex(NULL, TRUE, NULL))) { + r = GetLastError(); + fprintf(stderr, "Cannot create mutex: %d.\n", r); + return r; + } +#elif defined(THREAD_C11) + if (mtx_init(&mtx_conn, mtx_plain) != thrd_success) { + fprintf(stderr, "Cannot create mutex.\n"); + return 1; + } +#endif + +#ifdef WIN32 + fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); +#else + struct sigaction sa; + sa.sa_handler = intr; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGINT, &sa, NULL) || + sigaction(SIGTERM, &sa, NULL)) { + const int e = errno; + fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", + strerror(e), + e); + return e; + } +#endif + libac_config_t config = { +#ifdef DEBUG + .out = stderr, +#else + .out = NULL, +#endif + .tok = NULL + }; + if ((r = ac_init(&config))) return r; + void *connection; + SOCKET sock; + ac_connection_parameters_t parameters = { + .host = "localhost", + .port = 25575, + .id = "1", + .token = "123", + .version = 0, + .sock = NULL, + .on_send = on_send + }; + if ((r = net_connect(parameters.host, parameters.port, &sock))) { + fprintf(stderr, "Cannot connect to server: %s (%d).\n", + net_strerror(r), + r); + ac_free(); + net_free(); + return r; + } + parameters.sock = &sock; + if ((r = ac_connect(parameters, &connection))) { + net_close(&sock); + ac_free(); + net_free(); + return r; + } + ac_obj_t *obj; + int nr; + uint8_t buffer[1000U]; + size_t bytes; + enum ac_connection_state state; + printf("Waiting until the connection is established.\n"); + bool ready = false; + while (1) { + if ((nr = net_read(&sock, buffer, sizeof(buffer), &bytes, 10))) { + if (nr == NET_TIMEOUT) { + printf("Receive timeout.\n"); + continue; + } else { + goto end; + } + } + if ((r = lock())) { goto end; } + if ((r = ac_receive(connection, buffer, bytes, &obj))) { + if ((r = unlock())) { goto end; } + goto end; + } + if (!ready) { + /* Wait until ready. */ + if ((r = ac_get_state(connection, &state))) { + if ((r = unlock())) { goto end; } + goto end; + } + switch (state) { + case AC_STATE_INIT: + continue; + case AC_STATE_READY: + printf("Connection is established.\n"); + ready = true; + say(connection); + if ((r = unlock())) { goto end; } + continue; + default: + fprintf(stderr, "Unexpected state.\n"); + if ((r = unlock())) { goto end; } + goto end; + } + } + if ((r = unlock())) { goto end; } + + if (!obj) { + continue; + } + if (AC_IS_EVENT(obj->type)) { + handle_event((ac_event_t *) obj); + } else if (AC_IS_RESPONSE(obj->type)) { + handle_response((ac_response_t *) obj); + } + ac_object_free(obj); + } + end: + if (nr) { + if (nr == NET_CLOSED) { + fprintf(stderr, "Connection is closed while reading from the server.\n"); + } else { + fprintf(stderr, "Cannot receive from server: %s (%d).\n", + net_strerror(nr), + nr); + } + } + if ((r = lock())) { goto end; } + ac_disconnect(connection); + if ((r = unlock())) { goto end; } + net_close(&sock); + ac_free(); + net_free(); +#if defined(THREAD_WIN32) + CloseHandle(mtx_conn); +#elif defined(THREAD_C11) + mtx_destroy(&mtx_conn); +#endif + return r; +} \ No newline at end of file diff --git a/client/libacron/apps/helloworld/net.c b/client/libacron/apps/helloworld/net.c new file mode 100644 index 0000000..83192e0 --- /dev/null +++ b/client/libacron/apps/helloworld/net.c @@ -0,0 +1,181 @@ +/* + * Created by yuuta on 7/22/22. + * + * A simple cross-platform (Unix and Windows) implementation of a socket client. + */ + +#include "net.h" + +#include +#include +#include +#include + +#ifdef WIN32 +#include /* For DWORD */ +#include +#include + +#define errno_sock WSAGetLastError() + +#else + +#include +#include +#include +#include + +#define closesocket(socket) close(socket) +#define errno_sock errno + +#endif + +int net_connect(const char *host, + const uint16_t port, + SOCKET *sock) { + int r; + struct addrinfo *res; + char service[6]; + + snprintf(service, 6, "%u", port); + if ((r = getaddrinfo(host, service, NULL, &res))) { + return r; + } + + SOCKET fd; + if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { + const int e = errno_sock; + freeaddrinfo(res); + return e; + } + if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { + const int e = errno_sock; + closesocket(fd); + freeaddrinfo(res); + return e; + } + freeaddrinfo(res); + *sock = fd; + return 0; +} + +int net_read(const SOCKET *sock, + void *buffer, + const size_t length, + size_t *read, + const unsigned int timeout) { + +#ifdef WIN32 + DWORD to = timeout * 1000; + if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { +#else + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { +#endif + return errno_sock; + } + + int bytes; + if ((bytes = (int) recv(*sock, buffer, length, 0)) <= 0) { + if (bytes < 0) { + const int e = errno_sock; +#ifdef WIN32 + if (e == WSAETIMEDOUT) { +#else + if (e == EAGAIN) { +#endif + return NET_TIMEOUT; + } + return e; + } + return NET_CLOSED; + } + *read = bytes; + return 0; +} + +int net_send(const SOCKET *sock, + const void *buffer, + size_t len) { + size_t pos; + const uint8_t *ptr = buffer; + int retval; + + for (pos = 0U; pos < len; pos += retval) { + size_t n = len - pos; + retval = (int) send(*sock, &ptr[pos], (int) n, 0); + if (retval <= 0) { + return errno_sock; + } + } + return 0; +} + +int net_init(void) { +#ifdef WIN32 + int r; + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + if ((r = WSAStartup(wVersionRequested, &wsaData))) { + fprintf(stderr, "WSAStartup failed with error: %d\n", r); + return r; + } + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { + fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); + WSACleanup(); + return 1; + } +#endif + return 0; +} + +void net_close(const SOCKET *sock) { + closesocket(*sock); +} + +#ifdef WIN32 +static __declspec( thread ) LPTSTR err_buf = NULL; +#else +static _Thread_local char err_buf[1024]; +#endif + +void net_free(void) { +#ifdef WIN32 + WSACleanup(); + if (err_buf) { + LocalFree(err_buf); + err_buf = NULL; + } +#endif +} + +char *net_strerror(int errnum) { +#ifdef WIN32 + if (err_buf) { + LocalFree(err_buf); + err_buf = NULL; + } + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errnum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &err_buf, + 0, + NULL); + if (!err_buf) { + return "Unknown error"; + } +#else + int r; + if ((r = strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) { + snprintf(err_buf, 1024, "%d (%d)", errnum, r); + } +#endif + return err_buf; +} \ No newline at end of file diff --git a/client/libacron/apps/helloworld/net.h b/client/libacron/apps/helloworld/net.h new file mode 100644 index 0000000..1a49709 --- /dev/null +++ b/client/libacron/apps/helloworld/net.h @@ -0,0 +1,45 @@ +/* + * Created by yuuta on 7/23/22. + */ + +#ifndef ACRONC_NET_H +#define ACRONC_NET_H + +#include +#include + +#ifdef WIN32 +#include + +#else + +#define SOCKET int + +#endif + +#define NET_CLOSED -2 +#define NET_TIMEOUT -1 + +int net_connect(const char *host, + uint16_t port, + SOCKET *sock); + +int net_read(const SOCKET *sock, + void *buffer, + size_t length, + size_t *read, + unsigned int timeout); + +int net_send(const SOCKET *sock, + const void *buffer, + size_t len); + +void net_close(const SOCKET *sock); + +int net_init(void); + +void net_free(void); + +char *net_strerror(int errnum); + +#endif /* ACRONC_NET_H */ -- cgit v1.2.3