#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 set_non_blocking(SOCKET sock) { u_long mode = 1; int err = ioctlsocket(sock, FIONBIO, &mode); if (err != 0) { panicf("server: failed to set nonblocking mode %s", wsa_error()); } } void set_no_delay(SOCKET sock) { // Disable Nagle's algorithm, this would combine some of the packets together and these would // get received on server side in one combined buffer. Two stacked 'hello world', the horror! // This only finally worked when I set it on ALL the sockets, including the one in client. int nodelay = 0; int err = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); if (err != 0) { panicf("server: failed to setsockopt %s", wsa_error()); } } 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); }; set_no_delay(sock); set_non_blocking(sock); int max_pending_connections = 10; err = listen(sock, max_pending_connections); if (err != 0) { panicf("server: listen failed: %s", wsa_error()); } WSAPOLLFD pfds[256] = {}; int pfd_count = 0; { pfds[pfd_count].fd = sock; pfds[pfd_count].events = POLLIN; pfd_count += 1; } for (;;) { int timeout = -1; int poll_count = WSAPoll(pfds, pfd_count, timeout); if (poll_count == -1) { panicf("server: failed to poll %s", wsa_error()); } for (int i = 0; i < pfd_count; i += 1) { bool ok = pfds[i].revents & (POLLIN | POLLHUP); if (!ok) continue; // Handle new connection or receive data if (pfds[i].fd == sock) { sockaddr_storage addr = {}; int sockaddr_len = sizeof(addr); SOCKET client_sock = accept(sock, (sockaddr *)&addr, &sockaddr_len); if (client_sock == -1) { panicf("server: failed to accept connection %s", wsa_error()); } set_no_delay(client_sock); pfds[i].fd = client_sock; pfds[i].events = POLLIN; pfds[i].revents = 0; pfd_count += 1; debugf("server: new connection from: %s", serialize((sockaddr *)&addr)); } else { char buff[1024]; int bytes = recv(pfds[i].fd, 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); }; set_no_delay(sock); for (;;) { const char *buff = "hello world!"; int buff_size = (int)strlen(buff); debugf("client: sending %s", buff); int bytes = send(sock, buff, buff_size, 0); debugf("bytes sent: %d", bytes); // 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 = 8; 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; }