Compare commits
7 Commits
7dcc185c32
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dcd51845e | ||
|
|
2f049101c2 | ||
|
|
126e818e2c | ||
|
|
eaa3eab826 | ||
|
|
6e4264c486 | ||
|
|
d62bebee78 | ||
|
|
d1304b64d4 |
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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/c.cpp -Fe:server.exe -FC -WX -W3 -wd4200 -diagnostics:column -nologo -Zi -D_CRT_SECURE_NO_WARNINGS
|
||||||
cd ..
|
cd ..
|
||||||
262
src/a.cpp
Normal file
262
src/a.cpp
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
- [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;
|
||||||
|
}
|
||||||
247
src/b.cpp
Normal file
247
src/b.cpp
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
- [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
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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 "<unhandled_case>";
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
110
src/basic.cpp
110
src/basic.cpp
@@ -1,5 +1,16 @@
|
|||||||
#define panicf(...) (fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n"), exit(1))
|
#if _WIN32
|
||||||
|
#define OS_WINDOWS 1
|
||||||
|
#else
|
||||||
|
#define OS_WINDOWS 0
|
||||||
|
#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__))
|
||||||
|
#define assert(x) ((!(x)) && (panicf("%s:%d: assertion failed " #x, __FILE__, __LINE__), 0))
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct DEFER_ExitScope {
|
struct DEFER_ExitScope {
|
||||||
T lambda;
|
T lambda;
|
||||||
@@ -20,3 +31,100 @@ class DEFER_ExitScopeHelp {
|
|||||||
#define DEFER_CONCAT_INTERNAL(x, y) x##y
|
#define DEFER_CONCAT_INTERNAL(x, y) x##y
|
||||||
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
|
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
|
||||||
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
|
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
|
||||||
|
|
||||||
|
char* strf(const char* format, ...) { // @leak
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
va_list args_copy;
|
||||||
|
va_copy(args_copy, args);
|
||||||
|
int size = vsnprintf(NULL, 0, format, args_copy) + 1;
|
||||||
|
va_end(args_copy);
|
||||||
|
char* buffer = (char *)malloc(size);
|
||||||
|
assert(buffer);
|
||||||
|
vsnprintf(buffer, size, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct Slice {
|
||||||
|
T *data;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Enum_Value {
|
||||||
|
int value;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *serialize(Slice<Enum_Value> table, int value) {
|
||||||
|
for (int i = 0; i < table.len; i += 1) {
|
||||||
|
Enum_Value *it = table.data + i;
|
||||||
|
if (it->value == value) return it->name;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *serialize_flags(Slice<Enum_Value> table, int n) { // @leak
|
||||||
|
char *result = strf("%d", n);
|
||||||
|
for (int i = 0; i < table.len; i += 1) {
|
||||||
|
auto *it = table.data + i;
|
||||||
|
if (n & it->value) result = strf("%s %s", result, it->name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
|
||||||
|
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 "<unhandled_case>";
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
102
src/c.cpp
Normal file
102
src/c.cpp
Normal file
@@ -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 <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
266
src/main.cpp
266
src/main.cpp
@@ -1,266 +0,0 @@
|
|||||||
/*
|
|
||||||
- [ ] Create threads and put client code, server code on separate
|
|
||||||
- [ ] Create client code
|
|
||||||
|
|
||||||
*/
|
|
||||||
#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")
|
|
||||||
|
|
||||||
#define panicf(...) (fprintf(stderr, "%s:%d: ", __FILE__, __LINE__), fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n"), exit(1))
|
|
||||||
#define debugf(...) (printf("%s:%d: ", __FILE__, __LINE__), printf(__VA_ARGS__), printf("\n"))
|
|
||||||
#define assert(x) ((!(x)) && (panicf("%s:%d: assertion failed " #x, __FILE__, __LINE__), 0))
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct DEFER_ExitScope {
|
|
||||||
T lambda;
|
|
||||||
DEFER_ExitScope(T lambda) : lambda(lambda) {}
|
|
||||||
~DEFER_ExitScope() { lambda(); }
|
|
||||||
DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};
|
|
||||||
|
|
||||||
private:
|
|
||||||
DEFER_ExitScope &operator=(const DEFER_ExitScope &);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DEFER_ExitScopeHelp {
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
DEFER_ExitScope<T> operator+(T t) { return t; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DEFER_CONCAT_INTERNAL(x, y) x##y
|
|
||||||
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
|
|
||||||
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
|
|
||||||
|
|
||||||
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; // Remember to LocalFree() this later!
|
|
||||||
}
|
|
||||||
|
|
||||||
char* strf(const char* format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
va_list args_copy;
|
|
||||||
va_copy(args_copy, args);
|
|
||||||
int size = vsnprintf(NULL, 0, format, args_copy) + 1;
|
|
||||||
va_end(args_copy);
|
|
||||||
char* buffer = (char *)malloc(size);
|
|
||||||
assert(buffer);
|
|
||||||
vsnprintf(buffer, size, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define lengthof(x) (sizeof((x)) / sizeof((x)[0]))
|
|
||||||
|
|
||||||
struct EnumValue {
|
|
||||||
int value;
|
|
||||||
const char *name;
|
|
||||||
};
|
|
||||||
#define ENUM_VALUE(x) {x, #x}
|
|
||||||
const char *serialize(EnumValue *table, int table_size, int value) {
|
|
||||||
for (int i = 0; i < table_size; i += 1) {
|
|
||||||
EnumValue *it = table + i;
|
|
||||||
if (it->value == value) return it->name;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
EnumValue ai_flag_table[] = {
|
|
||||||
ENUM_VALUE(AI_PASSIVE),
|
|
||||||
ENUM_VALUE(AI_CANONNAME),
|
|
||||||
ENUM_VALUE(AI_NUMERICHOST),
|
|
||||||
};
|
|
||||||
|
|
||||||
char *serialize_ai_flags(int n) {
|
|
||||||
char *result = strf("%d", n);
|
|
||||||
for (int i = 0; i < lengthof(ai_flag_table); i += 1) {
|
|
||||||
auto *it = ai_flag_table + i;
|
|
||||||
if (n & it->value) result = strf("%s %s", result, it->name);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct {
|
|
||||||
const char *name;
|
|
||||||
int value;
|
|
||||||
const char *desc;
|
|
||||||
} ai_family_table[] = {
|
|
||||||
{"AF_UNSPEC", 0, "The address family is unspecified"},
|
|
||||||
{"AF_INET", 2, "The Internet Protocol version 4 (IPv4) address family."},
|
|
||||||
{"AF_NETBIOS", 17, "The NetBIOS address family. This address family is only supported if a Windows Sockets provider for NetBIOS is installed."},
|
|
||||||
{"AF_INET6", 23, "The Internet Protocol version 6 (IPv6) address family."},
|
|
||||||
{"AF_IRDA", 26, "The Infrared Data Association (IrDA) address family. This address family is only supported if the computer has an infrared port and driver installed."},
|
|
||||||
{"AF_BTH", 32, "The Bluetooth address family. This address family is only supported if a Bluetooth adapter is installed on Windows Server 2003 or later."},
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *serialize_ai_family(int n) {
|
|
||||||
for (int i = 0; i < lengthof(ai_family_table); i += 1) {
|
|
||||||
auto *it = ai_family_table + i;
|
|
||||||
if (it->value == n) {
|
|
||||||
return it->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EnumValue ai_socktype_table[] = {
|
|
||||||
ENUM_VALUE(SOCK_STREAM),
|
|
||||||
ENUM_VALUE(SOCK_DGRAM),
|
|
||||||
ENUM_VALUE(SOCK_RAW),
|
|
||||||
ENUM_VALUE(SOCK_RDM),
|
|
||||||
ENUM_VALUE(SOCK_SEQPACKET),
|
|
||||||
};
|
|
||||||
|
|
||||||
EnumValue ai_protocol_table[] = {
|
|
||||||
ENUM_VALUE(IPPROTO_TCP),
|
|
||||||
ENUM_VALUE(IPPROTO_UDP),
|
|
||||||
ENUM_VALUE(IPPROTO_PGM),
|
|
||||||
};
|
|
||||||
|
|
||||||
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_ai_flags(p->ai_flags), p->ai_flags);
|
|
||||||
printf(" ai_family = %s(%d)\n", serialize_ai_family(p->ai_family), p->ai_family);
|
|
||||||
printf(" ai_socktype = %s(%d)\n", serialize(ai_socktype_table, lengthof(ai_socktype_table), p->ai_socktype), p->ai_socktype);
|
|
||||||
printf(" ai_protocol = %s(%d)\n", serialize(ai_protocol_table, lengthof(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 run_server() {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
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); };
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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); };
|
|
||||||
|
|
||||||
// @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, n->sa_data, 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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
run_server();
|
|
||||||
debugf("exiting...");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user