diff options
Diffstat (limited to 'client/libacron/acronc')
-rw-r--r-- | client/libacron/acronc/main.c | 142 | ||||
-rw-r--r-- | client/libacron/acronc/net.c | 181 | ||||
-rw-r--r-- | client/libacron/acronc/net.h | 45 |
3 files changed, 331 insertions, 37 deletions
diff --git a/client/libacron/acronc/main.c b/client/libacron/acronc/main.c index f76b172..43802ad 100644 --- a/client/libacron/acronc/main.c +++ b/client/libacron/acronc/main.c @@ -3,15 +3,16 @@ */ #include "libac.h" +#include "net.h" #include <stdio.h> -#ifdef WIN32 -#include <winsock2.h> -#include <windef.h> -#else + +#ifndef WIN32 + #include <signal.h> #include <errno.h> #include <string.h> + #endif static const char *world_name(const enum ac_world world) { @@ -126,11 +127,47 @@ static void handle_response(const ac_response_t *response) { } #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; + } #ifdef WIN32 fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); #else @@ -139,7 +176,7 @@ int main(int argc, char **argv) { sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) || - sigaction(SIGTERM, &sa, NULL)) { + sigaction(SIGTERM, &sa, NULL)) { const int e = errno; fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", strerror(e), @@ -147,20 +184,6 @@ int main(int argc, char **argv) { return e; } #endif -#ifdef WIN32 - 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 libac_config_t config = { #ifdef DEBUG .out = stderr, @@ -171,33 +194,69 @@ int main(int argc, char **argv) { }; 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 + .version = 0, + .sock = NULL, + .on_send = on_send }; - if ((r = ac_connect(parameters, &connection))) { + 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; } - ac_obj_t *obj; - 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))) { - ac_disconnect(connection); + parameters.sock = &sock; + if ((r = ac_connect(parameters, &connection))) { + net_close(&sock); ac_free(); + net_free(); return r; } - while (!(r = ac_receive(connection, &obj, 10))) { + 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 = ac_receive(connection, buffer, bytes, &obj))) { + goto end; + } + if (!ready) { + /* Wait until ready. */ + if ((r = ac_get_state(connection, &state))) { + goto end; + } + switch (state) { + case AC_STATE_INIT: + continue; + case AC_STATE_READY: + printf("Connection is established.\n"); + ready = true; + say(connection); + continue; + default: + fprintf(stderr, "Unexpected state.\n"); + goto end; + } + } + if (!obj) { continue; } @@ -208,10 +267,19 @@ int main(int argc, char **argv) { } 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); + } + } ac_disconnect(connection); + net_close(&sock); ac_free(); -#ifdef WIN32 - WSACleanup(); -#endif + net_free(); return r; }
\ No newline at end of file diff --git a/client/libacron/acronc/net.c b/client/libacron/acronc/net.c new file mode 100644 index 0000000..83192e0 --- /dev/null +++ b/client/libacron/acronc/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 <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#ifdef WIN32 +#include <windows.h> /* For DWORD */ +#include <winsock2.h> +#include <ws2tcpip.h> + +#define errno_sock WSAGetLastError() + +#else + +#include <sys/socket.h> +#include <netdb.h> +#include <unistd.h> +#include <sys/time.h> + +#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 new file mode 100644 index 0000000..1a49709 --- /dev/null +++ b/client/libacron/acronc/net.h @@ -0,0 +1,45 @@ +/* + * Created by yuuta on 7/23/22. + */ + +#ifndef ACRONC_NET_H +#define ACRONC_NET_H + +#include <stdint.h> +#include <stddef.h> + +#ifdef WIN32 +#include <winsock2.h> + +#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 */ |