263 lines
8.0 KiB
C++
263 lines
8.0 KiB
C++
/*
|
|
- [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 <windows.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#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<Enum_Value> 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<Enum_Value> 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<Enum_Value> 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<Enum_Value> 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;
|
|
}
|