From 197ff389dbd1e9bd0a5abac478a32cc02944fe63 Mon Sep 17 00:00:00 2001 From: Seth Hamilton Date: Sat, 13 Sep 2025 22:25:33 -0500 Subject: [PATCH] Copy over some MSDN code into tcp_client.h. Beginning of project --- .gitignore | 4 + activate.bat | 23 ++++ build.bat | 9 ++ export.bat | 20 +++ inc/sync.h | 310 +++++++++++++++++++++++++++++++++++++++++++++++ inc/tcp_client.h | 110 +++++++++++++++++ src/main.cpp | 7 ++ 7 files changed, 483 insertions(+) create mode 100644 .gitignore create mode 100644 activate.bat create mode 100644 build.bat create mode 100644 export.bat create mode 100644 inc/sync.h create mode 100644 inc/tcp_client.h create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54b8d5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +compile_commands.json +bin/** +obj/** +.cache/** diff --git a/activate.bat b/activate.bat new file mode 100644 index 0000000..53f0e81 --- /dev/null +++ b/activate.bat @@ -0,0 +1,23 @@ +FOR /r "C:\Program Files\Microsoft Visual Studio" %%G IN (*vcvars64.bat) DO ( + SET "_file=%%G" + SET "_comp=%_file:\VC\=%" + IF NOT "%_comp%" == "%_file%" Call "%_file%" +) + +FOR /r "C:\Program Files\Microsoft Visual Studio" %%G IN (*vcvars64.bat) DO ( + SET "_file=%%G" + SET "_comp=%_file:\VC\=%" + IF NOT "%_comp%" == "%_file%" Call "%_file%" +) + +FOR /r "C:\Program Files (x86)\Microsoft Visual Studio" %%G IN (*vcvars64.bat) DO ( + SET "_file=%%G" + SET "_comp=%_file:\VC\=%" + IF NOT "%_comp%" == "%_file%" Call "%_file%" +) + +FOR /r "C:\Program Files (x86)\Microsoft Visual Studio" %%G IN (*vcvars64.bat) DO ( + SET "_file=%%G" + SET "_comp=%_file:\VC\=%" + IF NOT "%_comp%" == "%_file%" Call "%_file%" +) diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..be61f18 --- /dev/null +++ b/build.bat @@ -0,0 +1,9 @@ +@ECHO off +SET flags=-Od -ZI +IF "%1" == "release" ( + SET flags=-O2 +) +@echo on +SET srcs=src\* +mkdir bin obj +cl %srcs% Ws2_32.lib -I inc %flags% -std:c++20 -MP -Fo:obj\\ -Fe:bin\\ diff --git a/export.bat b/export.bat new file mode 100644 index 0000000..d8b041a --- /dev/null +++ b/export.bat @@ -0,0 +1,20 @@ +SETLOCAL ENABLEDELAYEDEXPANSION + +Echo [ > compile_commands.json + +FOR /r "src\" %%F IN (*.cpp) DO ( + +SET "file=%%F" +SET "file=!file:\=/!" +SET "directory=%~dp0" +SET "directory=!directory:\=/!" + +ECHO { >> compile_commands.json +ECHO "directory": "!directory!", >> compile_commands.json +ECHO "command": "cl !file! Ws2_32.lib -I inc -std:c++20 -Fo:obj\\", >> compile_commands.json +ECHO "file": "!file!" >> compile_commands.json +ECHO }, >> compile_commands.json + +) + +ECHO ] >> compile_commands.json diff --git a/inc/sync.h b/inc/sync.h new file mode 100644 index 0000000..404665d --- /dev/null +++ b/inc/sync.h @@ -0,0 +1,310 @@ +#pragma once + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +namespace sync { + +#ifdef _WIN32 +typedef CRITICAL_SECTION Mutex; +typedef CONDITION_VARIABLE ConditionVar; +typedef HANDLE Semaphore; +typedef HANDLE Thread; +typedef LARGE_INTEGER TimeSpan; +typedef DWORD (WINAPI *ThreadFunc)(_In_ LPVOID lpParameter); +typedef LPVOID ThreadArg; + +const TimeSpan infinite_ts = { .QuadPart=LLONG_MAX }; + +int get_num_cores() { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +} + +const int num_cores = get_num_cores(); + +LARGE_INTEGER _init_freq() { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + return freq; +} + +static LARGE_INTEGER freq = _init_freq(); +#endif + +Thread make_thread(ThreadFunc t, ThreadArg a); +Thread make_thread(ThreadFunc t, ThreadArg a, int core_affinity); +void join(Thread t); +void sleep(TimeSpan ts); +void allow_all_processors(); +void set_affinity(Thread &t, int core); +void set_affinity(int core); +int get_affinity(); + +Mutex make_mutex(); +void lock(Mutex &m); +bool trylock(Mutex &m); +void unlock(Mutex &m); +void dispose(Mutex &m); + +ConditionVar make_condition_var(); +void wait(ConditionVar &c, Mutex &m, TimeSpan ts); +void wake_one(ConditionVar &c); +void wake_all(ConditionVar &c); +void dispose(ConditionVar &c); + +Semaphore make_semaphore(int initial, int max); +void wait(Semaphore &s); +void post(Semaphore &s); +void dispose(Semaphore &s); + +TimeSpan from_ms(double milliseconds); +TimeSpan from_s(double seconds); +TimeSpan from_min(double minutes); +TimeSpan from_hours(double hours); +TimeSpan now(); +TimeSpan operator-(const TimeSpan &a, const TimeSpan &b); +TimeSpan operator+(const TimeSpan &a, const TimeSpan &b); +TimeSpan operator*(const TimeSpan &a, const TimeSpan &b); +TimeSpan operator/(const TimeSpan &a, const TimeSpan &b); + +double to_ms(TimeSpan &ts); +double to_s(TimeSpan &ts); +double to_min(TimeSpan &ts); +double to_hours(TimeSpan &ts); + +#ifdef _WIN32 + +uint64_t bitmask (unsigned short n) { + if (n == 64) return -((uint64_t)1); + return (((uint64_t) 1) << n) - 1; +} + +const int tab64[64] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5}; + +int log2_64 (uint64_t value) +{ + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58]; +} + +Thread make_thread(ThreadFunc f, ThreadArg a) { + DWORD tid; + return CreateThread(NULL, 0, f, a, 0, &tid); +} + +struct DummyThreadArgs { + int core_affinity; + ThreadFunc f; + ThreadArg a; +}; + +DWORD _dummy_thread(LPVOID a) { + DummyThreadArgs *wrap = static_cast(a); + set_affinity(wrap->core_affinity); + return wrap->f(wrap->a); +} + +Thread make_thread(ThreadFunc f, ThreadArg a, int core_affinity) { + DWORD tid; + DummyThreadArgs *args = (DummyThreadArgs*)malloc(sizeof(DummyThreadArgs)); + *args = { + .core_affinity=core_affinity, + .f=f, + .a=a + }; + return CreateThread(NULL, 0, _dummy_thread, args, 0, &tid); +} + +void join(Thread t) { + WaitForSingleObject(t, INFINITE); +} + +void sleep(TimeSpan ts) { + Sleep(static_cast(to_ms(ts))); +} + +void allow_all_processors() { + Thread t = GetCurrentThread(); + DWORD affinity = bitmask(num_cores); + SetProcessAffinityMask(t, affinity); +} + +void set_affinity(Thread &t, int core) { + DWORD mask = 1 << (core % num_cores); + DWORD old = SetThreadAffinityMask(t, mask); + DWORD confirm = SetThreadAffinityMask(t, mask); + assert(old && GetLastError() != ERROR_INVALID_PARAMETER && mask == confirm); +} + +void set_affinity(int core) { + Thread cur = GetCurrentThread(); + set_affinity(cur, core); +} + +int get_affinity() { + Thread t = GetCurrentThread(); + DWORD mask = 1; + DWORD affinity = SetThreadAffinityMask(t, (DWORD_PTR)mask); + DWORD check = SetThreadAffinityMask(t, (DWORD_PTR)affinity); + assert(check == mask); + return log2_64(affinity); +} + +Mutex make_mutex() { + Mutex m; + InitializeCriticalSection(&m); + return m; +} + +void lock(Mutex &m) { + EnterCriticalSection(&m); +} + +bool trylock(Mutex &m) { + return TryEnterCriticalSection(&m); +} + +void unlock(Mutex &m) { + LeaveCriticalSection(&m); +} + +void dispose(Mutex &m) { + DeleteCriticalSection(&m); +} + +ConditionVar make_condition_var() { + ConditionVar c; + InitializeConditionVariable(&c); + return c; +} + +void wait(ConditionVar &c, Mutex &m, TimeSpan ts) { + if (ts.QuadPart == infinite_ts.QuadPart) { + SleepConditionVariableCS(&c, &m, INFINITE); + } else { + SleepConditionVariableCS(&c, &m, static_cast(to_ms(ts))); + } +} + +void wake_one(ConditionVar &c) { + WakeConditionVariable(&c); +} + +void wake_all(ConditionVar &c) { + WakeAllConditionVariable(&c); +} + +void dispose(ConditionVar &c) { + return; // Windows doesn't have a delete condition variable func +} + +Semaphore make_semaphore(int initial, int max) { + return CreateSemaphoreA(NULL, (long)initial, (long)max, NULL); +} + +void wait(Semaphore &s) { + WaitForSingleObject(s, INFINITE); +} + +void post(Semaphore &s) { + ReleaseSemaphore(s, 1, NULL); +} + +void dispose(Semaphore &s) { + CloseHandle(s); +} + +TimeSpan from_ms(double milliseconds) { + TimeSpan ts; + ts.QuadPart = static_cast(milliseconds/1000.0)*freq.QuadPart; + return ts; +} + +TimeSpan from_s(double seconds) { + TimeSpan ts; + ts.QuadPart = static_cast(seconds)*freq.QuadPart; + return ts; +} + +TimeSpan from_min(double minutes) { + TimeSpan ts; + ts.QuadPart = static_cast(minutes*60.0)*freq.QuadPart; + return ts; +} + +TimeSpan from_hours(double hours) { + TimeSpan ts; + ts.QuadPart = static_cast(hours*60.0*60.0)*freq.QuadPart; + return ts; +} + +TimeSpan now() { + TimeSpan ts; + QueryPerformanceCounter(&ts); + return ts; +} + +TimeSpan operator-(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart - b.QuadPart; + return ts; +} + +TimeSpan operator+(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart + b.QuadPart; + return ts; +} + +TimeSpan operator*(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart * b.QuadPart; + return ts; +} + +TimeSpan operator/(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart / b.QuadPart; + return ts; +} + +double to_ms(TimeSpan &ts) { + return static_cast(ts.QuadPart*1000)/static_cast(freq.QuadPart); +} + +double to_s(TimeSpan &ts) { + return static_cast(ts.QuadPart)/static_cast(freq.QuadPart); +} + +double to_min(TimeSpan &ts) { + return static_cast(ts.QuadPart)/static_cast(freq.QuadPart*60); +} + +double to_hours(TimeSpan &ts) { + return static_cast(ts.QuadPart)/static_cast(freq.QuadPart*60*60); +} + +#endif + +} // namespace sync +// diff --git a/inc/tcp_client.h b/inc/tcp_client.h new file mode 100644 index 0000000..7b05d31 --- /dev/null +++ b/inc/tcp_client.h @@ -0,0 +1,110 @@ +#pragma once + +#ifdef _WIN32 +#include +#include +#include +#endif + +#ifdef _WIN32 +typedef SOCKET Socket; +#endif + +typedef unsigned char byte; + +namespace tcp { + +Socket make_socket(); +bool connect(Socket& s, const char* hostname, int port); +bool disconnect(Socket &s); +bool send_data(Socket& s, byte* buf, int len); +bool get_data(Socket& s, byte* out_buf, int buf_size, int& out_num_written); + +#ifdef _WIN32 + +Socket make_socket() { return INVALID_SOCKET; } + +bool connect(Socket& s, const char *hostname, int port) { + addrinfo *result, *ptr, hints; + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + char port_str[128]; + sprintf(port_str, "%d", port); + int code = getaddrinfo(hostname, port_str, &hints, &result); + if (code != 0) { + printf("getaddrinfo failed: %d\n", code); + WSACleanup(); + return false; + } + + s = INVALID_SOCKET; + ptr = result; + s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + if (s == INVALID_SOCKET) { + printf("Error at socket(): %ld\n", WSAGetLastError()); + freeaddrinfo(result); + WSACleanup(); + return false; + } + + // Iterate through the linked list of addresses returned by getaddrinfo + do { + code = connect(s, ptr->ai_addr, (int)ptr->ai_addrlen); + if (code == SOCKET_ERROR) { + closesocket(s); + s = INVALID_SOCKET; + } + ptr = ptr->ai_next; + } while (ptr); + + freeaddrinfo(result); + + if (s == INVALID_SOCKET) { + printf("Unable to connect to %s:%d\n", hostname, port); + WSACleanup(); + return 1; + } + + return true; +} + +bool disconnect(Socket &s) { + int code = shutdown(s, SD_BOTH); + if (code == SOCKET_ERROR) { + printf("disconnect failed: %d\n", WSAGetLastError()); + closesocket(s); + WSACleanup(); + } + return code != SOCKET_ERROR; +} + +// TODO: Retries? Should this be a param? +bool send_data(Socket &s, byte *buf, int len) { + int code = send(s, (char*)buf, len, 0); + if (code == SOCKET_ERROR) { + printf("send_data failed: %d\n", WSAGetLastError()); + closesocket(s); + WSACleanup(); + } + return code != SOCKET_ERROR; +} + +bool get_data(Socket &s, byte *out_buf, int buf_size, int &out_num_written) { + int code = recv(s, (char*)out_buf, buf_size, 0); + if (code >= 0) { + out_num_written = code; + } else { + printf("get_data failed: %d\n", WSAGetLastError()); + } + return code >= 0; +} + +#endif + + +} // namespace tcp + + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..7ab1dae --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,7 @@ +#include "tcp_client.h" + +using namespace tcp; + +int main() { + Socket s = make_socket(); +}