/* - [a] Create basic client code - [a] Create threads and put client code, server code on separate */ #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; }