diff --git a/build.bat b/build.bat index 816cec2..b32380d 100644 --- a/build.bat +++ b/build.bat @@ -2,5 +2,5 @@ mkdir build cd build -cl ../src/b.cpp -Fe:server.exe -FC -WX -W3 -wd4200 -diagnostics:column -nologo -Zi -D_CRT_SECURE_NO_WARNINGS +cl ../src/c.cpp -Fe:server.exe -FC -WX -W3 -wd4200 -diagnostics:column -nologo -Zi -D_CRT_SECURE_NO_WARNINGS cd .. \ No newline at end of file diff --git a/src/a.cpp b/src/a.cpp index a89b958..b0f8203 100644 --- a/src/a.cpp +++ b/src/a.cpp @@ -1,13 +1,6 @@ /* - [a] Create basic client code - [a] Create threads and put client code, server code on separate -- [ ] Connect to client and listen for messages - print them -- [ ] Create a chat server where you can prepend "client 0 to client 2: {msg}": and it will forward and print that message there - - [ ] type 'quit' or 'exit' to kill the app -- [ ] Write an http server and send some stuff to the browser! - - -https://beej.us/guide/bgnet/html/ */ #define WIN32_LEAN_AND_MEAN diff --git a/src/b.cpp b/src/b.cpp index 9ad3555..2994dc8 100644 --- a/src/b.cpp +++ b/src/b.cpp @@ -1,3 +1,7 @@ +/* +- [x] Allow for multiple clients connecting out of order +- [x] Non blocking, polling architecture +*/ #define WIN32_LEAN_AND_MEAN #define WIN32_NO_MIN_MAX #define NOMINMAX diff --git a/src/basic.cpp b/src/basic.cpp index 2817892..e4bb326 100644 --- a/src/basic.cpp +++ b/src/basic.cpp @@ -5,6 +5,8 @@ #endif #define lengthof(x) (sizeof((x)) / sizeof((x)[0])) +#define PLEN(x) {(x), lengthof(x)} +#define ENUM_VALUE(x) {x, #x} #define panicf(...) (fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, strf(__VA_ARGS__)), exit(1)) #define debugf(...) printf("%s:%d: %s\n", __FILE__, __LINE__, strf(__VA_ARGS__)) @@ -50,9 +52,6 @@ struct Slice { int len; }; -#define PLEN(x) {(x), lengthof(x)} -#define ENUM_VALUE(x) {x, #x} - struct Enum_Value { int value; const char *name; @@ -102,5 +101,30 @@ char *serialize_windows_error(DWORD errorCode) { char *wsa_error() { return serialize_windows_error(WSAGetLastError()); } +#endif -#endif \ No newline at end of file + +char *serialize(sockaddr *n) { + char s[INET6_ADDRSTRLEN]; + void *addr = NULL; + if (n->sa_family == AF_INET) { + sockaddr_in* ipv4 = (sockaddr_in*)n; + addr = &(ipv4->sin_addr); + } else if (n->sa_family == AF_INET6) { + sockaddr_in6* ipv6 = (sockaddr_in6*)n; + addr = &(ipv6->sin6_addr); + } else { + return ""; + } + const char *res = inet_ntop(n->sa_family, addr, s, sizeof(s)); + assert(res != NULL); + return strf("%s", s); +} + +int wrapped_close_socket(SOCKET socket) { + int err = closesocket(socket); + if (err == SOCKET_ERROR) { + panicf("server: failed to close socket: %s", wsa_error()); + } + return -1; +} \ No newline at end of file diff --git a/src/c.cpp b/src/c.cpp new file mode 100644 index 0000000..7148384 --- /dev/null +++ b/src/c.cpp @@ -0,0 +1,102 @@ +/* +- [ ] Create a chat server where you can prepend "client 0 to client 2: {msg}": and it will forward and print that message there. Needs a bit of thread synchronization .. + - [ ] type 'quit' or 'exit' to kill the app +- [ ] Write an http server and send some stuff to the browser! + +https://beej.us/guide/bgnet/html/ +*/ +#define WIN32_LEAN_AND_MEAN +#define WIN32_NO_MIN_MAX +#define NOMINMAX +#include +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "ws2_32") +#include "basic.cpp" + + +struct Thread_Data { + int idx; +}; + +struct Thread_Context { + int idx; +}; +#define THREAD_COUNT 8 +thread_local Thread_Context tctx; + +typedef void (*thread_task)(void *param); + +void add_task(thread_task *task, void *param = NULL) { + +} + +void task_print_val(void *param) { + int *value = (int *)param; + debugf("thread_idx: %d, value: %d", tctx.idx, *value); +} + +volatile int thread_value = -1; +void task_ask_user(void *param) { + printf("tell me! which thread would you like to ping? (GIVE ME INDEX)\n"); + int val = -1; + char data[32]; + for (;;) { + char *res = fgets(data, sizeof(data), stdin); + assert(res); + val = atoi(res); + bool inside = val >= 0 && val < THREAD_COUNT; + if (inside) { + break; + } + printf("retype, there are only %d threads!\n", THREAD_COUNT); + continue; + } + thread_value = val; +} + +DWORD thread_entry(void *param) { + Thread_Data *data = (Thread_Data *)param; + tctx.idx = data->idx; + debugf("thread: %d", data->idx); + + if (tctx.idx == 0) { + for (;;) { + task_ask_user(NULL); + } + } + + for (;;) { + if (thread_value == tctx.idx) { + debugf("thread: %d, value: %d", tctx.idx, thread_value); + thread_value = -1; + } + } + + return 0; +} + +int main() { + constexpr int thread_count = THREAD_COUNT - 1; // main thread is also incorporated + HANDLE thread_handles[thread_count] = {}; + Thread_Data thread_datas[thread_count] = {}; + DWORD thread_ids[thread_count] = {}; + int i = 0; + for (; i < thread_count; i += 1) { + thread_datas[i].idx = i; + thread_handles[i] = CreateThread(NULL, 0, thread_entry, thread_datas + i, 0, thread_ids + i); + assert(thread_handles[i] != NULL); + } + { + Thread_Data *ctx = thread_datas + i; + ctx->idx = i; + thread_entry((void *)ctx); + i += 1; + } + WaitForMultipleObjects(thread_count, thread_handles, TRUE, INFINITE); + return 0; +} diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index b7d6152..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* -- [x] Create basic client code -- [x] Create threads and put client code, server code on separate -- [ ] Create a chat server where you can prepend "client 0 to client 2: {msg}": and it will forward and print that message there - - [ ] type 'quit' or 'exit' to kill the app -- [ ] Write an http server and send some stuff to the browser! - - -https://beej.us/guide/bgnet/html/ - -*/ -#define WIN32_LEAN_AND_MEAN -#define WIN32_NO_MIN_MAX -#define NOMINMAX -#include -#include -#include -#include -#include -#pragma comment(lib, "ws2_32") -#include "basic.cpp" - -Enum_Value _ai_flags_table[] = { - ENUM_VALUE(AI_PASSIVE), - ENUM_VALUE(AI_CANONNAME), - ENUM_VALUE(AI_NUMERICHOST), -}; -Slice ai_flags_table = PLEN(_ai_flags_table); - -Enum_Value _ai_family_table[] = { - {0, "AF_UNSPEC"}, - {2, "AF_INET"}, - {17, "AF_NETBIOS"}, - {23, "AF_INET6"}, - {26, "AF_IRDA"}, - {32, "AF_BTH"}, -}; -Slice ai_family_table = PLEN(_ai_family_table); - -Enum_Value _ai_socktype_table[] = { - ENUM_VALUE(SOCK_STREAM), - ENUM_VALUE(SOCK_DGRAM), - ENUM_VALUE(SOCK_RAW), - ENUM_VALUE(SOCK_RDM), - ENUM_VALUE(SOCK_SEQPACKET), -}; -Slice ai_socktype_table = PLEN(_ai_socktype_table); - -Enum_Value _ai_protocol_table[] = { - ENUM_VALUE(IPPROTO_TCP), - ENUM_VALUE(IPPROTO_UDP), - ENUM_VALUE(IPPROTO_PGM), -}; -Slice ai_protocol_table = PLEN(_ai_protocol_table); - -char *serialize_error_code(DWORD errorCode) { - char* message = NULL; - - size_t size = FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&message, - 0, - NULL - ); - - if (size > 0) { - char* newline = strchr(message, '\r'); - if (newline) *newline = '\0'; - } - - return message; // @leak - LocalFree -} - -void serialize_addrinfo(struct addrinfo* in) { - for (struct addrinfo* p = in; p != NULL; p = p->ai_next) { - printf("addrinfo %p {\n", p); - printf(" ai_flags = %s(%d)\n", serialize_flags(ai_flags_table, p->ai_flags), p->ai_flags); - printf(" ai_family = %s(%d)\n", serialize(ai_family_table, p->ai_family), p->ai_family); - printf(" ai_socktype = %s(%d)\n", serialize(ai_socktype_table, p->ai_socktype), p->ai_socktype); - printf(" ai_protocol = %s(%d)\n", serialize(ai_protocol_table, p->ai_protocol), p->ai_protocol); - printf(" ai_addrlen = %zu\n", p->ai_addrlen); - printf(" ai_canonname = \"%s\"\n", p->ai_canonname ? p->ai_canonname : ""); - - const char* ipkind = ""; - void* addr = NULL; - if (p->ai_family == AF_INET) { - sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr; - addr = &(ipv4->sin_addr); - ipkind = "IPv4"; - } - else { - sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr; - addr = &(ipv6->sin6_addr); - ipkind = "IPv6"; - } - - char ipstr[INET6_ADDRSTRLEN]; - inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); - printf(" ai_addr = %s(%s)\n", ipkind, ipstr); - printf("}\n"); - } -} - -#define PORT "8000" - -void *get_in_addr(struct sockaddr *sa) -{ - if (sa->sa_family == AF_INET) { - return &(((struct sockaddr_in*)sa)->sin_addr); - } - - return &(((struct sockaddr_in6*)sa)->sin6_addr); -} - -void run_server() { - struct addrinfo *servinfo = {}; - struct addrinfo hints = {}; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; // tcp - hints.ai_flags = AI_PASSIVE; - // hints.ai_protocol = IPROTO_TCP; // tcp - INT err = getaddrinfo(NULL, PORT, &hints, &servinfo); - if (err != 0) { - panicf("getaddrinfo failed, error code: %s\n", gai_strerrorA(err)); - } - defer { freeaddrinfo(servinfo); }; - - SOCKET socket_fd = -1; - for (addrinfo *it = servinfo; it; it = it->ai_next) { - socket_fd = socket(it->ai_family, it->ai_socktype, it->ai_protocol); - if (socket_fd == -1) { - debugf("skipping socket call %s", serialize_error_code(WSAGetLastError())); - continue; - } - - int yes = 1; - if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(int)) == -1) { - panicf("failed to setsockopt %s", serialize_error_code(WSAGetLastError())); - } - - if (bind(socket_fd, it->ai_addr, (int)it->ai_addrlen) == -1) { - panicf("bind failed %s", serialize_error_code(WSAGetLastError())); - closesocket(socket_fd); - socket_fd = -1; - continue; - } - - break; - } - - if (socket_fd == -1) { - panicf("failed to bind"); - } - - int max_pending_connections = 10; - if (listen(socket_fd, max_pending_connections) == -1) { - panicf("listen failed %s", serialize_error_code(WSAGetLastError())); - } - - debugf("waiting for connections..."); - - sockaddr_storage their_addr = {}; - while (1) { - int their_addrlen = sizeof(their_addr); - SOCKET conn_sock = accept(socket_fd, (sockaddr *)&their_addr, &their_addrlen); - if (conn_sock == -1) { - debugf("failed to accept connection %s", serialize_error_code(WSAGetLastError())); - continue; - } - defer { closesocket(conn_sock); }; - - char s[INET6_ADDRSTRLEN]; - sockaddr *n = (sockaddr *)&their_addr; - inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof(s)); - debugf("server: got connection from: %s", s); - - const char *msg = "Hello world"; - int send_err_code = send(conn_sock, msg, (int)strlen(msg), 0); - if (send_err_code == -1) { - debugf("failed to send message %s", serialize_error_code(WSAGetLastError())); - } - } -} - - - -void run_client() { - struct addrinfo *servinfo = {}; - struct addrinfo hints = {}; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; // tcp - // hints.ai_flags = AI_PASSIVE; - // hints.ai_protocol = IPROTO_TCP; // tcp - INT err = getaddrinfo(NULL, PORT, &hints, &servinfo); - if (err != 0) { - panicf("client: getaddrinfo failed, error code: %s\n", gai_strerrorA(err)); - } - defer { freeaddrinfo(servinfo); }; - // serialize_addrinfo(servinfo); - - SOCKET socket_fd = -1; - for (addrinfo *it = servinfo; it; it = it->ai_next) { - socket_fd = socket(it->ai_family, it->ai_socktype, it->ai_protocol); - if (socket_fd == -1) { - debugf("skipping socket call %s", serialize_error_code(WSAGetLastError())); - continue; - } - - char s[INET6_ADDRSTRLEN]; - inet_ntop(it->ai_family, get_in_addr(it->ai_addr), s, sizeof(s)); - debugf("client: attempting connection to: %s", s); - - if (connect(socket_fd, it->ai_addr, (int)it->ai_addrlen) != 0) { - debugf("client: failed to connect %s", serialize_error_code(WSAGetLastError())); - closesocket(socket_fd); - socket_fd = -1; - continue; - } - - break; - } - - if (socket_fd == -1) { - panicf("client: failed to connect"); - } else { - debugf("client: connected!"); - } - - char buf[1024]; - int numbytes = recv(socket_fd, buf, lengthof(buf) - 1, 0); - if (numbytes == -1) { - panicf("client: recv failed %s", serialize_error_code(WSAGetLastError())); - } - - buf[numbytes] = '\0'; - debugf("client: received '%s'",buf); - closesocket(socket_fd); - -} - -struct Thread_Data { - int idx; -}; - -DWORD thread_entry(LPVOID param) { - Thread_Data *thread_data = (Thread_Data *)param; - printf("thread %d, reporting in!\n", thread_data->idx); - if (thread_data->idx == 0) { - run_server(); - } else { - run_client(); - } - printf("thread %d, exiting...\n", thread_data->idx); - return 0; -} - -int main() { - WSADATA wsaData; - - int err_wsa_startup = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (err_wsa_startup != 0) { - panicf("WSAStartup failed, error code: %d", err_wsa_startup); - } - defer { WSACleanup(); }; - - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { - panicf("Version 2.2 of Winsock not available"); - } - - constexpr int thread_count = 8; - HANDLE thread_handles[32]; - Thread_Data thread_data[32]; - DWORD thread_ids[32]; - static_assert(thread_count < lengthof(thread_ids)); - for (int i = 0; i < thread_count; i += 1) { - thread_data[i].idx = i; - LPSECURITY_ATTRIBUTES attribs = NULL; - SIZE_T stack_size = 0; - DWORD creation_flags = 0; - thread_handles[i] = CreateThread(attribs, stack_size, thread_entry, &thread_data[i], creation_flags, &thread_ids[i]); - } - WaitForMultipleObjects(thread_count, thread_handles, TRUE, INFINITE); - - // run_server(); - debugf("exiting..."); - return 0; -}