implementing server
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
|||||||
build/
|
build/
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
:OpenCode
|
:OpenCode
|
||||||
:Set BinaryUnderDebug "C:/handmade_http/build/server.exe"
|
:Set BinaryUnderDebug "C:/dev/practice_sockets/build/server.exe"
|
||||||
|
|||||||
213
src/main.cpp
213
src/main.cpp
@@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
- [ ] Setup so that both client and server are in one program?
|
||||||
|
- [ ] I would like to make it nicely visible in debugger, I don't want to switch etc.
|
||||||
|
|
||||||
|
*/
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_NO_MIN_MAX
|
#define WIN32_NO_MIN_MAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
@@ -6,11 +11,34 @@
|
|||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "basic.cpp"
|
|
||||||
|
|
||||||
#pragma comment(lib, "ws2_32")
|
#pragma comment(lib, "ws2_32")
|
||||||
|
|
||||||
char *win32_alloc_error_string(DWORD errorCode) {
|
#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;
|
char* message = NULL;
|
||||||
|
|
||||||
size_t size = FormatMessageA(
|
size_t size = FormatMessageA(
|
||||||
@@ -33,76 +61,181 @@ char *win32_alloc_error_string(DWORD errorCode) {
|
|||||||
return message; // Remember to LocalFree() this later!
|
return message; // Remember to LocalFree() this later!
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize_addrinfo(struct addrinfo *in) {
|
char* strf(const char* format, ...) {
|
||||||
for (struct addrinfo *p = in; p != NULL; p = p->ai_next) {
|
va_list args;
|
||||||
const char *ipkind = "";
|
va_start(args, format);
|
||||||
void *addr = NULL;
|
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) {
|
if (p->ai_family == AF_INET) {
|
||||||
sockaddr_in *ipv4 = (sockaddr_in *)p->ai_addr;
|
sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr;
|
||||||
addr = &(ipv4->sin_addr);
|
addr = &(ipv4->sin_addr);
|
||||||
ipkind = "IPv4";
|
ipkind = "IPv4";
|
||||||
} else {
|
}
|
||||||
sockaddr_in6 *ipv6 = (sockaddr_in6 *)p->ai_addr;
|
else {
|
||||||
|
sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr;
|
||||||
addr = &(ipv6->sin6_addr);
|
addr = &(ipv6->sin6_addr);
|
||||||
ipkind = "IPv6";
|
ipkind = "IPv6";
|
||||||
}
|
}
|
||||||
|
|
||||||
char ipstr[INET6_ADDRSTRLEN];
|
char ipstr[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
||||||
printf(" %s: %s\n", ipkind, ipstr);
|
printf(" ai_addr = %s(%s)\n", ipkind, ipstr);
|
||||||
|
printf("}\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PORT "8000"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
|
|
||||||
int err_wsa_startup = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
int err_wsa_startup = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
if (err_wsa_startup != 0) {
|
if (err_wsa_startup != 0) {
|
||||||
panicf("WSAStartup failed, error code: %d\n", err_wsa_startup);
|
panicf("WSAStartup failed, error code: %d", err_wsa_startup);
|
||||||
}
|
}
|
||||||
defer { WSACleanup(); };
|
defer { WSACleanup(); };
|
||||||
|
|
||||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
||||||
panicf("Version 2.2 of Winsock not available.\n");
|
panicf("Version 2.2 of Winsock not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct addrinfo *servinfo = {};
|
struct addrinfo *servinfo = {};
|
||||||
struct addrinfo hints = {};
|
struct addrinfo hints = {};
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM; // tcp
|
||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
INT err = getaddrinfo(NULL, "8000", &hints, &servinfo);
|
// hints.ai_protocol = IPROTO_TCP; // tcp
|
||||||
|
INT err = getaddrinfo(NULL, PORT, &hints, &servinfo);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
panicf("getaddrinfo failed, error code: %s\n", gai_strerrorA(err));
|
panicf("getaddrinfo failed, error code: %s\n", gai_strerrorA(err));
|
||||||
}
|
}
|
||||||
defer { freeaddrinfo(servinfo); };
|
defer { freeaddrinfo(servinfo); };
|
||||||
serialize_addrinfo(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;
|
||||||
|
}
|
||||||
|
|
||||||
printf("exiting...\n");
|
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...");
|
||||||
|
|
||||||
|
debugf("exiting...");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
int getaddrinfo(const char *node, // e.g. "www.example.com" or IP
|
|
||||||
const char *service, // e.g. "http" or port number
|
|
||||||
const struct addrinfo *hints,
|
|
||||||
struct addrinfo **res);
|
|
||||||
|
|
||||||
struct addrinfo {
|
|
||||||
int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
|
|
||||||
int ai_family; // AF_INET, AF_INET6, AF_UNSPEC
|
|
||||||
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
|
|
||||||
int ai_protocol; // use 0 for "any"
|
|
||||||
size_t ai_addrlen; // size of ai_addr in bytes
|
|
||||||
struct sockaddr *ai_addr; // struct sockaddr_in or _in6
|
|
||||||
char *ai_canonname; // full canonical hostname
|
|
||||||
|
|
||||||
struct addrinfo *ai_next; // linked list, next node
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sockaddr {
|
|
||||||
unsigned short sa_family; // address family, AF_xxx
|
|
||||||
char sa_data[14]; // 14 bytes of protocol address
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user