From 126e818e2c0ba4ccfc512f62f0199c73fbdd2726 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Wed, 28 Jan 2026 23:12:42 +0100 Subject: [PATCH] b: new case, connect to client and receive->print his data --- build.bat | 2 +- src/a.cpp | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/b.cpp | 189 +++++++++++++++++++++++++++++++++++ src/basic.cpp | 38 ++++++- src/main.cpp | 1 - 5 files changed, 496 insertions(+), 3 deletions(-) create mode 100644 src/a.cpp create mode 100644 src/b.cpp diff --git a/build.bat b/build.bat index 71f6c9a..816cec2 100644 --- a/build.bat +++ b/build.bat @@ -2,5 +2,5 @@ mkdir build cd build -cl ../src/main.cpp -Fe:server.exe -FC -WX -W3 -wd4200 -diagnostics:column -nologo -Zi -D_CRT_SECURE_NO_WARNINGS +cl ../src/b.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 new file mode 100644 index 0000000..a89b958 --- /dev/null +++ b/src/a.cpp @@ -0,0 +1,269 @@ +/* +- [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 +#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); + +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_windows_error(WSAGetLastError())); + continue; + } + + int yes = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(int)) == -1) { + panicf("failed to setsockopt %s", serialize_windows_error(WSAGetLastError())); + } + + if (bind(socket_fd, it->ai_addr, (int)it->ai_addrlen) == -1) { + panicf("bind failed %s", serialize_windows_error(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_windows_error(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_windows_error(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_windows_error(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_windows_error(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_windows_error(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_windows_error(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; +} diff --git a/src/b.cpp b/src/b.cpp new file mode 100644 index 0000000..226f8a6 --- /dev/null +++ b/src/b.cpp @@ -0,0 +1,189 @@ +#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" + +#define SERVER_NAME NULL +#define SERVER_PORT "8000" + +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; +} + +void run_server() { + struct addrinfo *infos = {}; + struct addrinfo hints = {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + int err = getaddrinfo(SERVER_NAME, SERVER_PORT, &hints, &infos); + if (err != 0) { + panicf("server: getaddrinfo failed, error code: %d", gai_strerrorA(err)); + } + defer { freeaddrinfo(infos); }; + + SOCKET sock = -1; + for (addrinfo *it = infos; it; it = it->ai_next) { + sock = socket(it->ai_family, it->ai_socktype, it->ai_protocol); + if (sock == -1) { + debugf("server: skipping socket call: %s", wsa_error()); + continue; + } + + err = bind(sock, it->ai_addr, (int)it->ai_addrlen); + if (err != 0) { + debugf("server: bind failed: %s", wsa_error()); + sock = wrapped_close_socket(sock); + continue; + } + + debugf("server: bind to %s", serialize(it->ai_addr)); + break; + } + + if (sock == -1) { + panicf("server: failed to bind to a port!"); + } + defer { wrapped_close_socket(sock); }; + + int max_pending_connections = 10; + err = listen(sock, max_pending_connections); + if (err != 0) { panicf("server: listen failed: %s", wsa_error()); } + + sockaddr_storage client_sockaddr = {}; + int sockaddr_len = sizeof(client_sockaddr); + SOCKET client_sock = accept(sock, (sockaddr *)&client_sockaddr, &sockaddr_len); + if (client_sock == -1) { + panicf("server: failed to accept connection %s", wsa_error()); + } + defer { wrapped_close_socket(client_sock); }; + + for (;;) { + char buff[1024]; + int bytes = recv(client_sock, buff, lengthof(buff) - 1, 0); + if (bytes == -1) { + panicf("server: failed recv %s", wsa_error()); + } + debugf("server: %.*s", bytes, buff); + + if (bytes == 4 && buff[0] == 'q' && buff[1] == 'u' && buff[2] == 'i' && buff[3] == 't') { + break; + } + } + debugf("server: exiting"); +} + +void run_client() { + struct addrinfo *infos = {}; + struct addrinfo hints = {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + int err = getaddrinfo(SERVER_NAME, SERVER_PORT, &hints, &infos); + if (err != 0) { + panicf("client: getaddrinfo failed, error code: %d", gai_strerrorA(err)); + } + defer { freeaddrinfo(infos); }; + + SOCKET sock = -1; + for (addrinfo *it = infos; it; it = it->ai_next) { + sock = socket(it->ai_family, it->ai_socktype, it->ai_protocol); + if (sock == -1) { + debugf("client: skipping socket call: %s", wsa_error()); + continue; + } + + err = connect(sock, it->ai_addr, (int)it->ai_addrlen); + if (err != 0) { + debugf("client: failed to connect %s", wsa_error()); + sock = wrapped_close_socket(sock); + continue; + } + + break; + } + + if (sock == -1) { + panicf("client: failed to connect %s", wsa_error()); + } + defer { wrapped_close_socket(sock); }; + + for (;;) { + const char *buff = "hello world!"; + int buff_size = (int)strlen(buff); + int bytes = send(sock, buff, buff_size, 0); + // bytes can be less then buffer and that needs to be handled + // that is the remaining amount needs to be sent? + if (bytes == SOCKET_ERROR) { + debugf("client: failed to send %s", wsa_error()); + } + + } + +} + +struct Thread_Context { + int idx; +}; + +DWORD thread_entry(void *param) { + Thread_Context *ctx = (Thread_Context *)param; + if (ctx->idx == 0) { + run_server(); + } else { + run_client(); + } + return 0; +} + +int main() { + WSADATA wsaData; + int err = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (err != 0) { + panicf("WSAStartup failed: %s", serialize_windows_error(err)); + } + defer { WSACleanup(); }; + + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { + panicf("Version 2.2 of Winsock not available"); + } + + constexpr int thread_count = 2; + HANDLE thread_handles[thread_count] = {}; + DWORD thread_ids[thread_count] = {}; + Thread_Context thread_datas[thread_count] = {}; + for (int i = 0; i < thread_count; i += 1) { + Thread_Context *ctx = thread_datas + i; + ctx->idx = i; + LPSECURITY_ATTRIBUTES attribs = NULL; + thread_handles[i] = CreateThread(attribs, 0, thread_entry, thread_datas + i, 0, thread_ids + i); + } + WaitForMultipleObjects(thread_count, thread_handles, TRUE, INFINITE); + return 0; +} \ No newline at end of file diff --git a/src/basic.cpp b/src/basic.cpp index b499d68..2817892 100644 --- a/src/basic.cpp +++ b/src/basic.cpp @@ -1,3 +1,9 @@ +#if _WIN32 +#define OS_WINDOWS 1 +#else +#define OS_WINDOWS 0 +#endif + #define lengthof(x) (sizeof((x)) / sizeof((x)[0])) #define panicf(...) (fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, strf(__VA_ARGS__)), exit(1)) @@ -67,4 +73,34 @@ char *serialize_flags(Slice table, int n) { // @leak if (n & it->value) result = strf("%s %s", result, it->name); } return result; -} \ No newline at end of file +} + +#if OS_WINDOWS +char *serialize_windows_error(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 +} + +char *wsa_error() { + return serialize_windows_error(WSAGetLastError()); +} + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 3452134..b7d6152 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -174,7 +174,6 @@ void run_server() { } defer { closesocket(conn_sock); }; - // @todo: this might be a little wrong, also the win32 and unix seem to differ on field names in sockaddr 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));