Compare commits

..

5 Commits

Author SHA1 Message Date
Krzosa Karol
4dcd51845e Update 2026-01-31 10:03:44 +01:00
Krzosa Karol
2f049101c2 Polling and handling connections + messages at the same time 2026-01-29 10:30:41 +01:00
Krzosa Karol
126e818e2c b: new case, connect to client and receive->print his data 2026-01-28 23:12:42 +01:00
Krzosa Karol
eaa3eab826 modify the checklist 2026-01-28 19:06:00 +01:00
Krzosa Karol
6e4264c486 fix printing incoming ips on server side 2026-01-28 08:41:46 +01:00
5 changed files with 434 additions and 49 deletions

View File

@@ -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/c.cpp -Fe:server.exe -FC -WX -W3 -wd4200 -diagnostics:column -nologo -Zi -D_CRT_SECURE_NO_WARNINGS
cd ..

View File

@@ -1,6 +1,6 @@
/*
- [ ] Create threads and put client code, server code on separate
- [ ] Create client code
- [a] Create basic client code
- [a] Create threads and put client code, server code on separate
*/
#define WIN32_LEAN_AND_MEAN
@@ -47,29 +47,6 @@ Enum_Value _ai_protocol_table[] = {
};
Slice<Enum_Value> ai_protocol_table = PLEN(_ai_protocol_table);
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; // @leak - LocalFree
}
void serialize_addrinfo(struct addrinfo* in) {
for (struct addrinfo* p = in; p != NULL; p = p->ai_next) {
printf("addrinfo %p {\n", p);
@@ -102,6 +79,15 @@ void serialize_addrinfo(struct addrinfo* in) {
#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 = {};
@@ -114,23 +100,22 @@ void run_server() {
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()));
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_error_code(WSAGetLastError()));
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_error_code(WSAGetLastError()));
panicf("bind failed %s", serialize_windows_error(WSAGetLastError()));
closesocket(socket_fd);
socket_fd = -1;
continue;
@@ -145,7 +130,7 @@ void run_server() {
int max_pending_connections = 10;
if (listen(socket_fd, max_pending_connections) == -1) {
panicf("listen failed %s", serialize_error_code(WSAGetLastError()));
panicf("listen failed %s", serialize_windows_error(WSAGetLastError()));
}
debugf("waiting for connections...");
@@ -155,33 +140,25 @@ void run_server() {
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()));
debugf("failed to accept connection %s", serialize_windows_error(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));
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_error_code(WSAGetLastError()));
debugf("failed to send message %s", serialize_windows_error(WSAGetLastError()));
}
}
}
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_client() {
struct addrinfo *servinfo = {};
@@ -201,7 +178,7 @@ void run_client() {
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()));
debugf("skipping socket call %s", serialize_windows_error(WSAGetLastError()));
continue;
}
@@ -210,7 +187,7 @@ void run_client() {
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_error_code(WSAGetLastError()));
debugf("client: failed to connect %s", serialize_windows_error(WSAGetLastError()));
closesocket(socket_fd);
socket_fd = -1;
continue;
@@ -228,7 +205,7 @@ void run_client() {
char buf[1024];
int numbytes = recv(socket_fd, buf, lengthof(buf) - 1, 0);
if (numbytes == -1) {
panicf("client: recv failed %s", serialize_error_code(WSAGetLastError()));
panicf("client: recv failed %s", serialize_windows_error(WSAGetLastError()));
}
buf[numbytes] = '\0';
@@ -255,7 +232,6 @@ DWORD thread_entry(LPVOID param) {
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);

247
src/b.cpp Normal file
View 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;
}

View File

@@ -1,4 +1,12 @@
#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__))
@@ -44,9 +52,6 @@ struct Slice {
int len;
};
#define PLEN(x) {(x), lengthof(x)}
#define ENUM_VALUE(x) {x, #x}
struct Enum_Value {
int value;
const char *name;
@@ -68,3 +73,58 @@ char *serialize_flags(Slice<Enum_Value> table, int n) { // @leak
}
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
View 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;
}