aboutsummaryrefslogtreecommitdiff
path: root/client/helloworld/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/helloworld/net.c')
-rw-r--r--client/helloworld/net.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/client/helloworld/net.c b/client/helloworld/net.c
new file mode 100644
index 0000000..83192e0
--- /dev/null
+++ b/client/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 <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