From 986f67d0309aae98e203750f9c60d3e4c2eabfee Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:22:22 +0100 Subject: [PATCH 01/56] start of basics collection --- basics.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 basics.md diff --git a/basics.md b/basics.md new file mode 100644 index 0000000..9ba231a --- /dev/null +++ b/basics.md @@ -0,0 +1,40 @@ + +#### struct timeval +- VSC +- GCC + +```cpp +struct timeval +{ + long tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; +``` +#### fd_set / select +- VSC +- GCC + + +```cpp +// assign socket (file descriptor) to fd_set +FD_SET(int fd, fd_set *fdset); +// clear socket (file descriptor) to fd_set +FD_CLR(int fd, fd_set *fdset); +FD_ISSET(int fd, fd_set *fdset); +FD_ZERO(fd_set *fdset); +``` + +```cpp +// return: number of ready sockets +// 0: timeout reached +// <0: error +int select( + int nfds, // historical, unused + fd_set* readfds, // [in, out] + fd_set* writefds, // [in, out] + fd_set* exceptfds, // [in, out] + const timeval * timeout // [in] +); +``` + + From e11a1f2264ec3cf082b69ac6baf22202af8a4eba Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:38:48 +0100 Subject: [PATCH 02/56] start of basics collection --- basics.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/basics.md b/basics.md index 9ba231a..4e4f738 100644 --- a/basics.md +++ b/basics.md @@ -14,14 +14,15 @@ struct timeval - VSC - GCC - ```cpp +// reset fd_set +FD_ZERO(fd_set *fdset); // assign socket (file descriptor) to fd_set FD_SET(int fd, fd_set *fdset); -// clear socket (file descriptor) to fd_set -FD_CLR(int fd, fd_set *fdset); +// check if socket has event FD_ISSET(int fd, fd_set *fdset); -FD_ZERO(fd_set *fdset); +// clear socket (file descriptor) in fd_set +FD_CLR(int fd, fd_set *fdset); ``` ```cpp @@ -36,5 +37,24 @@ int select( const timeval * timeout // [in] ); ``` - +### server socket +``` + socket() + | + bind() + | + listen() + | +-> FD_ZERO() +| | +| FD_SET() +| | +| select() +| | +| FD_ISSET() +| | | +| |N |Y +|______| accept() -> client socket thread +|____________| +``` From 9138767470a7713971f79b912347b520e771b4c1 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:52:48 +0100 Subject: [PATCH 03/56] basics collection --- basics.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/basics.md b/basics.md index 4e4f738..2914cf1 100644 --- a/basics.md +++ b/basics.md @@ -44,8 +44,9 @@ int select( bind() | listen() - | --> FD_ZERO() +________| +| | +| FD_ZERO() | | | FD_SET() | | @@ -53,8 +54,9 @@ int select( | | | FD_ISSET() | | | -| |N |Y -|______| accept() -> client socket thread -|____________| +| N Y +|______| | +| accept() -> client socket thread +|___________| ``` From 5eb23c3f521df6a4459a355bcabeb083fabb77fd Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:53:00 +0100 Subject: [PATCH 04/56] py: echo client & server --- .gitattributes | 14 ++++++++ .gitignore | 15 ++++++++ basics.md | 28 ++++++++++++--- include/TCP_Srv/BaseTypes.h | 33 ++++++++++++++++++ include/TCP_Srv/TCP_Srv_Base.h | 40 +++++++++++++++++++++ py/echoClient.py | 23 ++++++++++++ py/echoCommon.py | 31 ++++++++++++++++ py/echoServer.py | 64 ++++++++++++++++++++++++++++++++++ 8 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 .gitattributes create mode 100644 include/TCP_Srv/BaseTypes.h create mode 100644 include/TCP_Srv/TCP_Srv_Base.h create mode 100644 py/echoClient.py create mode 100644 py/echoCommon.py create mode 100644 py/echoServer.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9afd509 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# all text files unix line endings +* text=auto + +*.cpp text eol=lf +*.h text eol=lf +*.py text eol=lf +*.sh text eol=lf +*.lua text eol=lf +*.txt text eol=lf +*.svg text eol=lf +*.mdj text eol=lf +*.md text eol=lf +*.make text eol=lf + diff --git a/.gitignore b/.gitignore index 259148f..474e518 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,18 @@ *.exe *.out *.app + +# python +__pycache__ + +# premake / build +Makefile +build +bin +obj +lib +exe +.vs +*.vcxproj.* +*.vcxproj +*.sln \ No newline at end of file diff --git a/basics.md b/basics.md index 2914cf1..bc6d27a 100644 --- a/basics.md +++ b/basics.md @@ -1,5 +1,5 @@ - -#### struct timeval +# c++ +### struct timeval - VSC - GCC @@ -23,9 +23,7 @@ FD_SET(int fd, fd_set *fdset); FD_ISSET(int fd, fd_set *fdset); // clear socket (file descriptor) in fd_set FD_CLR(int fd, fd_set *fdset); -``` -```cpp // return: number of ready sockets // 0: timeout reached // <0: error @@ -60,3 +58,25 @@ ________| |___________| ``` +# python +### ipv6 example +```python +>>> import socket +>>> socket.getaddrinfo('www.python.org', 80, 0, 0, socket.SOL_TCP) +[(2, 1, 6, '', ('82.94.164.162', 80)), + (10, 1, 6, '', ('2001:888:2000:d::a2', 80, 0, 0))] + +>>> ourSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) +>>> ourSocket.connect(('2001:888:2000:d::a2', 80, 0, 0)) +``` +### client example +```python +HOST = '127.0.0.1' +PORT = 65432 +import socket +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + s.sendall(b'Hello, world') + data = s.recv(1024) + print(f'Received {data}') +``` diff --git a/include/TCP_Srv/BaseTypes.h b/include/TCP_Srv/BaseTypes.h new file mode 100644 index 0000000..c6602ef --- /dev/null +++ b/include/TCP_Srv/BaseTypes.h @@ -0,0 +1,33 @@ +// ============================================================ +// basic integral types +// ============================================================ +// created by Manfred Sorgo + +#pragma once +#ifndef BASETYPES_H +#define BASETYPES_H + +#include + +using UINT8 = uint8_t; +using BYTE = UINT8; +using UINT16 = uint16_t; +using UINT32 = uint32_t; +using UINT64 = uint64_t; + +using INT8 = int8_t; +using INT16 = int16_t; +using INT32 = int32_t; +using INT64 = int64_t; + +using CHAR = char; +using PTR = void*; +using CPTR = const void*; +using C_STRING = CHAR*; +using CONST_C_STRING = const CHAR*; + +#ifndef _WIN32 +using std::size_t; +#endif + +#endif // _H diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/include/TCP_Srv/TCP_Srv_Base.h new file mode 100644 index 0000000..4499225 --- /dev/null +++ b/include/TCP_Srv/TCP_Srv_Base.h @@ -0,0 +1,40 @@ +// ============================================================ +// TCP server base class +// ============================================================ +// created by Manfred Sorgo + +#ifdef _WIN32 +#include +#else +#include +using SOCKET = int; // for Linux +#endif +#include + +#ifndef SELECT_SECONDS +#define SELECT_SECONDS 0 +#endif + +#ifndef SELECT_MICRO_SECONDS +#define SELECT_MICRO_SECONDS 10 +#endif + +class TCP_Srv_Base +{ +public: + inline TCP_Srv_Base() : + mRunning(false) + {} + void start(); + void stop(); +protected: + virtual void process(int s) = 0; + const timeval mSelectTime = {SELECT_SECONDS, SELECT_MICRO_SECONDS}; +private: + // thread method + void tm(int s); + // thread control + bool mRunning; + // thread synchronization + std::mutex mMtx; +}; diff --git a/py/echoClient.py b/py/echoClient.py new file mode 100644 index 0000000..2a0c4b4 --- /dev/null +++ b/py/echoClient.py @@ -0,0 +1,23 @@ +# ============================================================ +# simple TCP echo client +# ============================================================ +# created by Manfred Sorgo + +from echoCommon import getSocketData +from sys import argv + +def getEcho(*args): + s, addr, port, message = getSocketData(*args) + print(f'msg : "{message}"') + try: + s.connect((addr, port)) + s.sendall(message.encode()) + data = s.recv(1024) + s.close() + print(f'recv: "{data.decode()}"') + except Exception as e: + print(e) + +if __name__ == '__main__': + getEcho(*argv[1:]) + diff --git a/py/echoCommon.py b/py/echoCommon.py new file mode 100644 index 0000000..da52e5c --- /dev/null +++ b/py/echoCommon.py @@ -0,0 +1,31 @@ +# ============================================================ +# common functions for echo server and client +# ============================================================ +# created by Manfred Sorgo +import re +import socket + +def getSocketData(*args): + rx4 = re.compile(r'^\d+(?:\.\d+){3}$') + rx6 = re.compile(r'^[0-9a-fA-F]*(?:\:[0-9a-fA-F]*){5}$') + rxP = re.compile(r'^\d+$') + prot = socket.AF_INET + addr = '127.0.0.1' + port = 8080 + message = 'Hello world' + for arg in args: + if rx4.match(arg): + addr = arg + elif rx6.match(arg): + addr = arg + prot = socket.AF_INET6 + elif rxP.match(arg): + port = int(arg) + else: + message = arg + print() + print('addr:', addr) + print('prot:', 'ipv6' if prot == socket.AF_INET6 else 'ipv4') + print('port:', port) + + return socket.socket(prot, socket.SOCK_STREAM), addr, port, message \ No newline at end of file diff --git a/py/echoServer.py b/py/echoServer.py new file mode 100644 index 0000000..19c6438 --- /dev/null +++ b/py/echoServer.py @@ -0,0 +1,64 @@ +# ============================================================ +# simple TCP echo server +# ============================================================ +# created by Manfred Sorgo + +from echoCommon import getSocketData +from sys import argv +from select import select +import signal +from os.path import basename +from getopt import getopt + +class EchoServer(object): + def __init__(self, *args): + self.verbose = False + opts, args = getopt(args, 'vh') + for o, v in opts: + if o == '-v': + self.verbose = True + elif o == '-h': + self.help() + + for sig in ('TERM', 'INT'): + signal.signal(getattr(signal, 'SIG' + sig), lambda *any: self.done()) + + self.sock, self.addr, self.port, *x = getSocketData(*args) + + def run(self): + try: + self.sock.bind((self.addr, self.port)) + self.sock.listen() + + print('Press Ctrl+C to stop') + while True: + if self.verbose: print('.', end='', flush=True) + for n in range(20): + rd, *x = select([self.sock], [], [], 0.05) + if rd: + conn, *x = self.sock.accept() + with conn: + while True: + data = conn.recv(1024) + if not data: + break + conn.sendall(data) + except Exception as e: + self.sock.close() + print(e) + + def done(*any): + print() + exit() + + def help(self): + print(f'Usage: {basename(argv[0])} [options] [addr] [port]') + print("""options: + -v verbose + -h this help""") + exit() + +if __name__ == '__main__': + srv = EchoServer(*argv[1:]) + srv.run() + From 03f6cb5a7a624c2b8682d68d9b237f9b0fe13920 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Fri, 29 Mar 2024 17:00:33 +0100 Subject: [PATCH 05/56] interim --- .gitignore | 4 +- basics.md | 18 ++++- devel/qnd/useCout.h | 27 +++++++ include/TCP_Srv/TCP_Srv_Base.h | 11 +++ make/premake5.lua | 88 +++++++++++++++++++++ py/echoServer.py | 1 + sample/EchoSrv.h | 18 +++++ sample/MS_SampleSrv.cpp | 137 +++++++++++++++++++++++++++++++++ sample/MethodThreads.cpp | 87 +++++++++++++++++++++ src/TCP_Srv_Base.cpp | 40 ++++++++++ 10 files changed, 427 insertions(+), 4 deletions(-) create mode 100644 devel/qnd/useCout.h create mode 100644 make/premake5.lua create mode 100644 sample/EchoSrv.h create mode 100644 sample/MS_SampleSrv.cpp create mode 100644 sample/MethodThreads.cpp create mode 100644 src/TCP_Srv_Base.cpp diff --git a/.gitignore b/.gitignore index 474e518..b3af50b 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,6 @@ exe .vs *.vcxproj.* *.vcxproj -*.sln \ No newline at end of file +*.sln + +*.make \ No newline at end of file diff --git a/basics.md b/basics.md index bc6d27a..73e0763 100644 --- a/basics.md +++ b/basics.md @@ -1,4 +1,5 @@ # c++ +## sockets ### struct timeval - VSC - GCC @@ -58,6 +59,15 @@ ________| |___________| ``` +## threads +### Start a thread with object methods +```cpp +#include +std::thread t(&Class::method, this, arguments..); +t.detach(); +``` + + # python ### ipv6 example ```python @@ -71,12 +81,14 @@ ________| ``` ### client example ```python -HOST = '127.0.0.1' -PORT = 65432 import socket + +HOST = '127.0.0.1' +PORT = 8001 + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) s.sendall(b'Hello, world') data = s.recv(1024) - print(f'Received {data}') + print(f'received {data}') ``` diff --git a/devel/qnd/useCout.h b/devel/qnd/useCout.h new file mode 100644 index 0000000..be2f5be --- /dev/null +++ b/devel/qnd/useCout.h @@ -0,0 +1,27 @@ +// ============================================================ +// use cout for quick and dirty temporary traces +// note +// CI application build will fail +// with this file (still) included +// and merge request will be blocked +// ============================================================ +// created by Manfred Sorgo + +#pragma once +#ifndef USECOUT_H +#define USECOUT_H + +#include +#include +using std::ostream; +using std::cout; +using std::endl; +using std::setw; +using std::left; +using std::setfill; + +#include + +#define TRACEVAR(VAR) cout << #VAR << ": " << VAR << endl; + +#endif // H_ \ No newline at end of file diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/include/TCP_Srv/TCP_Srv_Base.h index 4499225..3be84eb 100644 --- a/include/TCP_Srv/TCP_Srv_Base.h +++ b/include/TCP_Srv/TCP_Srv_Base.h @@ -3,12 +3,17 @@ // ============================================================ // created by Manfred Sorgo +#pragma once +#ifndef TCP_SRV_BASE_H +#define TCP_SRV_BASE_H + #ifdef _WIN32 #include #else #include using SOCKET = int; // for Linux #endif + #include #ifndef SELECT_SECONDS @@ -19,6 +24,10 @@ using SOCKET = int; // for Linux #define SELECT_MICRO_SECONDS 10 #endif +#ifndef READ_BUFFER_SIZE +#define READ_BUFFER_SIZE 1024 +#endif + class TCP_Srv_Base { public: @@ -38,3 +47,5 @@ class TCP_Srv_Base // thread synchronization std::mutex mMtx; }; + +#endif // _H \ No newline at end of file diff --git a/make/premake5.lua b/make/premake5.lua new file mode 100644 index 0000000..59cdf0f --- /dev/null +++ b/make/premake5.lua @@ -0,0 +1,88 @@ +-- ============================================================ +-- premake5 build rules for gcc (gmake2): +-- ============================================================ + +buildOptsGcc = { '-std=c++17 -pedantic-errors -Werror -Wall' } +includesBase = { '../include' } +-- ============================================================ +-- VS setup +-- warning level: 4 (high) +-- +-- suppressed warnings application: +-- - 4100 unreferenced formal parameter +-- - 4103 change of alignement with header inclusion +-- this is intended behaviour of packBegin.h / packEnd.h +-- additional suppressed warnings test code: +-- - 4127 suggested 'if constexpr' +-- warning caused by CppUTest headers code +-- ============================================================ +buildOptsVS = { '/std:c++17 /W4' } + +-- ============================================================ +-- > TCP_Srv_Base library +-- -> VS: lib/TCP_Srv_Base.lib +-- -> gcc: lib/libTCP_Srv_Base.a +-- ============================================================ +workspace '01_TCP_Srv_Base' + configurations { 'ci' } + language 'C++' + objdir 'obj/%{prj.name}' + includedirs { includesBase } + targetdir 'lib' + + filter { 'action:vs*' } + buildoptions { buildOptsVS } + + filter { 'action:gmake*' } + buildoptions { buildOptsGcc } + + project 'TCP_Srv_Base' + kind 'StaticLib' + targetdir 'lib' + files { '../src/*.cpp' } + +-- ============================================================ +-- > sample: metod threads +-- -> gcc: bin/11_sampleMethodTreads +-- -> VS: exe/11_sampleMethodTreads.exe +-- ============================================================ +workspace '11_SampleMethodTreads' + configurations { 'ci' } + language 'C++' + objdir 'obj/%{prj.name}' + + filter { 'action:vs*' } + buildoptions { buildOptsVS } + targetdir 'exe' + + filter { 'action:gmake*' } + buildoptions { buildOptsGcc } + linkoptions { '-pthread' } + targetdir 'bin' + + project '11_sampleMethodTreads' + kind 'ConsoleApp' + files { '../sample/MethodThreads.cpp' } + +-- ============================================================ +-- > Microsoft sample: echo server +-- VS only +-- -> exe/12_MS_SamplSrv.exe +-- ============================================================ +workspace '12_MS_SamplSrv' + configurations { 'ci' } + language 'C++' + objdir 'obj/%{prj.name}' + + filter { 'action:vs*' } + buildoptions { buildOptsVS } + targetdir 'exe' + files { '../sample/MS_SampleSrv.cpp' } + project '12_MS_SamplSrv' + kind 'ConsoleApp' + + -- filter { 'action:gmake*' } + -- buildoptions { buildOptsGcc } + -- targetdir 'bin' + + \ No newline at end of file diff --git a/py/echoServer.py b/py/echoServer.py index 19c6438..77a7faa 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -49,6 +49,7 @@ def run(self): def done(*any): print() + print('done') exit() def help(self): diff --git a/sample/EchoSrv.h b/sample/EchoSrv.h new file mode 100644 index 0000000..b30ca88 --- /dev/null +++ b/sample/EchoSrv.h @@ -0,0 +1,18 @@ + +#include + +#pragma once +#ifndef ECHO_SRV_H +#define ECHO_SRV_H + +class EchoSrv : public TCP_Srv_Base +{ +public: + inline EchoSrv() = default; +protected: + void process(int s) override; +private: + /* data */ +}; + +#endif // _H \ No newline at end of file diff --git a/sample/MS_SampleSrv.cpp b/sample/MS_SampleSrv.cpp new file mode 100644 index 0000000..2761108 --- /dev/null +++ b/sample/MS_SampleSrv.cpp @@ -0,0 +1,137 @@ +#undef UNICODE + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include + +// Need to link with Ws2_32.lib +#pragma comment (lib, "Ws2_32.lib") +// #pragma comment (lib, "Mswsock.lib") + +#define DEFAULT_BUFLEN 512 +#define DEFAULT_PORT "8080" + +int __cdecl main(void) +{ + WSADATA wsaData; + int iResult; + + SOCKET ListenSocket = INVALID_SOCKET; + SOCKET ClientSocket = INVALID_SOCKET; + + struct addrinfo *result = NULL; + struct addrinfo hints; + + int iSendResult; + char recvbuf[DEFAULT_BUFLEN]; + int recvbuflen = DEFAULT_BUFLEN; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed with error: %d\n", iResult); + return 1; + } + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + // Resolve the server address and port + iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); + if ( iResult != 0 ) { + printf("getaddrinfo failed with error: %d\n", iResult); + WSACleanup(); + return 1; + } + + // Create a SOCKET for the server to listen for client connections. + ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (ListenSocket == INVALID_SOCKET) { + printf("socket failed with error: %ld\n", WSAGetLastError()); + freeaddrinfo(result); + WSACleanup(); + return 1; + } + + // Setup the TCP listening socket + iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); + if (iResult == SOCKET_ERROR) { + printf("bind failed with error: %d\n", WSAGetLastError()); + freeaddrinfo(result); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + freeaddrinfo(result); + + iResult = listen(ListenSocket, SOMAXCONN); + if (iResult == SOCKET_ERROR) { + printf("listen failed with error: %d\n", WSAGetLastError()); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + // Accept a client socket + ClientSocket = accept(ListenSocket, NULL, NULL); + if (ClientSocket == INVALID_SOCKET) { + printf("accept failed with error: %d\n", WSAGetLastError()); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + // No longer need server socket + closesocket(ListenSocket); + + // Receive until the peer shuts down the connection + do { + + iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); + if (iResult > 0) { + printf("Bytes received: %d\n", iResult); + + // Echo the buffer back to the sender + iSendResult = send( ClientSocket, recvbuf, iResult, 0 ); + if (iSendResult == SOCKET_ERROR) { + printf("send failed with error: %d\n", WSAGetLastError()); + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + printf("Bytes sent: %d\n", iSendResult); + } + else if (iResult == 0) + printf("Connection closing...\n"); + else { + printf("recv failed with error: %d\n", WSAGetLastError()); + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + + } while (iResult > 0); + + // shutdown the connection since we're done + iResult = shutdown(ClientSocket, SD_SEND); + if (iResult == SOCKET_ERROR) { + printf("shutdown failed with error: %d\n", WSAGetLastError()); + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + + // cleanup + closesocket(ClientSocket); + WSACleanup(); + + return 0; +} \ No newline at end of file diff --git a/sample/MethodThreads.cpp b/sample/MethodThreads.cpp new file mode 100644 index 0000000..ea1ba9b --- /dev/null +++ b/sample/MethodThreads.cpp @@ -0,0 +1,87 @@ +// ============================================================ +// sample of threads with object methods +// ============================================================ +// created by Manfred Sorgo + +// header content +#include +#include +#include + +class Test +{ +public: + inline Test(int num = 0) : + mOff(num), + mRunning(false) + {} + void start(); + void stop(); +private: + // thread method + void tm(int s); + // context number offset + const int mOff; + // thread synchronization + std::mutex mMtx; + // thread control + bool mRunning; +}; + +// source content +using std::cout; +using std::endl; +using std::this_thread::sleep_for; +using std::chrono::milliseconds; +using std::chrono::seconds; +using mutextlock = std::unique_lock; + +void Test::start() +{ + cout << "start" << endl; + mRunning = true; + for (int i = 0; i < 10; ++i) + { + // this is the way to start a thread with a class method + // 1st parameter: class::method + // 2nd parameter: object -> this + // following parameters: method parameters + std::thread t(&Test::tm, this, i); + t.detach(); + } +} + +void Test::stop() +{ + mutextlock lock(mMtx); + cout << "stop ..." << endl; + mRunning = false; + lock.unlock(); + sleep_for(milliseconds(300)); +} + +void Test::tm(int s) +{ + mutextlock lock(mMtx); + lock.unlock(); + while (mRunning) + { + sleep_for(milliseconds(100)); + lock.lock(); + cout << "tm " << s << ": " << mOff + s << endl; + lock.unlock(); + } + lock.lock(); + cout << "tm " << s << " done." << endl; +} + +// main content +int main() +{ + Test tt(10); + tt.start(); + sleep_for(seconds(1)); + tt.stop(); + cout << "test done." << endl; + return 0; +} \ No newline at end of file diff --git a/src/TCP_Srv_Base.cpp b/src/TCP_Srv_Base.cpp new file mode 100644 index 0000000..797bcd4 --- /dev/null +++ b/src/TCP_Srv_Base.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include +using std::cout; +using std::endl; + +void TCP_Srv_Base::tm(int s) +{ + std::unique_lock lock(mMtx); + lock.unlock(); + while (mRunning) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + lock.lock(); + cout << "tm " << s << endl; + process(s); + lock.unlock(); + } + lock.lock(); + cout << "tm " << s << " terminated." << endl; +} + +void TCP_Srv_Base::start() +{ + cout << "start" << endl; + mRunning = true; + for (int i = 0; i < 10; ++i) + { + std::thread t(&TCP_Srv_Base::tm, this, i); + t.detach(); + } +} +void TCP_Srv_Base::stop() +{ + cout << "stop" << endl; + mRunning = false; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); +} + From 933830fed391c01906e70cd52db00a8c53095a7b Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:46:38 +0100 Subject: [PATCH 06/56] interim --- make/premake5.lua | 45 ++++++-- sample/MethodThreads.cpp | 4 +- sample/Tenouk_SampleSrv.cpp | 210 ++++++++++++++++++++++++++++++++++++ sample/dummy.cpp | 11 ++ 4 files changed, 259 insertions(+), 11 deletions(-) create mode 100644 sample/Tenouk_SampleSrv.cpp create mode 100644 sample/dummy.cpp diff --git a/make/premake5.lua b/make/premake5.lua index 59cdf0f..86bb664 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -18,6 +18,8 @@ includesBase = { '../include' } -- ============================================================ buildOptsVS = { '/std:c++17 /W4' } +dummy = { '../sample/dummy.cpp' } + -- ============================================================ -- > TCP_Srv_Base library -- -> VS: lib/TCP_Srv_Base.lib @@ -73,16 +75,41 @@ workspace '12_MS_SamplSrv' configurations { 'ci' } language 'C++' objdir 'obj/%{prj.name}' + kind 'ConsoleApp' - filter { 'action:vs*' } - buildoptions { buildOptsVS } - targetdir 'exe' - files { '../sample/MS_SampleSrv.cpp' } - project '12_MS_SamplSrv' - kind 'ConsoleApp' + project '12_MS_SamplSrv' + + filter { 'action:vs*' } + buildoptions { buildOptsVS } + targetdir 'exe' + files { '../sample/MS_SampleSrv.cpp' } + + filter { 'action:gmake*' } + buildoptions { buildOptsGcc } + files { dummy } + targetdir 'bin' + +-- ============================================================ +-- > Tenouk sample: echo server +-- VS only +-- -> exe/12_Tenouk_SampleSrv.exe +-- ============================================================ +workspace '13_Tenouk_SampleSrv' + configurations { 'ci' } + language 'C++' + objdir 'obj/%{prj.name}' + kind 'ConsoleApp' + includedirs { includesBase } + + project '13_MS_SamplSrv' - -- filter { 'action:gmake*' } - -- buildoptions { buildOptsGcc } - -- targetdir 'bin' + filter { 'action:vs*' } + buildoptions { buildOptsVS } + targetdir 'exe' + files { '../sample/Tenouk_SampleSrv.cpp' } + filter { 'action:gmake*' } + buildoptions { buildOptsGcc } + files { dummy } + targetdir 'bin' \ No newline at end of file diff --git a/sample/MethodThreads.cpp b/sample/MethodThreads.cpp index ea1ba9b..dc5c860 100644 --- a/sample/MethodThreads.cpp +++ b/sample/MethodThreads.cpp @@ -4,9 +4,7 @@ // created by Manfred Sorgo // header content -#include #include -#include class Test { @@ -29,6 +27,8 @@ class Test }; // source content +#include +#include using std::cout; using std::endl; using std::this_thread::sleep_for; diff --git a/sample/Tenouk_SampleSrv.cpp b/sample/Tenouk_SampleSrv.cpp new file mode 100644 index 0000000..d96d51a --- /dev/null +++ b/sample/Tenouk_SampleSrv.cpp @@ -0,0 +1,210 @@ +// ============================================================ +// sample TCP echo server +// figured out from Tenouk's Winsock2 tutorial +// ============================================================ +// see: https://www.tenouk.com/Winsock/Winsock2example2.html + +// header content + +#ifdef _WIN32 +#include +#pragma comment(lib, "ws2_32") +#endif + +#include +#include + +class SampleSrv +{ +public: + inline SampleSrv() = default; + ~SampleSrv(); + bool init(UINT16 port = 8080); + bool run(); + void stop(); +private: + SOCKET mListenSocket = INVALID_SOCKET; + bool cleanup(); +}; + +#include +using std::cout; +using std::endl; + +bool SampleSrv::init(const UINT16 port) +{ + #ifdef _WIN32 + // check for Windows Sockets version 2.2 + { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + return false; + } + } + #endif + mListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mListenSocket == INVALID_SOCKET) + { + cout << "socket failed" << endl; + return cleanup(); + } + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + + if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) + { + cout << "bind failed" << endl; + return cleanup(); + } + + return true; +} + +bool SampleSrv::cleanup() +{ +#ifdef _WIN32 + if (mListenSocket != INVALID_SOCKET) + { + closesocket(mListenSocket); + } + WSACleanup(); +#endif + return false; +} + +bool SampleSrv::run() +{ + cout << "run ..." << endl; + bool ok = true; + if (listen(mListenSocket, SOMAXCONN) == SOCKET_ERROR) + { + cout << "listen failed" << endl; + ok = false; + } + while (ok) + { + fd_set lset; + FD_ZERO(&lset); + FD_SET(mListenSocket, &lset); + const timeval tv {0, 100000}; + if (ok and (select(0, &lset, nullptr, nullptr, &tv) == SOCKET_ERROR)) + { + cout << "listen select failed" << endl; + ok = false; + } + if (ok and FD_ISSET(mListenSocket, &lset)) + { + SOCKET clientSocket = accept(mListenSocket, nullptr, nullptr); + if (clientSocket == INVALID_SOCKET) + { + cout << "accept failed" << endl; + ok = false; + } + else + { + cout << "client connected" << endl; + while(clientSocket != INVALID_SOCKET) + { + fd_set cset; + FD_ZERO(&cset); + FD_SET(clientSocket, &cset); + select(0, &cset, nullptr, nullptr, &tv); + if (FD_ISSET(clientSocket, &cset)) + { + char buffer[1024]; + int bytes = recv(clientSocket, buffer, sizeof(buffer), 0); + if (bytes > 0) + { + cout << "client sent " << bytes << " bytes" << endl; + send(clientSocket, buffer, bytes, 0); + } + else + { + cout << "client disconnected" << endl; + closesocket(clientSocket); + clientSocket = INVALID_SOCKET; + } + } + } + } + } + } + return ok or cleanup(); +} + +// struct addrinfo *result = nullptr; +// struct addrinfo hints; + +// iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + +// ZeroMemory(&hints, sizeof(hints)); +// hints.ai_family = AF_INET; +// hints.ai_socktype = SOCK_STREAM; +// hints.ai_protocol = IPPROTO_TCP; +// hints.ai_flags = AI_PASSIVE; + +// iResult = getaddrinfo(nullptr, "8080", &hints, &result); +// if (iResult != 0) +// { +// WSACleanup(); +// return false; +// } + +// mListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); +// if (mListenSocket == INVALID_SOCKET) +// { +// freeaddrinfo(result); +// WSACleanup(); +// return false; +// } + +// iResult = bind(mListenSocket, result->ai_addr, (int)result->ai_addrlen); +// if (iResult == SOCKET_ERROR) +// { +// freeaddrinfo(result); +// closesocket(mListenSocket); +// WSACleanup(); +// return false; +// } + +// freeaddrinfo(result); + +// iResult = listen(mListenSocket, SOMAXCONN); +// if (iResult == SOCKET_ERROR) +// { +// closesocket(mListenSocket); +// WSACleanup(); +// return false; +// } + +// return true; +// } + +SampleSrv::~SampleSrv() +{ + if (mListenSocket != INVALID_SOCKET) + { + closesocket(mListenSocket); + } + WSACleanup(); +} + +int main() +{ + SampleSrv srv; + if (not srv.init()) + { + cout << "init failed" << endl; + return 1; + } + if (not srv.run()) + { + cout << "run failed" << endl; + return 1; + } + cout << "test done." << endl; + return 0; +} \ No newline at end of file diff --git a/sample/dummy.cpp b/sample/dummy.cpp new file mode 100644 index 0000000..e76b765 --- /dev/null +++ b/sample/dummy.cpp @@ -0,0 +1,11 @@ +// ============================================================ +// dummy source file for premake5 +// ============================================================ +// created by Manfred Sorgo + +#include +int main() +{ + std::cout << "Hello, World!" << std::endl; + return 0; +} \ No newline at end of file From 11590686d917f34ea1c1315bcbdae3e63b747c51 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:47:42 +0100 Subject: [PATCH 07/56] interim --- sample/MS_SampleSrv.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sample/MS_SampleSrv.cpp b/sample/MS_SampleSrv.cpp index 2761108..e2f4ef5 100644 --- a/sample/MS_SampleSrv.cpp +++ b/sample/MS_SampleSrv.cpp @@ -1,3 +1,8 @@ +// ============================================================ +// Microsoft sample TCP echo server +// ============================================================ +// see: https://learn.microsoft.com/en-us/windows/win32/winsock/complete-server-code + #undef UNICODE #define WIN32_LEAN_AND_MEAN From f6415f4b3fdca9388205043fe077c34a0a121081 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:52:03 +0100 Subject: [PATCH 08/56] interim --- py/echoServer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/py/echoServer.py b/py/echoServer.py index 77a7faa..b3ef470 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -2,6 +2,7 @@ # simple TCP echo server # ============================================================ # created by Manfred Sorgo +# see: https://realpython.com/python-sockets/ from echoCommon import getSocketData from sys import argv From 39ae9d6aaf187ec211e75261cb5dd8cae0d362cf Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:05:44 +0100 Subject: [PATCH 09/56] interim --- .gitignore | 5 ++- make/buildAll.sh | 53 ++++++++++++++++++++++ make/buildVS.md | 59 ++++++++++++++++++++++++ make/premake5.lua | 26 +++++------ py/echoClient.py | 15 ++++--- py/echoCommon.py | 10 ++++- py/echoServer.py | 2 +- sample/Tenouk_SampleSrv.cpp | 89 +++++++++++++------------------------ 8 files changed, 176 insertions(+), 83 deletions(-) create mode 100644 make/buildAll.sh create mode 100644 make/buildVS.md diff --git a/.gitignore b/.gitignore index b3af50b..815c518 100644 --- a/.gitignore +++ b/.gitignore @@ -45,5 +45,8 @@ exe *.vcxproj.* *.vcxproj *.sln +*.make -*.make \ No newline at end of file +# general +tmp.* +tmp_.* diff --git a/make/buildAll.sh b/make/buildAll.sh new file mode 100644 index 0000000..303e054 --- /dev/null +++ b/make/buildAll.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +help() +{ + echo "====================================" + echo "build all major makes" + echo "options:" + echo "-r remove all artifacts before" + echo "-p premake5 makefiles" + echo "-h this help" + echo "====================================" + exit +} + +tm() +{ + en=$(date +%s) + printf "%-25s[%3d]\n" $1 $(($en-$2)) +} + +mk() +{ + st=$(date +%s) + make -j -f $1 > /dev/null + tm ${1%.*} $st +} + +rem= +exe= +pre= +while getopts hrxp option; do + case $option in + (h) help;; + (r) rem=1;; + (x) exe=1;; + (p) pre=1 + esac +done + +cd $(dirname $0) + +if test ! -z $rem; then git clean -dxf . 2>/dev/null >/dev/null; pre=1; fi + +if test ! -z $pre; then premake5 gmake2 > /dev/null; fi + +st=$(date +%s) +rm -f $(ls *.make | grep '_MS_') $(ls *.make | grep '_MS_') + +for fn in $(ls *.make | grep '^[0-9][0-9]'); do mk $fn; done +wait +echo binaries: +for b in bin/*; do echo "- $b"; done +tm "DONE" $st diff --git a/make/buildVS.md b/make/buildVS.md new file mode 100644 index 0000000..f60e8cc --- /dev/null +++ b/make/buildVS.md @@ -0,0 +1,59 @@ +# Visual Studio build +## preconditions +- premake5 available +- Visual Studio + +## 1) generate VS solution using premake5 +### 1.1) find out suitable VS version using premake5 --help + +````shell +$> premake5 --help +Usage: premake5 [options] action [arguments] + +OPTIONS - General + --file=FILE Read FILE as a Premake script; default is 'premake5.lua' + +ACTIONS + vs2005 Generate Visual Studio 2005 project files + vs2008 Generate Visual Studio 2008 project files + vs2010 Generate Visual Studio 2010 project files + vs2012 Generate Visual Studio 2012 project files + vs2013 Generate Visual Studio 2013 project files + vs2015 Generate Visual Studio 2015 project files + vs2017 Generate Visual Studio 2017 project files + vs2019 Generate Visual Studio 2019 project files + vs2022 Generate Visual Studio 2022 project files +```` +### 1.2) generate solution with your VS version +e.g. VS 2019 +````shell +$> premake5 --file=premake5_vs.lua vs2019 +Building configurations... +Running action 'vs2019'... +Generated cpputest.sln... +Generated cpputest.vcxproj... +Generated cpputest.vcxproj.filters... +Generated tests.sln... +Generated tests.vcxproj... +Generated tests.vcxproj.filters... +Generated dstw.sln... +Generated dstw.vcxproj... +Generated dstw.vcxproj.filters... +Done (130ms). +```` +## 2) build executables in VS +### 2.1) build and run module tests +- open _cpputest.sln_ +- build +- open _tests.sln_ +- build and run + +### 2.2) build and run application +- open _gendata.sln_ +- build and run +- open _dstw.sln_ +- build and run + +## 3) VS keyboard shortcuts +- build: [CTRL] [shift] [B] +- (build and) run: [CTRL] [F5] diff --git a/make/premake5.lua b/make/premake5.lua index 86bb664..605b221 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -7,14 +7,6 @@ includesBase = { '../include' } -- ============================================================ -- VS setup -- warning level: 4 (high) --- --- suppressed warnings application: --- - 4100 unreferenced formal parameter --- - 4103 change of alignement with header inclusion --- this is intended behaviour of packBegin.h / packEnd.h --- additional suppressed warnings test code: --- - 4127 suggested 'if constexpr' --- warning caused by CppUTest headers code -- ============================================================ buildOptsVS = { '/std:c++17 /W4' } @@ -33,6 +25,7 @@ workspace '01_TCP_Srv_Base' targetdir 'lib' filter { 'action:vs*' } + warnings 'high' buildoptions { buildOptsVS } filter { 'action:gmake*' } @@ -54,6 +47,7 @@ workspace '11_SampleMethodTreads' objdir 'obj/%{prj.name}' filter { 'action:vs*' } + warnings 'high' buildoptions { buildOptsVS } targetdir 'exe' @@ -62,7 +56,7 @@ workspace '11_SampleMethodTreads' linkoptions { '-pthread' } targetdir 'bin' - project '11_sampleMethodTreads' + project 'sampleMethodTreads' kind 'ConsoleApp' files { '../sample/MethodThreads.cpp' } @@ -77,9 +71,10 @@ workspace '12_MS_SamplSrv' objdir 'obj/%{prj.name}' kind 'ConsoleApp' - project '12_MS_SamplSrv' + project 'MS_SamplSrv' filter { 'action:vs*' } + warnings 'high' buildoptions { buildOptsVS } targetdir 'exe' files { '../sample/MS_SampleSrv.cpp' } @@ -91,8 +86,8 @@ workspace '12_MS_SamplSrv' -- ============================================================ -- > Tenouk sample: echo server --- VS only --- -> exe/12_Tenouk_SampleSrv.exe +-- -> gcc: bin/12_Tenouk_SampleSrv +-- -> VS: exe/12_Tenouk_SampleSrv.exe -- ============================================================ workspace '13_Tenouk_SampleSrv' configurations { 'ci' } @@ -100,16 +95,15 @@ workspace '13_Tenouk_SampleSrv' objdir 'obj/%{prj.name}' kind 'ConsoleApp' includedirs { includesBase } + files { '../sample/Tenouk_SampleSrv.cpp' } - project '13_MS_SamplSrv' - + project 'Tenouk_SampleSrv' filter { 'action:vs*' } + warnings 'high' buildoptions { buildOptsVS } targetdir 'exe' - files { '../sample/Tenouk_SampleSrv.cpp' } filter { 'action:gmake*' } buildoptions { buildOptsGcc } - files { dummy } targetdir 'bin' \ No newline at end of file diff --git a/py/echoClient.py b/py/echoClient.py index 2a0c4b4..af5a1b5 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -5,16 +5,21 @@ from echoCommon import getSocketData from sys import argv +from time import sleep +from select import select def getEcho(*args): - s, addr, port, message = getSocketData(*args) - print(f'msg : "{message}"') + [s, addr, port, message, num, *x] = getSocketData(*args) + enc = message.encode() try: s.connect((addr, port)) - s.sendall(message.encode()) - data = s.recv(1024) + for n in range(num): + if n > 0: sleep(1) + print(f'send: "{message}"') + s.sendall(enc) + data = s.recv(1024) + print(f'recv: "{data.decode()}"') s.close() - print(f'recv: "{data.decode()}"') except Exception as e: print(e) diff --git a/py/echoCommon.py b/py/echoCommon.py index da52e5c..c7170ba 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -9,9 +9,13 @@ def getSocketData(*args): rx4 = re.compile(r'^\d+(?:\.\d+){3}$') rx6 = re.compile(r'^[0-9a-fA-F]*(?:\:[0-9a-fA-F]*){5}$') rxP = re.compile(r'^\d+$') + rxN = re.compile(r'^(\d+)[xX]+$') + rxT = re.compile(r'^(\d+)[tT]+$') prot = socket.AF_INET addr = '127.0.0.1' port = 8080 + num = 1 + ths = 1 message = 'Hello world' for arg in args: if rx4.match(arg): @@ -21,6 +25,10 @@ def getSocketData(*args): prot = socket.AF_INET6 elif rxP.match(arg): port = int(arg) + elif rxN.match(arg): + num = int(rxN.match(arg).group(1)) + elif rxT.match(arg): + ths = int(rxT.match(arg).group(1)) else: message = arg print() @@ -28,4 +36,4 @@ def getSocketData(*args): print('prot:', 'ipv6' if prot == socket.AF_INET6 else 'ipv4') print('port:', port) - return socket.socket(prot, socket.SOCK_STREAM), addr, port, message \ No newline at end of file + return [socket.socket(prot, socket.SOCK_STREAM), addr, port, message, num, ths, prot] \ No newline at end of file diff --git a/py/echoServer.py b/py/echoServer.py index b3ef470..26fc25f 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -24,7 +24,7 @@ def __init__(self, *args): for sig in ('TERM', 'INT'): signal.signal(getattr(signal, 'SIG' + sig), lambda *any: self.done()) - self.sock, self.addr, self.port, *x = getSocketData(*args) + [self.sock, self.addr, self.port, *x] = getSocketData(*args) def run(self): try: diff --git a/sample/Tenouk_SampleSrv.cpp b/sample/Tenouk_SampleSrv.cpp index d96d51a..0e8538c 100644 --- a/sample/Tenouk_SampleSrv.cpp +++ b/sample/Tenouk_SampleSrv.cpp @@ -9,10 +9,20 @@ #ifdef _WIN32 #include #pragma comment(lib, "ws2_32") +using tval = const timeval; +#else +#include +#include +#include +using SOCKET = int; +constexpr SOCKET INVALID_SOCKET = -1; +#define closesocket close +using tval = timeval; #endif #include #include +#include class SampleSrv { @@ -20,8 +30,8 @@ class SampleSrv inline SampleSrv() = default; ~SampleSrv(); bool init(UINT16 port = 8080); + inline bool init(const char* port) { return init(static_cast(atoi(port))); } bool run(); - void stop(); private: SOCKET mListenSocket = INVALID_SOCKET; bool cleanup(); @@ -54,9 +64,9 @@ bool SampleSrv::init(const UINT16 port) addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); - if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) + if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { - cout << "bind failed" << endl; + cout << "bind failed, port: " << port << endl; return cleanup(); } @@ -65,11 +75,11 @@ bool SampleSrv::init(const UINT16 port) bool SampleSrv::cleanup() { -#ifdef _WIN32 if (mListenSocket != INVALID_SOCKET) { closesocket(mListenSocket); } +#ifdef _WIN32 WSACleanup(); #endif return false; @@ -79,7 +89,7 @@ bool SampleSrv::run() { cout << "run ..." << endl; bool ok = true; - if (listen(mListenSocket, SOMAXCONN) == SOCKET_ERROR) + if (listen(mListenSocket, SOMAXCONN) < 0) { cout << "listen failed" << endl; ok = false; @@ -89,11 +99,13 @@ bool SampleSrv::run() fd_set lset; FD_ZERO(&lset); FD_SET(mListenSocket, &lset); - const timeval tv {0, 100000}; - if (ok and (select(0, &lset, nullptr, nullptr, &tv) == SOCKET_ERROR)) { - cout << "listen select failed" << endl; - ok = false; + tval tv {0, 100000}; + if (ok and (select(0, &lset, nullptr, nullptr, &tv) < 0)) + { + cout << "listen select failed" << endl; + ok = false; + } } if (ok and FD_ISSET(mListenSocket, &lset)) { @@ -111,8 +123,13 @@ bool SampleSrv::run() fd_set cset; FD_ZERO(&cset); FD_SET(clientSocket, &cset); - select(0, &cset, nullptr, nullptr, &tv); - if (FD_ISSET(clientSocket, &cset)) + tval tv {0, 100000}; + if (select(0, &cset, nullptr, nullptr, &tv) < 0) + { + cout << "client select failed" << endl; + ok = false; + } + else if (FD_ISSET(clientSocket, &cset)) { char buffer[1024]; int bytes = recv(clientSocket, buffer, sizeof(buffer), 0); @@ -135,61 +152,15 @@ bool SampleSrv::run() return ok or cleanup(); } -// struct addrinfo *result = nullptr; -// struct addrinfo hints; - -// iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - -// ZeroMemory(&hints, sizeof(hints)); -// hints.ai_family = AF_INET; -// hints.ai_socktype = SOCK_STREAM; -// hints.ai_protocol = IPPROTO_TCP; -// hints.ai_flags = AI_PASSIVE; - -// iResult = getaddrinfo(nullptr, "8080", &hints, &result); -// if (iResult != 0) -// { -// WSACleanup(); -// return false; -// } - -// mListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); -// if (mListenSocket == INVALID_SOCKET) -// { -// freeaddrinfo(result); -// WSACleanup(); -// return false; -// } - -// iResult = bind(mListenSocket, result->ai_addr, (int)result->ai_addrlen); -// if (iResult == SOCKET_ERROR) -// { -// freeaddrinfo(result); -// closesocket(mListenSocket); -// WSACleanup(); -// return false; -// } - -// freeaddrinfo(result); - -// iResult = listen(mListenSocket, SOMAXCONN); -// if (iResult == SOCKET_ERROR) -// { -// closesocket(mListenSocket); -// WSACleanup(); -// return false; -// } - -// return true; -// } - SampleSrv::~SampleSrv() { if (mListenSocket != INVALID_SOCKET) { closesocket(mListenSocket); } +#ifdef _WIN32 WSACleanup(); +#endif } int main() From e16f8bf2d73b37e3cd76128de370fbb626e75977 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sat, 30 Mar 2024 19:28:33 +0100 Subject: [PATCH 10/56] rework TCP_Srv_Base --- include/TCP_Srv/TCP_Srv_Base.h | 37 +++++---- include/TCP_Srv/TraceMacros.h | 14 ++++ py/echoParallelClient.py | 37 +++++++++ src/TCP_Srv_Base.cpp | 143 +++++++++++++++++++++++++++++---- 4 files changed, 201 insertions(+), 30 deletions(-) create mode 100644 include/TCP_Srv/TraceMacros.h create mode 100644 py/echoParallelClient.py diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/include/TCP_Srv/TCP_Srv_Base.h index 3be84eb..7f4535d 100644 --- a/include/TCP_Srv/TCP_Srv_Base.h +++ b/include/TCP_Srv/TCP_Srv_Base.h @@ -7,13 +7,23 @@ #ifndef TCP_SRV_BASE_H #define TCP_SRV_BASE_H +#include + #ifdef _WIN32 -#include +#include +#pragma comment(lib, "ws2_32") +using tval = const timeval; #else -#include -using SOCKET = int; // for Linux +#include +#include +#include +using SOCKET = int; +constexpr SOCKET INVALID_SOCKET = -1; +#define closesocket close +using tval = timeval; #endif +#include // atoi #include #ifndef SELECT_SECONDS @@ -31,21 +41,20 @@ using SOCKET = int; // for Linux class TCP_Srv_Base { public: - inline TCP_Srv_Base() : - mRunning(false) - {} - void start(); - void stop(); + inline TCP_Srv_Base() = default; + bool run(UINT16 port = 8080); + bool run(const INT32 argc, const CONST_C_STRING* const argv); + protected: - virtual void process(int s) = 0; + using Buffer = CHAR[READ_BUFFER_SIZE]; + virtual void process(SOCKET cs, Buffer buff, size_t n) = 0; const timeval mSelectTime = {SELECT_SECONDS, SELECT_MICRO_SECONDS}; + private: // thread method - void tm(int s); - // thread control - bool mRunning; - // thread synchronization - std::mutex mMtx; + void tm(SOCKET cs); + bool cleanup(); + SOCKET mListenSocket = INVALID_SOCKET; }; #endif // _H \ No newline at end of file diff --git a/include/TCP_Srv/TraceMacros.h b/include/TCP_Srv/TraceMacros.h new file mode 100644 index 0000000..19495a3 --- /dev/null +++ b/include/TCP_Srv/TraceMacros.h @@ -0,0 +1,14 @@ +#pragma once +#ifndef TRACE_MACROS_H +#define TRACE_MACROS_H + +#ifdef TRACE_ON +#include +#define TRACE(MSG) std::cout << MSG << std::endl; +#define TRACEVAR(VAR) std::cout << #VAR << ": " << VAR << std::endl; +#else +#define TRACE(MSG) +#define TRACEVAR(VAR) +#endif + +#endif // _H diff --git a/py/echoParallelClient.py b/py/echoParallelClient.py new file mode 100644 index 0000000..64206d7 --- /dev/null +++ b/py/echoParallelClient.py @@ -0,0 +1,37 @@ +# ============================================================ +# multi thread TCP echo client +# ============================================================ +# created by Manfred Sorgo + +from echoCommon import getSocketData +from sys import argv +from threading import Thread +from logging import info as log, basicConfig as logconfig, INFO +from socket import socket, SOCK_STREAM +from time import sleep + +def getEchoes(*args): + [addr, port, message, num, nths, prot] = getSocketData(*args)[1:] + logconfig(format="%(asctime)s %(message)s", level=INFO, datefmt="%H:%M:%S") + for n in range(nths): + Thread(target=tfunc, args=(n + 1, prot, addr, port, message, num)).start() + +def tfunc(nr:int, prot:int, addr:str, port:int, message:str, num:int): + try: + log("%02d connecting ..." % nr) + s = socket(prot, SOCK_STREAM) + s.connect((addr, port)) + log("%02d connected." % nr) + for n in range(num): + if n > 0: sleep(1) + log("%02d %d / %d send: '%s'" % (nr, n + 1, num, message)) + s.sendall(message.encode()) + data = s.recv(1024) + log("%02d %d / %d recv: '%s'" % (nr, n + 1, num, data.decode())) + s.close() + except Exception as e: + print(e) + +if __name__ == '__main__': + getEchoes(*argv[1:]) + diff --git a/src/TCP_Srv_Base.cpp b/src/TCP_Srv_Base.cpp index 797bcd4..50124e3 100644 --- a/src/TCP_Srv_Base.cpp +++ b/src/TCP_Srv_Base.cpp @@ -1,36 +1,147 @@ #include + +#define TRACE_ON +#include + #include +#include #include using std::cout; +using std::cerr; using std::endl; -void TCP_Srv_Base::tm(int s) +bool TCP_Srv_Base::cleanup() +{ + if (mListenSocket != INVALID_SOCKET) + { + closesocket(mListenSocket); + } +#ifdef _WIN32 + WSACleanup(); +#endif + return false; +} + +void TCP_Srv_Base::tm(SOCKET cs) { - std::unique_lock lock(mMtx); - lock.unlock(); - while (mRunning) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - lock.lock(); - cout << "tm " << s << endl; - process(s); - lock.unlock(); - } - lock.lock(); - cout << "tm " << s << " terminated." << endl; + bool cont = true; + while(cont) + { + fd_set cset; + FD_ZERO(&cset); + FD_SET(cs, &cset); + tval tv {SELECT_SECONDS, SELECT_MICRO_SECONDS}; + if (select(0, &cset, nullptr, nullptr, &tv) < 0) + { + TRACE("client select failed") + cont = false; + } + else if (FD_ISSET(cs, &cset)) + { + Buffer buff; + size_t bytes = recv(cs, buff, READ_BUFFER_SIZE, 0); + if (bytes > 0) + { + TRACE("client sent " << bytes << " bytes") + process(cs, buff, bytes); + } + else + { + TRACE("client disconnected") + cont = false; + } + } + } + closesocket(cs); } -void TCP_Srv_Base::start() +bool TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { - cout << "start" << endl; - mRunning = true; + if (argc > 1) + { + return run(static_cast(atoi(argv[1]))); + } + else + { + return run(); + } +} + +bool TCP_Srv_Base::run(const UINT16 port) +{ + TRACE("run ...") + #ifdef _WIN32 + // check for Windows Sockets version 2.2 + { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + cerr << "WSAStartup failed" << endl; + return cleanup; + } + } + #endif + mListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mListenSocket == INVALID_SOCKET) + { + cerr << "socket failed" << endl; + return cleanup(); + } + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + + if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) + { + cerr << "bind failed: port " << port << endl; + return cleanup(); + } + + if (listen(mListenSocket, SOMAXCONN) < 0) + { + cerr << "listen failed" << endl; + return cleanup(); + } + + bool cont = true; + while (cont) + { + fd_set lset; + FD_ZERO(&lset); + FD_SET(mListenSocket, &lset); + { + tval tv {0, 100000}; + if (ok and (select(0, &lset, nullptr, nullptr, &tv) < 0)) + { + cout << "listen select failed" << endl; + ok = false; + } + } + } + + for (int i = 0; i < 10; ++i) { std::thread t(&TCP_Srv_Base::tm, this, i); t.detach(); } } + +bool TCP_Srv_Base::cleanup() +{ + if (mListenSocket != INVALID_SOCKET) + { + closesocket(mListenSocket); + } +#ifdef _WIN32 + WSACleanup(); +#endif + return false; +} + + void TCP_Srv_Base::stop() { cout << "stop" << endl; From 4f5266f2521cfd89ae622c95e4f00fe904f126e5 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sat, 30 Mar 2024 19:42:15 +0100 Subject: [PATCH 11/56] rework TCP_Srv_Base --- sample/Tenouk_SampleSrv.cpp | 42 ++++++++++++++++++++++++--------- src/TCP_Srv_Base.cpp | 46 +++++++++++-------------------------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/sample/Tenouk_SampleSrv.cpp b/sample/Tenouk_SampleSrv.cpp index 0e8538c..269aa57 100644 --- a/sample/Tenouk_SampleSrv.cpp +++ b/sample/Tenouk_SampleSrv.cpp @@ -9,20 +9,12 @@ #ifdef _WIN32 #include #pragma comment(lib, "ws2_32") -using tval = const timeval; #else -#include -#include -#include using SOCKET = int; constexpr SOCKET INVALID_SOCKET = -1; -#define closesocket close -using tval = timeval; #endif #include -#include -#include class SampleSrv { @@ -30,16 +22,42 @@ class SampleSrv inline SampleSrv() = default; ~SampleSrv(); bool init(UINT16 port = 8080); - inline bool init(const char* port) { return init(static_cast(atoi(port))); } + bool init(const INT32 argc, const CONST_C_STRING* const argv); bool run(); private: SOCKET mListenSocket = INVALID_SOCKET; bool cleanup(); }; +// source content + +#ifdef _WIN32 +using tval = const timeval; +#else +#include +#include +#include +#define closesocket close +using tval = timeval; +#endif + #include using std::cout; using std::endl; +#include // atoi +#include + +bool SampleSrv::init(const INT32 argc, const CONST_C_STRING* const argv) +{ + if (argc > 1) + { + return init(static_cast(atoi(argv[1]))); + } + else + { + return init(); + } +} bool SampleSrv::init(const UINT16 port) { @@ -163,10 +181,12 @@ SampleSrv::~SampleSrv() #endif } -int main() +// runtime + +int main(const INT32 argc, const CONST_C_STRING* const argv) { SampleSrv srv; - if (not srv.init()) + if (not srv.init(argc, argv)) { cout << "init failed" << endl; return 1; diff --git a/src/TCP_Srv_Base.cpp b/src/TCP_Srv_Base.cpp index 50124e3..a8e35b3 100644 --- a/src/TCP_Srv_Base.cpp +++ b/src/TCP_Srv_Base.cpp @@ -11,18 +11,6 @@ using std::cout; using std::cerr; using std::endl; -bool TCP_Srv_Base::cleanup() -{ - if (mListenSocket != INVALID_SOCKET) - { - closesocket(mListenSocket); - } -#ifdef _WIN32 - WSACleanup(); -#endif - return false; -} - void TCP_Srv_Base::tm(SOCKET cs) { bool cont = true; @@ -111,22 +99,25 @@ bool TCP_Srv_Base::run(const UINT16 port) fd_set lset; FD_ZERO(&lset); FD_SET(mListenSocket, &lset); + tval tv {SELECT_SECONDS, SELECT_MICRO_SECONDS}; + if (select(0, &lset, nullptr, nullptr, &tv) < 0) { - tval tv {0, 100000}; - if (ok and (select(0, &lset, nullptr, nullptr, &tv) < 0)) - { - cout << "listen select failed" << endl; - ok = false; - } + cerr << "listen select failed" << endl; + cont = false; + } + else if (FD_ISSET(mListenSocket, &lset)) + { + } } - for (int i = 0; i < 10; ++i) - { - std::thread t(&TCP_Srv_Base::tm, this, i); - t.detach(); - } + // for (int i = 0; i < 10; ++i) + // { + // std::thread t(&TCP_Srv_Base::tm, this, i); + // t.detach(); + // } + return true; } bool TCP_Srv_Base::cleanup() @@ -140,12 +131,3 @@ bool TCP_Srv_Base::cleanup() #endif return false; } - - -void TCP_Srv_Base::stop() -{ - cout << "stop" << endl; - mRunning = false; - std::this_thread::sleep_for(std::chrono::milliseconds(200)); -} - From 5880acee9def6db8a1b09bcd142715aa27defe70 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:52:53 +0100 Subject: [PATCH 12/56] rework TCP_Srv_Base --- include/TCP_Srv/TCP_Srv_Base.h | 34 ++++------ src/TCP_Srv_Base.cpp | 116 +++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 71 deletions(-) diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/include/TCP_Srv/TCP_Srv_Base.h index 7f4535d..165d38f 100644 --- a/include/TCP_Srv/TCP_Srv_Base.h +++ b/include/TCP_Srv/TCP_Srv_Base.h @@ -11,27 +11,17 @@ #ifdef _WIN32 #include -#pragma comment(lib, "ws2_32") -using tval = const timeval; #else -#include #include -#include -using SOCKET = int; -constexpr SOCKET INVALID_SOCKET = -1; -#define closesocket close -using tval = timeval; +using SOCKET = INT32; #endif -#include // atoi -#include - #ifndef SELECT_SECONDS #define SELECT_SECONDS 0 #endif -#ifndef SELECT_MICRO_SECONDS -#define SELECT_MICRO_SECONDS 10 +#ifndef SELECT_MILLI_SECONDS +#define SELECT_MILLI_SECONDS 10 #endif #ifndef READ_BUFFER_SIZE @@ -42,19 +32,21 @@ class TCP_Srv_Base { public: inline TCP_Srv_Base() = default; - bool run(UINT16 port = 8080); - bool run(const INT32 argc, const CONST_C_STRING* const argv); + void run(UINT16 port = 8080); + void run(const INT32 argc, const CONST_C_STRING* const argv); protected: using Buffer = CHAR[READ_BUFFER_SIZE]; - virtual void process(SOCKET cs, Buffer buff, size_t n) = 0; - const timeval mSelectTime = {SELECT_SECONDS, SELECT_MICRO_SECONDS}; - + virtual void process(const SOCKET clientSocket, Buffer buff, size_t n) = 0; + private: // thread method - void tm(SOCKET cs); - bool cleanup(); - SOCKET mListenSocket = INVALID_SOCKET; + void tm(SOCKET clientSocket); + + inline static timeval gettval() + { + return {SELECT_SECONDS, SELECT_MILLI_SECONDS * 1000}; + } }; #endif // _H \ No newline at end of file diff --git a/src/TCP_Srv_Base.cpp b/src/TCP_Srv_Base.cpp index a8e35b3..534d45a 100644 --- a/src/TCP_Srv_Base.cpp +++ b/src/TCP_Srv_Base.cpp @@ -3,36 +3,47 @@ #define TRACE_ON #include +#ifdef _WIN32 +using tval = const timeval; +#pragma comment(lib, "ws2_32") +#else +#include +#include +#define closesocket close +constexpr SOCKET INVALID_SOCKET = -1; +using tval = timeval; +#endif + #include #include +#include // atoi #include -using std::cout; using std::cerr; using std::endl; -void TCP_Srv_Base::tm(SOCKET cs) +void TCP_Srv_Base::tm(SOCKET clientSocket) { bool cont = true; while(cont) { fd_set cset; FD_ZERO(&cset); - FD_SET(cs, &cset); - tval tv {SELECT_SECONDS, SELECT_MICRO_SECONDS}; + FD_SET(clientSocket, &cset); + tval tv = gettval(); if (select(0, &cset, nullptr, nullptr, &tv) < 0) { TRACE("client select failed") cont = false; } - else if (FD_ISSET(cs, &cset)) + else if (FD_ISSET(clientSocket, &cset)) { Buffer buff; - size_t bytes = recv(cs, buff, READ_BUFFER_SIZE, 0); + size_t bytes = recv(clientSocket, buff, sizeof(Buffer), 0); if (bytes > 0) { TRACE("client sent " << bytes << " bytes") - process(cs, buff, bytes); + process(clientSocket, buff, bytes); } else { @@ -41,93 +52,98 @@ void TCP_Srv_Base::tm(SOCKET cs) } } } - closesocket(cs); + closesocket(clientSocket); } -bool TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) +void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { if (argc > 1) { - return run(static_cast(atoi(argv[1]))); + run(static_cast(atoi(argv[1]))); } else { - return run(); + run(); } } -bool TCP_Srv_Base::run(const UINT16 port) +void TCP_Srv_Base::run(const UINT16 port) { TRACE("run ...") - #ifdef _WIN32 + bool cont = true; + SOCKET listenSocket = INVALID_SOCKET; + +#ifdef _WIN32 // check for Windows Sockets version 2.2 { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { cerr << "WSAStartup failed" << endl; - return cleanup; + cont = false; } } - #endif - mListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (mListenSocket == INVALID_SOCKET) +#endif + if (cont) { - cerr << "socket failed" << endl; - return cleanup(); + listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listenSocket < 0) + { + cerr << "socket failed" << endl; + cont = false; + } } - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); - - if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) + if (cont) { - cerr << "bind failed: port " << port << endl; - return cleanup(); - } + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); - if (listen(mListenSocket, SOMAXCONN) < 0) + if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) + { + cerr << "bind failed: port " << port << endl; + cont = false; + } + } + if (cont and (listen(listenSocket, SOMAXCONN) < 0)) { cerr << "listen failed" << endl; - return cleanup(); + cont = false; } - bool cont = true; while (cont) { fd_set lset; FD_ZERO(&lset); - FD_SET(mListenSocket, &lset); - tval tv {SELECT_SECONDS, SELECT_MICRO_SECONDS}; + FD_SET(listenSocket, &lset); + tval tv = gettval(); if (select(0, &lset, nullptr, nullptr, &tv) < 0) { cerr << "listen select failed" << endl; cont = false; } - else if (FD_ISSET(mListenSocket, &lset)) + else if (FD_ISSET(listenSocket, &lset)) { - + SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); + if (clientSocket < 0) + { + cerr << "accept failed" << endl; + cont = false; + } + else + { + std::thread t(&TCP_Srv_Base::tm, this, clientSocket); + t.detach(); + } } } - - - // for (int i = 0; i < 10; ++i) - // { - // std::thread t(&TCP_Srv_Base::tm, this, i); - // t.detach(); - // } - return true; -} - -bool TCP_Srv_Base::cleanup() -{ - if (mListenSocket != INVALID_SOCKET) + if (listenSocket != INVALID_SOCKET) { - closesocket(mListenSocket); + closesocket(listenSocket); } #ifdef _WIN32 WSACleanup(); #endif - return false; } + From 4c81fe1ba303a96fd61797adb3b556259ec6ad7e Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 31 Mar 2024 11:50:38 +0200 Subject: [PATCH 13/56] interim / naming --- include/TCP_Srv/TCP_Srv_Base.h | 10 ++-- make/buildAll.sh | 2 +- make/premake5.lua | 50 +++++++++---------- sample/{EchoSrv.h => TCP_Srv_Echo.cpp} | 0 ...dThreads.cpp => sample_Method_Threads.cpp} | 0 ...pleSrv.cpp => sample_Srv_MicroSoft_VS.cpp} | 0 ...uk_SampleSrv.cpp => sample_Srv_Tenouk.cpp} | 0 src/TCP_Srv_Base.cpp | 4 +- 8 files changed, 30 insertions(+), 36 deletions(-) rename sample/{EchoSrv.h => TCP_Srv_Echo.cpp} (100%) rename sample/{MethodThreads.cpp => sample_Method_Threads.cpp} (100%) rename sample/{MS_SampleSrv.cpp => sample_Srv_MicroSoft_VS.cpp} (100%) rename sample/{Tenouk_SampleSrv.cpp => sample_Srv_Tenouk.cpp} (100%) diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/include/TCP_Srv/TCP_Srv_Base.h index 165d38f..de14fda 100644 --- a/include/TCP_Srv/TCP_Srv_Base.h +++ b/include/TCP_Srv/TCP_Srv_Base.h @@ -36,17 +36,15 @@ class TCP_Srv_Base void run(const INT32 argc, const CONST_C_STRING* const argv); protected: - using Buffer = CHAR[READ_BUFFER_SIZE]; + constexpr static UINT32 tmSec = SELECT_SECONDS; + constexpr static UINT32 tmMic = SELECT_MILLI_SECONDS * 1000; + constexpr static UINT32 bufSize = READ_BUFFER_SIZE; + using Buffer = CHAR[bufSize]; virtual void process(const SOCKET clientSocket, Buffer buff, size_t n) = 0; private: // thread method void tm(SOCKET clientSocket); - - inline static timeval gettval() - { - return {SELECT_SECONDS, SELECT_MILLI_SECONDS * 1000}; - } }; #endif // _H \ No newline at end of file diff --git a/make/buildAll.sh b/make/buildAll.sh index 303e054..0177310 100644 --- a/make/buildAll.sh +++ b/make/buildAll.sh @@ -44,7 +44,7 @@ if test ! -z $rem; then git clean -dxf . 2>/dev/null >/dev/null; pre=1; fi if test ! -z $pre; then premake5 gmake2 > /dev/null; fi st=$(date +%s) -rm -f $(ls *.make | grep '_MS_') $(ls *.make | grep '_MS_') +rm -f *_VS.make for fn in $(ls *.make | grep '^[0-9][0-9]'); do mk $fn; done wait diff --git a/make/premake5.lua b/make/premake5.lua index 605b221..28c6730 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -1,13 +1,9 @@ -- ============================================================ -- premake5 build rules for gcc (gmake2): -- ============================================================ +includesBase = { '../include' } buildOptsGcc = { '-std=c++17 -pedantic-errors -Werror -Wall' } -includesBase = { '../include' } --- ============================================================ --- VS setup --- warning level: 4 (high) --- ============================================================ buildOptsVS = { '/std:c++17 /W4' } dummy = { '../sample/dummy.cpp' } @@ -20,7 +16,7 @@ dummy = { '../sample/dummy.cpp' } workspace '01_TCP_Srv_Base' configurations { 'ci' } language 'C++' - objdir 'obj/%{prj.name}' + objdir 'obj' includedirs { includesBase } targetdir 'lib' @@ -37,14 +33,14 @@ workspace '01_TCP_Srv_Base' files { '../src/*.cpp' } -- ============================================================ --- > sample: metod threads --- -> gcc: bin/11_sampleMethodTreads --- -> VS: exe/11_sampleMethodTreads.exe +-- > sample: method threads +-- -> gcc: bin/sample_Method_Threads +-- -> VS: exe/sample_Method_Threads.exe -- ============================================================ -workspace '11_SampleMethodTreads' +workspace '11_sample_Method_Threads' configurations { 'ci' } language 'C++' - objdir 'obj/%{prj.name}' + objdir 'obj' filter { 'action:vs*' } warnings 'high' @@ -56,28 +52,28 @@ workspace '11_SampleMethodTreads' linkoptions { '-pthread' } targetdir 'bin' - project 'sampleMethodTreads' + project 'sample_Method_Threads' kind 'ConsoleApp' - files { '../sample/MethodThreads.cpp' } + files { '../sample/sample_Method_Threads.cpp' } -- ============================================================ -- > Microsoft sample: echo server -- VS only --- -> exe/12_MS_SamplSrv.exe +-- -> exe/sample_Srv_MicroSoft_VS.exe -- ============================================================ -workspace '12_MS_SamplSrv' +workspace '12_sample_Srv_MicroSoft_VS' configurations { 'ci' } language 'C++' - objdir 'obj/%{prj.name}' - kind 'ConsoleApp' + objdir 'obj' - project 'MS_SamplSrv' + project 'sample_Srv_MicroSoft_VS' + kind 'ConsoleApp' filter { 'action:vs*' } warnings 'high' buildoptions { buildOptsVS } targetdir 'exe' - files { '../sample/MS_SampleSrv.cpp' } + files { '../sample/sample_Srv_MicroSoft_VS.cpp' } filter { 'action:gmake*' } buildoptions { buildOptsGcc } @@ -86,18 +82,19 @@ workspace '12_MS_SamplSrv' -- ============================================================ -- > Tenouk sample: echo server --- -> gcc: bin/12_Tenouk_SampleSrv --- -> VS: exe/12_Tenouk_SampleSrv.exe +-- -> gcc: bin/sample_Srv_Tenouk +-- -> VS: exe/sample_Srv_Tenouk.exe -- ============================================================ -workspace '13_Tenouk_SampleSrv' +workspace '13_sample_Srv_Tenouk' configurations { 'ci' } language 'C++' - objdir 'obj/%{prj.name}' - kind 'ConsoleApp' - includedirs { includesBase } - files { '../sample/Tenouk_SampleSrv.cpp' } + objdir 'obj' project 'Tenouk_SampleSrv' + kind 'ConsoleApp' + includedirs { includesBase } + files { '../sample/sample_Srv_Tenouk.cpp' } + filter { 'action:vs*' } warnings 'high' buildoptions { buildOptsVS } @@ -106,4 +103,3 @@ workspace '13_Tenouk_SampleSrv' filter { 'action:gmake*' } buildoptions { buildOptsGcc } targetdir 'bin' - \ No newline at end of file diff --git a/sample/EchoSrv.h b/sample/TCP_Srv_Echo.cpp similarity index 100% rename from sample/EchoSrv.h rename to sample/TCP_Srv_Echo.cpp diff --git a/sample/MethodThreads.cpp b/sample/sample_Method_Threads.cpp similarity index 100% rename from sample/MethodThreads.cpp rename to sample/sample_Method_Threads.cpp diff --git a/sample/MS_SampleSrv.cpp b/sample/sample_Srv_MicroSoft_VS.cpp similarity index 100% rename from sample/MS_SampleSrv.cpp rename to sample/sample_Srv_MicroSoft_VS.cpp diff --git a/sample/Tenouk_SampleSrv.cpp b/sample/sample_Srv_Tenouk.cpp similarity index 100% rename from sample/Tenouk_SampleSrv.cpp rename to sample/sample_Srv_Tenouk.cpp diff --git a/src/TCP_Srv_Base.cpp b/src/TCP_Srv_Base.cpp index 534d45a..e6fa67e 100644 --- a/src/TCP_Srv_Base.cpp +++ b/src/TCP_Srv_Base.cpp @@ -30,7 +30,7 @@ void TCP_Srv_Base::tm(SOCKET clientSocket) fd_set cset; FD_ZERO(&cset); FD_SET(clientSocket, &cset); - tval tv = gettval(); + tval tv { tmSec, tmMic }; if (select(0, &cset, nullptr, nullptr, &tv) < 0) { TRACE("client select failed") @@ -117,7 +117,7 @@ void TCP_Srv_Base::run(const UINT16 port) fd_set lset; FD_ZERO(&lset); FD_SET(listenSocket, &lset); - tval tv = gettval(); + tval tv { tmSec, tmMic }; if (select(0, &lset, nullptr, nullptr, &tv) < 0) { cerr << "listen select failed" << endl; From 939f23ee059eef5cb8c3b6db90017487963be029 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 31 Mar 2024 13:10:11 +0200 Subject: [PATCH 14/56] interim / naming --- .gitignore | 3 +++ make/buildAll.sh | 2 +- make/{buildVS.md => howto_build_VS.md} | 35 ++++++++------------------ make/premake5.lua | 28 ++++++++------------- py/echoCommon.py | 10 ++++---- py/echoParallelClient.py | 14 +++++------ sample/TCP_Srv_Echo.cpp | 18 ------------- sample/TCP_Srv_Echo.h | 20 +++++++++++++++ 8 files changed, 57 insertions(+), 73 deletions(-) rename make/{buildVS.md => howto_build_VS.md} (64%) delete mode 100644 sample/TCP_Srv_Echo.cpp create mode 100644 sample/TCP_Srv_Echo.h diff --git a/.gitignore b/.gitignore index 815c518..d870998 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ exe # general tmp.* tmp_.* + +# sample +!TCP_Srv*.make \ No newline at end of file diff --git a/make/buildAll.sh b/make/buildAll.sh index 0177310..7f4a22c 100644 --- a/make/buildAll.sh +++ b/make/buildAll.sh @@ -46,7 +46,7 @@ if test ! -z $pre; then premake5 gmake2 > /dev/null; fi st=$(date +%s) rm -f *_VS.make -for fn in $(ls *.make | grep '^[0-9][0-9]'); do mk $fn; done +for fn in *.make; do mk $fn; done wait echo binaries: for b in bin/*; do echo "- $b"; done diff --git a/make/buildVS.md b/make/howto_build_VS.md similarity index 64% rename from make/buildVS.md rename to make/howto_build_VS.md index f60e8cc..a196cff 100644 --- a/make/buildVS.md +++ b/make/howto_build_VS.md @@ -10,9 +10,6 @@ $> premake5 --help Usage: premake5 [options] action [arguments] -OPTIONS - General - --file=FILE Read FILE as a Premake script; default is 'premake5.lua' - ACTIONS vs2005 Generate Visual Studio 2005 project files vs2008 Generate Visual Studio 2008 project files @@ -27,31 +24,21 @@ ACTIONS ### 1.2) generate solution with your VS version e.g. VS 2019 ````shell -$> premake5 --file=premake5_vs.lua vs2019 +$> premake5 vs2019 Building configurations... Running action 'vs2019'... -Generated cpputest.sln... -Generated cpputest.vcxproj... -Generated cpputest.vcxproj.filters... -Generated tests.sln... -Generated tests.vcxproj... -Generated tests.vcxproj.filters... -Generated dstw.sln... -Generated dstw.vcxproj... -Generated dstw.vcxproj.filters... -Done (130ms). +Generated TCP_Srv_Base.sln... +Generated TCP_Srv_Base.vcxproj... +Generated sample_Method_Threads.sln... +Generated sample_Method_Threads.vcxproj... +Generated sample_Srv_MicroSoft_VS.sln... +Generated sample_Srv_MicroSoft_VS.vcxproj... +Generated sample_Srv_Tenouk.sln... +Generated sample_Srv_Tenouk.vcxproj... +Done (49ms). ```` ## 2) build executables in VS -### 2.1) build and run module tests -- open _cpputest.sln_ -- build -- open _tests.sln_ -- build and run - -### 2.2) build and run application -- open _gendata.sln_ -- build and run -- open _dstw.sln_ +- open solution (*.sln) - build and run ## 3) VS keyboard shortcuts diff --git a/make/premake5.lua b/make/premake5.lua index 28c6730..1a1f8ac 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -1,5 +1,5 @@ -- ============================================================ --- premake5 build rules for gcc (gmake2): +-- premake5 build rules -- ============================================================ includesBase = { '../include' } @@ -9,11 +9,9 @@ buildOptsVS = { '/std:c++17 /W4' } dummy = { '../sample/dummy.cpp' } -- ============================================================ --- > TCP_Srv_Base library --- -> VS: lib/TCP_Srv_Base.lib --- -> gcc: lib/libTCP_Srv_Base.a +-- TCP_Srv_Base library -- ============================================================ -workspace '01_TCP_Srv_Base' +workspace 'TCP_Srv_Base' configurations { 'ci' } language 'C++' objdir 'obj' @@ -33,11 +31,9 @@ workspace '01_TCP_Srv_Base' files { '../src/*.cpp' } -- ============================================================ --- > sample: method threads --- -> gcc: bin/sample_Method_Threads --- -> VS: exe/sample_Method_Threads.exe +-- sample of method threads -- ============================================================ -workspace '11_sample_Method_Threads' +workspace 'sample_Method_Threads' configurations { 'ci' } language 'C++' objdir 'obj' @@ -57,11 +53,9 @@ workspace '11_sample_Method_Threads' files { '../sample/sample_Method_Threads.cpp' } -- ============================================================ --- > Microsoft sample: echo server --- VS only --- -> exe/sample_Srv_MicroSoft_VS.exe +-- sample echo server Microsoft VS only -- ============================================================ -workspace '12_sample_Srv_MicroSoft_VS' +workspace 'sample_Srv_MicroSoft_VS' configurations { 'ci' } language 'C++' objdir 'obj' @@ -81,16 +75,14 @@ workspace '12_sample_Srv_MicroSoft_VS' targetdir 'bin' -- ============================================================ --- > Tenouk sample: echo server --- -> gcc: bin/sample_Srv_Tenouk --- -> VS: exe/sample_Srv_Tenouk.exe +-- sample echo server Tenouk -- ============================================================ -workspace '13_sample_Srv_Tenouk' +workspace 'sample_Srv_Tenouk' configurations { 'ci' } language 'C++' objdir 'obj' - project 'Tenouk_SampleSrv' + project 'sample_Srv_Tenouk' kind 'ConsoleApp' includedirs { includesBase } files { '../sample/sample_Srv_Tenouk.cpp' } diff --git a/py/echoCommon.py b/py/echoCommon.py index c7170ba..5c3e5bb 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -14,8 +14,8 @@ def getSocketData(*args): prot = socket.AF_INET addr = '127.0.0.1' port = 8080 - num = 1 - ths = 1 + loops = 1 + threads = 1 message = 'Hello world' for arg in args: if rx4.match(arg): @@ -26,9 +26,9 @@ def getSocketData(*args): elif rxP.match(arg): port = int(arg) elif rxN.match(arg): - num = int(rxN.match(arg).group(1)) + loops = int(rxN.match(arg).group(1)) elif rxT.match(arg): - ths = int(rxT.match(arg).group(1)) + threads = int(rxT.match(arg).group(1)) else: message = arg print() @@ -36,4 +36,4 @@ def getSocketData(*args): print('prot:', 'ipv6' if prot == socket.AF_INET6 else 'ipv4') print('port:', port) - return [socket.socket(prot, socket.SOCK_STREAM), addr, port, message, num, ths, prot] \ No newline at end of file + return [socket.socket(prot, socket.SOCK_STREAM), addr, port, message, loops, threads, prot] \ No newline at end of file diff --git a/py/echoParallelClient.py b/py/echoParallelClient.py index 64206d7..86b5dd7 100644 --- a/py/echoParallelClient.py +++ b/py/echoParallelClient.py @@ -11,23 +11,23 @@ from time import sleep def getEchoes(*args): - [addr, port, message, num, nths, prot] = getSocketData(*args)[1:] + [addr, port, message, loops, threads, prot] = getSocketData(*args)[1:] logconfig(format="%(asctime)s %(message)s", level=INFO, datefmt="%H:%M:%S") - for n in range(nths): - Thread(target=tfunc, args=(n + 1, prot, addr, port, message, num)).start() + for n in range(threads): + Thread(target=tfunc, args=(n + 1, prot, addr, port, message, loops)).start() -def tfunc(nr:int, prot:int, addr:str, port:int, message:str, num:int): +def tfunc(nr:int, prot:int, addr:str, port:int, message:str, loops:int): try: log("%02d connecting ..." % nr) s = socket(prot, SOCK_STREAM) s.connect((addr, port)) log("%02d connected." % nr) - for n in range(num): + for n in range(loops): if n > 0: sleep(1) - log("%02d %d / %d send: '%s'" % (nr, n + 1, num, message)) + log("%02d %d / %d send: '%s'" % (nr, n + 1, loops, message)) s.sendall(message.encode()) data = s.recv(1024) - log("%02d %d / %d recv: '%s'" % (nr, n + 1, num, data.decode())) + log("%02d %d / %d recv: '%s'" % (nr, n + 1, loops, data.decode())) s.close() except Exception as e: print(e) diff --git a/sample/TCP_Srv_Echo.cpp b/sample/TCP_Srv_Echo.cpp deleted file mode 100644 index b30ca88..0000000 --- a/sample/TCP_Srv_Echo.cpp +++ /dev/null @@ -1,18 +0,0 @@ - -#include - -#pragma once -#ifndef ECHO_SRV_H -#define ECHO_SRV_H - -class EchoSrv : public TCP_Srv_Base -{ -public: - inline EchoSrv() = default; -protected: - void process(int s) override; -private: - /* data */ -}; - -#endif // _H \ No newline at end of file diff --git a/sample/TCP_Srv_Echo.h b/sample/TCP_Srv_Echo.h new file mode 100644 index 0000000..7b67657 --- /dev/null +++ b/sample/TCP_Srv_Echo.h @@ -0,0 +1,20 @@ + +#pragma once +#ifndef TCP_SRV_ECHO_H +#define TCP_SRV_ECHO_H + +#include +#include + +class TCP_Srv_Echo : public TCP_Srv_Base +{ +public: + inline TCP_Srv_Echo() = default; +protected: + void process(const SOCKET clientSocket, Buffer buff, size_t n) final; +private: + // prevent from parallel output + std::mutex mMtx; +}; + +#endif // _H From 8e3ee2fd5737c0149d7c43e0d2b9f8037b5a4c48 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:03:00 +0200 Subject: [PATCH 15/56] progress, ready for cleanup --- include/TCP_Srv/TCP_Srv_Base.h | 28 ++++++- make/TCP_Srv_Echo_silent.make | 135 +++++++++++++++++++++++++++++++ make/TCP_Srv_Echo_verbose.make | 135 +++++++++++++++++++++++++++++++ make/premake5.lua | 64 ++++++++++----- py/echoCommon.py | 2 +- sample/TCP_Srv_Echo.cpp | 20 +++++ sample/TCP_Srv_Echo.h | 4 +- sample/TCP_Srv_Echo_main.cpp | 11 +++ sample/sample_Method_Threads.cpp | 6 +- src/TCP_Srv_Base.cpp | 110 ++++++++++++++++--------- 10 files changed, 448 insertions(+), 67 deletions(-) create mode 100644 make/TCP_Srv_Echo_silent.make create mode 100644 make/TCP_Srv_Echo_verbose.make create mode 100644 sample/TCP_Srv_Echo.cpp create mode 100644 sample/TCP_Srv_Echo_main.cpp diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/include/TCP_Srv/TCP_Srv_Base.h index de14fda..c1599fb 100644 --- a/include/TCP_Srv/TCP_Srv_Base.h +++ b/include/TCP_Srv/TCP_Srv_Base.h @@ -15,6 +15,7 @@ #include using SOCKET = INT32; #endif +#include #ifndef SELECT_SECONDS #define SELECT_SECONDS 0 @@ -32,7 +33,9 @@ class TCP_Srv_Base { public: inline TCP_Srv_Base() = default; + // run with given port void run(UINT16 port = 8080); + // run with port from 1st CLI argument void run(const INT32 argc, const CONST_C_STRING* const argv); protected: @@ -40,11 +43,28 @@ class TCP_Srv_Base constexpr static UINT32 tmMic = SELECT_MILLI_SECONDS * 1000; constexpr static UINT32 bufSize = READ_BUFFER_SIZE; using Buffer = CHAR[bufSize]; - virtual void process(const SOCKET clientSocket, Buffer buff, size_t n) = 0; - + + // process received data + // must be implemented by derived class + virtual void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) = 0; + + // prevent from parallel output + std::mutex mMtxOut; + using mutexlock = std::unique_lock; + private: - // thread method - void tm(SOCKET clientSocket); + // thread method + void tm(SOCKET clientSocket, UINT32 nr); + // thread count + UINT32 mCnt = 0; + // thread number + UINT32 mNum = 0; + // mutex for thread count and number + std::mutex mMtxStat; + // start thread with increased thread number and count + void startThread(SOCKET clientSocket); + // decrease thread count, reset thread number when count is 0 + void endOfThread(); }; #endif // _H \ No newline at end of file diff --git a/make/TCP_Srv_Echo_silent.make b/make/TCP_Srv_Echo_silent.make new file mode 100644 index 0000000..7f20b2e --- /dev/null +++ b/make/TCP_Srv_Echo_silent.make @@ -0,0 +1,135 @@ +# Alternative GNU Make project makefile autogenerated by Premake + +ifndef config + config=ci +endif + +ifndef verbose + SILENT = @ +endif + +.PHONY: clean prebuild + +SHELLTYPE := posix +ifeq ($(shell echo "test"), "test") + SHELLTYPE := msdos +endif + +# Configurations +# ############################################# + +RESCOMP = windres +TARGETDIR = bin +TARGET = $(TARGETDIR)/TCP_Srv_Echo_silent +OBJDIR = obj/TCP_Srv_Echo_silent +DEFINES += +INCLUDES += -I../include +FORCE_INCLUDE += +ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall +ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) +LIBS += +LDDEPS += +ALL_LDFLAGS += $(LDFLAGS) -s -pthread +LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) +define PREBUILDCMDS +endef +define PRELINKCMDS +endef +define POSTBUILDCMDS +endef + +# Per File Configurations +# ############################################# + + +# File sets +# ############################################# + +GENERATED := +OBJECTS := + +GENERATED += $(OBJDIR)/TCP_Srv_Base.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o +OBJECTS += $(OBJDIR)/TCP_Srv_Base.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o + +# Rules +# ############################################# + +all: $(TARGET) + @: + +$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) + $(PRELINKCMDS) + @echo Linking TCP_Srv_Echo_silent + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif + +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + +clean: + @echo Cleaning TCP_Srv_Echo_silent +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(GENERATED) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif + +prebuild: | $(OBJDIR) + $(PREBUILDCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) +$(GCH): $(PCH) | prebuild + @echo $(notdir $<) + $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) touch "$@" +else + $(SILENT) echo $null >> "$@" +endif +else +$(OBJECTS): | prebuild +endif + + +# File Rules +# ############################################# + +$(OBJDIR)/TCP_Srv_Echo.o: ../sample/TCP_Srv_Echo.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Echo_main.o: ../sample/TCP_Srv_Echo_main.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Base.o: ../src/TCP_Srv_Base.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(PCH_PLACEHOLDER).d +endif \ No newline at end of file diff --git a/make/TCP_Srv_Echo_verbose.make b/make/TCP_Srv_Echo_verbose.make new file mode 100644 index 0000000..ba1bcbd --- /dev/null +++ b/make/TCP_Srv_Echo_verbose.make @@ -0,0 +1,135 @@ +# Alternative GNU Make project makefile autogenerated by Premake + +ifndef config + config=ci +endif + +ifndef verbose + SILENT = @ +endif + +.PHONY: clean prebuild + +SHELLTYPE := posix +ifeq ($(shell echo "test"), "test") + SHELLTYPE := msdos +endif + +# Configurations +# ############################################# + +RESCOMP = windres +TARGETDIR = bin +TARGET = $(TARGETDIR)/TCP_Srv_Echo_verbose +OBJDIR = obj/TCP_Srv_Echo_verbose +DEFINES += +INCLUDES += -I../include +FORCE_INCLUDE += +ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -DTRACE_ON +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -DTRACE_ON +ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) +LIBS += +LDDEPS += +ALL_LDFLAGS += $(LDFLAGS) -s -pthread +LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) +define PREBUILDCMDS +endef +define PRELINKCMDS +endef +define POSTBUILDCMDS +endef + +# Per File Configurations +# ############################################# + + +# File sets +# ############################################# + +GENERATED := +OBJECTS := + +GENERATED += $(OBJDIR)/TCP_Srv_Base.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o +OBJECTS += $(OBJDIR)/TCP_Srv_Base.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o + +# Rules +# ############################################# + +all: $(TARGET) + @: + +$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) + $(PRELINKCMDS) + @echo Linking TCP_Srv_Echo_verbose + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif + +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + +clean: + @echo Cleaning TCP_Srv_Echo_verbose +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(GENERATED) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif + +prebuild: | $(OBJDIR) + $(PREBUILDCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) +$(GCH): $(PCH) | prebuild + @echo $(notdir $<) + $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) touch "$@" +else + $(SILENT) echo $null >> "$@" +endif +else +$(OBJECTS): | prebuild +endif + + +# File Rules +# ############################################# + +$(OBJDIR)/TCP_Srv_Echo.o: ../sample/TCP_Srv_Echo.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Echo_main.o: ../sample/TCP_Srv_Echo_main.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Base.o: ../src/TCP_Srv_Base.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(PCH_PLACEHOLDER).d +endif \ No newline at end of file diff --git a/make/premake5.lua b/make/premake5.lua index 1a1f8ac..a2cc9c9 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -3,32 +3,58 @@ -- ============================================================ includesBase = { '../include' } -buildOptsGcc = { '-std=c++17 -pedantic-errors -Werror -Wall' } -buildOptsVS = { '/std:c++17 /W4' } +buildOptsGcc = '-std=c++17 -pedantic-errors -Werror -Wall ' +buildOptsVS = '/std:c++17 /W4 ' dummy = { '../sample/dummy.cpp' } -- ============================================================ --- TCP_Srv_Base library +-- sample TCP_Srv_Echo based on TCP_Srv_Base +-- with tracing off -- ============================================================ -workspace 'TCP_Srv_Base' +workspace 'TCP_Srv_Echo_silent' configurations { 'ci' } language 'C++' - objdir 'obj' - includedirs { includesBase } - targetdir 'lib' + objdir 'obj/%{prj.name}' - filter { 'action:vs*' } - warnings 'high' - buildoptions { buildOptsVS } + project 'TCP_Srv_Echo_silent' + includedirs { includesBase } + kind 'ConsoleApp' + files { '../src/*.cpp', '../sample/TCP_Srv_Echo*.cpp' } - filter { 'action:gmake*' } - buildoptions { buildOptsGcc } + filter { 'action:vs*' } + warnings 'high' + buildoptions { buildOptsVS .. '/wd4100' } + targetdir 'exe' + + filter { 'action:gmake*' } + buildoptions { buildOptsGcc } + linkoptions { '-pthread' } + targetdir 'bin' - project 'TCP_Srv_Base' - kind 'StaticLib' - targetdir 'lib' - files { '../src/*.cpp' } +-- ============================================================ +-- sample TCP_Srv_Echo based on TCP_Srv_Base +-- with tracing on +-- ============================================================ +workspace 'TCP_Srv_Echo_verbose' + configurations { 'ci' } + language 'C++' + objdir 'obj/%{prj.name}' + + project 'TCP_Srv_Echo_verbose' + includedirs { includesBase } + kind 'ConsoleApp' + files { '../src/*.cpp', '../sample/TCP_Srv_Echo*.cpp' } + + filter { 'action:vs*' } + warnings 'high' + buildoptions { buildOptsVS .. '/DTRACE_ON' } + targetdir 'exe' + + filter { 'action:gmake*' } + buildoptions { buildOptsGcc .. '-DTRACE_ON' } + linkoptions { '-pthread' } + targetdir 'bin' -- ============================================================ -- sample of method threads @@ -36,7 +62,7 @@ workspace 'TCP_Srv_Base' workspace 'sample_Method_Threads' configurations { 'ci' } language 'C++' - objdir 'obj' + objdir 'obj/%{prj.name}' filter { 'action:vs*' } warnings 'high' @@ -58,7 +84,7 @@ workspace 'sample_Method_Threads' workspace 'sample_Srv_MicroSoft_VS' configurations { 'ci' } language 'C++' - objdir 'obj' + objdir 'obj/%{prj.name}' project 'sample_Srv_MicroSoft_VS' kind 'ConsoleApp' @@ -80,7 +106,7 @@ workspace 'sample_Srv_MicroSoft_VS' workspace 'sample_Srv_Tenouk' configurations { 'ci' } language 'C++' - objdir 'obj' + objdir 'obj/%{prj.name}' project 'sample_Srv_Tenouk' kind 'ConsoleApp' diff --git a/py/echoCommon.py b/py/echoCommon.py index 5c3e5bb..4faf0d7 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -16,7 +16,7 @@ def getSocketData(*args): port = 8080 loops = 1 threads = 1 - message = 'Hello world' + message = 'hello world' for arg in args: if rx4.match(arg): addr = arg diff --git a/sample/TCP_Srv_Echo.cpp b/sample/TCP_Srv_Echo.cpp new file mode 100644 index 0000000..4616158 --- /dev/null +++ b/sample/TCP_Srv_Echo.cpp @@ -0,0 +1,20 @@ +#include "TCP_Srv_Echo.h" + +#include +using std::cout; +using std::endl; +#include + +void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t size, const UINT32 nr) +{ + send(clientSocket, buff, size, 0); +#ifdef TRACE_ON + mutexlock lock(mMtxOut); + cout << std::setw(3) << nr << " <- "; + for (size_t n = 0; n < size; ++n) + { + cout << buff[n]; + } + cout << " ->" << endl; +#endif +} diff --git a/sample/TCP_Srv_Echo.h b/sample/TCP_Srv_Echo.h index 7b67657..04402df 100644 --- a/sample/TCP_Srv_Echo.h +++ b/sample/TCP_Srv_Echo.h @@ -11,10 +11,12 @@ class TCP_Srv_Echo : public TCP_Srv_Base public: inline TCP_Srv_Echo() = default; protected: - void process(const SOCKET clientSocket, Buffer buff, size_t n) final; + void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) final; private: // prevent from parallel output std::mutex mMtx; + + }; #endif // _H diff --git a/sample/TCP_Srv_Echo_main.cpp b/sample/TCP_Srv_Echo_main.cpp new file mode 100644 index 0000000..477b18e --- /dev/null +++ b/sample/TCP_Srv_Echo_main.cpp @@ -0,0 +1,11 @@ +#include "TCP_Srv_Echo.h" +#include +#include +#include +int main(const INT32 argc, const CONST_C_STRING* const argv) +{ + std::setlocale(LC_ALL, "de_DE.UTF-8"); + TCP_Srv_Echo srv; + srv.run(argc, argv); + return 0; +} \ No newline at end of file diff --git a/sample/sample_Method_Threads.cpp b/sample/sample_Method_Threads.cpp index dc5c860..7547651 100644 --- a/sample/sample_Method_Threads.cpp +++ b/sample/sample_Method_Threads.cpp @@ -34,7 +34,7 @@ using std::endl; using std::this_thread::sleep_for; using std::chrono::milliseconds; using std::chrono::seconds; -using mutextlock = std::unique_lock; +using mutexlock = std::unique_lock; void Test::start() { @@ -53,7 +53,7 @@ void Test::start() void Test::stop() { - mutextlock lock(mMtx); + mutexlock lock(mMtx); cout << "stop ..." << endl; mRunning = false; lock.unlock(); @@ -62,7 +62,7 @@ void Test::stop() void Test::tm(int s) { - mutextlock lock(mMtx); + mutexlock lock(mMtx); lock.unlock(); while (mRunning) { diff --git a/src/TCP_Srv_Base.cpp b/src/TCP_Srv_Base.cpp index e6fa67e..65c0803 100644 --- a/src/TCP_Srv_Base.cpp +++ b/src/TCP_Srv_Base.cpp @@ -1,16 +1,16 @@ #include -#define TRACE_ON -#include - #ifdef _WIN32 +// timevel can be used const in VS using tval = const timeval; +// required lib for Winsock #pragma comment(lib, "ws2_32") #else #include #include #define closesocket close constexpr SOCKET INVALID_SOCKET = -1; +// timevel must be non const gcc using tval = timeval; #endif @@ -19,12 +19,29 @@ using tval = timeval; #include // atoi #include +using std::cout; using std::cerr; using std::endl; +#include +using std::setw; + +// tracing +#ifdef TRACE_ON +// general trace +#define TRACE(MSG) cout << MSG << endl; +// mutex locked trace with thread number +#define TRACE_TM(MSG) { mutexlock lock(mMtxOut); TRACE(setw(3) << nr << ' ' << MSG) } +#else +#define TRACE(MSG) +#define TRACE_TM(MSG) +#endif + -void TCP_Srv_Base::tm(SOCKET clientSocket) +void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) { bool cont = true; + TRACE_TM("CON") + while(cont) { fd_set cset; @@ -33,26 +50,27 @@ void TCP_Srv_Base::tm(SOCKET clientSocket) tval tv { tmSec, tmMic }; if (select(0, &cset, nullptr, nullptr, &tv) < 0) { - TRACE("client select failed") + TRACE_TM("ERR select") cont = false; } else if (FD_ISSET(clientSocket, &cset)) { Buffer buff; - size_t bytes = recv(clientSocket, buff, sizeof(Buffer), 0); - if (bytes > 0) + size_t size = recv(clientSocket, buff, sizeof(Buffer), 0); + if (size > 0) { - TRACE("client sent " << bytes << " bytes") - process(clientSocket, buff, bytes); + TRACE_TM("<- " << size) + process(clientSocket, buff, size, nr); } else { - TRACE("client disconnected") + TRACE_TM("EX") cont = false; } } } closesocket(clientSocket); + endOfThread(); } void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) @@ -67,9 +85,12 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) } } +// trace error and exit loop +#define TRACE_ERR_X(MSG) { cerr << endl << MSG << endl; cont = false; } + void TCP_Srv_Base::run(const UINT16 port) { - TRACE("run ...") + std::cout << "..." << endl; bool cont = true; SOCKET listenSocket = INVALID_SOCKET; @@ -78,21 +99,16 @@ void TCP_Srv_Base::run(const UINT16 port) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) - { - cerr << "WSAStartup failed" << endl; - cont = false; - } + TRACE_ERR_X("WSAStartup failed") } #endif + // create socket if (cont) { listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listenSocket < 0) - { - cerr << "socket failed" << endl; - cont = false; - } + if (listenSocket < 0) TRACE_ERR_X("socket failed") } + // bind socket to port if (cont) { sockaddr_in addr; @@ -101,43 +117,38 @@ void TCP_Srv_Base::run(const UINT16 port) addr.sin_port = htons(port); if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) - { - cerr << "bind failed: port " << port << endl; - cont = false; - } + TRACE_ERR_X("bind failed: port " << port) } + // listen to socket if (cont and (listen(listenSocket, SOMAXCONN) < 0)) - { - cerr << "listen failed" << endl; - cont = false; - } + TRACE_ERR_X("listen failed") + // select and accept loop + TRACE("running on port " << port << endl << "press Ctrl+C to stop") while (cont) { + // select fd_set lset; FD_ZERO(&lset); FD_SET(listenSocket, &lset); tval tv { tmSec, tmMic }; if (select(0, &lset, nullptr, nullptr, &tv) < 0) - { - cerr << "listen select failed" << endl; - cont = false; - } + TRACE_ERR_X("listen select failed") + + // accept to new client socket if listen socket is set else if (FD_ISSET(listenSocket, &lset)) { SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); - if (clientSocket < 0) - { - cerr << "accept failed" << endl; - cont = false; - } + if (clientSocket < 0) TRACE_ERR_X("accept failed") + // start thread with client socket else { - std::thread t(&TCP_Srv_Base::tm, this, clientSocket); - t.detach(); + startThread(clientSocket); } } } + // should not be reached: + // clean up in case of error if (listenSocket != INVALID_SOCKET) { closesocket(listenSocket); @@ -147,3 +158,24 @@ void TCP_Srv_Base::run(const UINT16 port) #endif } +void TCP_Srv_Base::startThread(SOCKET clientSocket) +{ + mutexlock lock(mMtxStat); + ++mCnt; + ++mNum; + std::thread(&TCP_Srv_Base::tm, this, clientSocket, mNum).detach(); +} + +void TCP_Srv_Base::endOfThread() +{ + mutexlock lock(mMtxStat); + if (mCnt > 0) + { + --mCnt; + if (mCnt == 0) + { + mNum = 0; + TRACE("--- no clients ---") + } + } +} \ No newline at end of file From a2d0946fdbbc97a2104d6f877d1e35ef89d62677 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:05:21 +0200 Subject: [PATCH 16/56] removed dev samples --- make/premake5.lua | 66 ---------- sample/dummy.cpp | 11 -- sample/sample_Method_Threads.cpp | 87 ------------- sample/sample_Srv_MicroSoft_VS.cpp | 142 -------------------- sample/sample_Srv_Tenouk.cpp | 201 ----------------------------- 5 files changed, 507 deletions(-) delete mode 100644 sample/dummy.cpp delete mode 100644 sample/sample_Method_Threads.cpp delete mode 100644 sample/sample_Srv_MicroSoft_VS.cpp delete mode 100644 sample/sample_Srv_Tenouk.cpp diff --git a/make/premake5.lua b/make/premake5.lua index a2cc9c9..445aea4 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -55,69 +55,3 @@ workspace 'TCP_Srv_Echo_verbose' buildoptions { buildOptsGcc .. '-DTRACE_ON' } linkoptions { '-pthread' } targetdir 'bin' - --- ============================================================ --- sample of method threads --- ============================================================ -workspace 'sample_Method_Threads' - configurations { 'ci' } - language 'C++' - objdir 'obj/%{prj.name}' - - filter { 'action:vs*' } - warnings 'high' - buildoptions { buildOptsVS } - targetdir 'exe' - - filter { 'action:gmake*' } - buildoptions { buildOptsGcc } - linkoptions { '-pthread' } - targetdir 'bin' - - project 'sample_Method_Threads' - kind 'ConsoleApp' - files { '../sample/sample_Method_Threads.cpp' } - --- ============================================================ --- sample echo server Microsoft VS only --- ============================================================ -workspace 'sample_Srv_MicroSoft_VS' - configurations { 'ci' } - language 'C++' - objdir 'obj/%{prj.name}' - - project 'sample_Srv_MicroSoft_VS' - kind 'ConsoleApp' - - filter { 'action:vs*' } - warnings 'high' - buildoptions { buildOptsVS } - targetdir 'exe' - files { '../sample/sample_Srv_MicroSoft_VS.cpp' } - - filter { 'action:gmake*' } - buildoptions { buildOptsGcc } - files { dummy } - targetdir 'bin' - --- ============================================================ --- sample echo server Tenouk --- ============================================================ -workspace 'sample_Srv_Tenouk' - configurations { 'ci' } - language 'C++' - objdir 'obj/%{prj.name}' - - project 'sample_Srv_Tenouk' - kind 'ConsoleApp' - includedirs { includesBase } - files { '../sample/sample_Srv_Tenouk.cpp' } - - filter { 'action:vs*' } - warnings 'high' - buildoptions { buildOptsVS } - targetdir 'exe' - - filter { 'action:gmake*' } - buildoptions { buildOptsGcc } - targetdir 'bin' diff --git a/sample/dummy.cpp b/sample/dummy.cpp deleted file mode 100644 index e76b765..0000000 --- a/sample/dummy.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// ============================================================ -// dummy source file for premake5 -// ============================================================ -// created by Manfred Sorgo - -#include -int main() -{ - std::cout << "Hello, World!" << std::endl; - return 0; -} \ No newline at end of file diff --git a/sample/sample_Method_Threads.cpp b/sample/sample_Method_Threads.cpp deleted file mode 100644 index 7547651..0000000 --- a/sample/sample_Method_Threads.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// ============================================================ -// sample of threads with object methods -// ============================================================ -// created by Manfred Sorgo - -// header content -#include - -class Test -{ -public: - inline Test(int num = 0) : - mOff(num), - mRunning(false) - {} - void start(); - void stop(); -private: - // thread method - void tm(int s); - // context number offset - const int mOff; - // thread synchronization - std::mutex mMtx; - // thread control - bool mRunning; -}; - -// source content -#include -#include -using std::cout; -using std::endl; -using std::this_thread::sleep_for; -using std::chrono::milliseconds; -using std::chrono::seconds; -using mutexlock = std::unique_lock; - -void Test::start() -{ - cout << "start" << endl; - mRunning = true; - for (int i = 0; i < 10; ++i) - { - // this is the way to start a thread with a class method - // 1st parameter: class::method - // 2nd parameter: object -> this - // following parameters: method parameters - std::thread t(&Test::tm, this, i); - t.detach(); - } -} - -void Test::stop() -{ - mutexlock lock(mMtx); - cout << "stop ..." << endl; - mRunning = false; - lock.unlock(); - sleep_for(milliseconds(300)); -} - -void Test::tm(int s) -{ - mutexlock lock(mMtx); - lock.unlock(); - while (mRunning) - { - sleep_for(milliseconds(100)); - lock.lock(); - cout << "tm " << s << ": " << mOff + s << endl; - lock.unlock(); - } - lock.lock(); - cout << "tm " << s << " done." << endl; -} - -// main content -int main() -{ - Test tt(10); - tt.start(); - sleep_for(seconds(1)); - tt.stop(); - cout << "test done." << endl; - return 0; -} \ No newline at end of file diff --git a/sample/sample_Srv_MicroSoft_VS.cpp b/sample/sample_Srv_MicroSoft_VS.cpp deleted file mode 100644 index e2f4ef5..0000000 --- a/sample/sample_Srv_MicroSoft_VS.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// ============================================================ -// Microsoft sample TCP echo server -// ============================================================ -// see: https://learn.microsoft.com/en-us/windows/win32/winsock/complete-server-code - -#undef UNICODE - -#define WIN32_LEAN_AND_MEAN - -#include -#include -#include -#include -#include - -// Need to link with Ws2_32.lib -#pragma comment (lib, "Ws2_32.lib") -// #pragma comment (lib, "Mswsock.lib") - -#define DEFAULT_BUFLEN 512 -#define DEFAULT_PORT "8080" - -int __cdecl main(void) -{ - WSADATA wsaData; - int iResult; - - SOCKET ListenSocket = INVALID_SOCKET; - SOCKET ClientSocket = INVALID_SOCKET; - - struct addrinfo *result = NULL; - struct addrinfo hints; - - int iSendResult; - char recvbuf[DEFAULT_BUFLEN]; - int recvbuflen = DEFAULT_BUFLEN; - - // Initialize Winsock - iResult = WSAStartup(MAKEWORD(2,2), &wsaData); - if (iResult != 0) { - printf("WSAStartup failed with error: %d\n", iResult); - return 1; - } - - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - // Resolve the server address and port - iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); - if ( iResult != 0 ) { - printf("getaddrinfo failed with error: %d\n", iResult); - WSACleanup(); - return 1; - } - - // Create a SOCKET for the server to listen for client connections. - ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); - if (ListenSocket == INVALID_SOCKET) { - printf("socket failed with error: %ld\n", WSAGetLastError()); - freeaddrinfo(result); - WSACleanup(); - return 1; - } - - // Setup the TCP listening socket - iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); - if (iResult == SOCKET_ERROR) { - printf("bind failed with error: %d\n", WSAGetLastError()); - freeaddrinfo(result); - closesocket(ListenSocket); - WSACleanup(); - return 1; - } - - freeaddrinfo(result); - - iResult = listen(ListenSocket, SOMAXCONN); - if (iResult == SOCKET_ERROR) { - printf("listen failed with error: %d\n", WSAGetLastError()); - closesocket(ListenSocket); - WSACleanup(); - return 1; - } - - // Accept a client socket - ClientSocket = accept(ListenSocket, NULL, NULL); - if (ClientSocket == INVALID_SOCKET) { - printf("accept failed with error: %d\n", WSAGetLastError()); - closesocket(ListenSocket); - WSACleanup(); - return 1; - } - - // No longer need server socket - closesocket(ListenSocket); - - // Receive until the peer shuts down the connection - do { - - iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); - if (iResult > 0) { - printf("Bytes received: %d\n", iResult); - - // Echo the buffer back to the sender - iSendResult = send( ClientSocket, recvbuf, iResult, 0 ); - if (iSendResult == SOCKET_ERROR) { - printf("send failed with error: %d\n", WSAGetLastError()); - closesocket(ClientSocket); - WSACleanup(); - return 1; - } - printf("Bytes sent: %d\n", iSendResult); - } - else if (iResult == 0) - printf("Connection closing...\n"); - else { - printf("recv failed with error: %d\n", WSAGetLastError()); - closesocket(ClientSocket); - WSACleanup(); - return 1; - } - - } while (iResult > 0); - - // shutdown the connection since we're done - iResult = shutdown(ClientSocket, SD_SEND); - if (iResult == SOCKET_ERROR) { - printf("shutdown failed with error: %d\n", WSAGetLastError()); - closesocket(ClientSocket); - WSACleanup(); - return 1; - } - - // cleanup - closesocket(ClientSocket); - WSACleanup(); - - return 0; -} \ No newline at end of file diff --git a/sample/sample_Srv_Tenouk.cpp b/sample/sample_Srv_Tenouk.cpp deleted file mode 100644 index 269aa57..0000000 --- a/sample/sample_Srv_Tenouk.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// ============================================================ -// sample TCP echo server -// figured out from Tenouk's Winsock2 tutorial -// ============================================================ -// see: https://www.tenouk.com/Winsock/Winsock2example2.html - -// header content - -#ifdef _WIN32 -#include -#pragma comment(lib, "ws2_32") -#else -using SOCKET = int; -constexpr SOCKET INVALID_SOCKET = -1; -#endif - -#include - -class SampleSrv -{ -public: - inline SampleSrv() = default; - ~SampleSrv(); - bool init(UINT16 port = 8080); - bool init(const INT32 argc, const CONST_C_STRING* const argv); - bool run(); -private: - SOCKET mListenSocket = INVALID_SOCKET; - bool cleanup(); -}; - -// source content - -#ifdef _WIN32 -using tval = const timeval; -#else -#include -#include -#include -#define closesocket close -using tval = timeval; -#endif - -#include -using std::cout; -using std::endl; -#include // atoi -#include - -bool SampleSrv::init(const INT32 argc, const CONST_C_STRING* const argv) -{ - if (argc > 1) - { - return init(static_cast(atoi(argv[1]))); - } - else - { - return init(); - } -} - -bool SampleSrv::init(const UINT16 port) -{ - #ifdef _WIN32 - // check for Windows Sockets version 2.2 - { - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) - { - return false; - } - } - #endif - mListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (mListenSocket == INVALID_SOCKET) - { - cout << "socket failed" << endl; - return cleanup(); - } - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); - - if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) - { - cout << "bind failed, port: " << port << endl; - return cleanup(); - } - - return true; -} - -bool SampleSrv::cleanup() -{ - if (mListenSocket != INVALID_SOCKET) - { - closesocket(mListenSocket); - } -#ifdef _WIN32 - WSACleanup(); -#endif - return false; -} - -bool SampleSrv::run() -{ - cout << "run ..." << endl; - bool ok = true; - if (listen(mListenSocket, SOMAXCONN) < 0) - { - cout << "listen failed" << endl; - ok = false; - } - while (ok) - { - fd_set lset; - FD_ZERO(&lset); - FD_SET(mListenSocket, &lset); - { - tval tv {0, 100000}; - if (ok and (select(0, &lset, nullptr, nullptr, &tv) < 0)) - { - cout << "listen select failed" << endl; - ok = false; - } - } - if (ok and FD_ISSET(mListenSocket, &lset)) - { - SOCKET clientSocket = accept(mListenSocket, nullptr, nullptr); - if (clientSocket == INVALID_SOCKET) - { - cout << "accept failed" << endl; - ok = false; - } - else - { - cout << "client connected" << endl; - while(clientSocket != INVALID_SOCKET) - { - fd_set cset; - FD_ZERO(&cset); - FD_SET(clientSocket, &cset); - tval tv {0, 100000}; - if (select(0, &cset, nullptr, nullptr, &tv) < 0) - { - cout << "client select failed" << endl; - ok = false; - } - else if (FD_ISSET(clientSocket, &cset)) - { - char buffer[1024]; - int bytes = recv(clientSocket, buffer, sizeof(buffer), 0); - if (bytes > 0) - { - cout << "client sent " << bytes << " bytes" << endl; - send(clientSocket, buffer, bytes, 0); - } - else - { - cout << "client disconnected" << endl; - closesocket(clientSocket); - clientSocket = INVALID_SOCKET; - } - } - } - } - } - } - return ok or cleanup(); -} - -SampleSrv::~SampleSrv() -{ - if (mListenSocket != INVALID_SOCKET) - { - closesocket(mListenSocket); - } -#ifdef _WIN32 - WSACleanup(); -#endif -} - -// runtime - -int main(const INT32 argc, const CONST_C_STRING* const argv) -{ - SampleSrv srv; - if (not srv.init(argc, argv)) - { - cout << "init failed" << endl; - return 1; - } - if (not srv.run()) - { - cout << "run failed" << endl; - return 1; - } - cout << "test done." << endl; - return 0; -} \ No newline at end of file From 1504242f70d3ed00730aa5ac4863f6a58669d5cb Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:46:19 +0200 Subject: [PATCH 17/56] reduction to required --- {include/TCP_Srv => code}/BaseTypes.h | 0 {src => code}/TCP_Srv_Base.cpp | 2 +- {include/TCP_Srv => code}/TCP_Srv_Base.h | 2 +- {sample => code}/TCP_Srv_Echo.cpp | 0 {sample => code}/TCP_Srv_Echo.h | 2 +- {sample => code}/TCP_Srv_Echo_main.cpp | 0 devel/qnd/useCout.h | 27 ------------------------ include/TCP_Srv/TraceMacros.h | 14 ------------ make/TCP_Srv_Echo_silent.make | 8 +++---- make/TCP_Srv_Echo_verbose.make | 8 +++---- make/buildAll.sh | 7 ++---- make/howto_build_VS.md | 18 ++++++---------- make/premake5.lua | 9 ++------ py/echoParallelClient.py | 2 +- 14 files changed, 23 insertions(+), 76 deletions(-) rename {include/TCP_Srv => code}/BaseTypes.h (100%) rename {src => code}/TCP_Srv_Base.cpp (99%) rename {include/TCP_Srv => code}/TCP_Srv_Base.h (98%) rename {sample => code}/TCP_Srv_Echo.cpp (100%) rename {sample => code}/TCP_Srv_Echo.h (91%) rename {sample => code}/TCP_Srv_Echo_main.cpp (100%) delete mode 100644 devel/qnd/useCout.h delete mode 100644 include/TCP_Srv/TraceMacros.h diff --git a/include/TCP_Srv/BaseTypes.h b/code/BaseTypes.h similarity index 100% rename from include/TCP_Srv/BaseTypes.h rename to code/BaseTypes.h diff --git a/src/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp similarity index 99% rename from src/TCP_Srv_Base.cpp rename to code/TCP_Srv_Base.cpp index 65c0803..004bd2d 100644 --- a/src/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -1,4 +1,4 @@ -#include +#include "TCP_Srv_Base.h" #ifdef _WIN32 // timevel can be used const in VS diff --git a/include/TCP_Srv/TCP_Srv_Base.h b/code/TCP_Srv_Base.h similarity index 98% rename from include/TCP_Srv/TCP_Srv_Base.h rename to code/TCP_Srv_Base.h index c1599fb..818a31f 100644 --- a/include/TCP_Srv/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -7,7 +7,7 @@ #ifndef TCP_SRV_BASE_H #define TCP_SRV_BASE_H -#include +#include "BaseTypes.h" #ifdef _WIN32 #include diff --git a/sample/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp similarity index 100% rename from sample/TCP_Srv_Echo.cpp rename to code/TCP_Srv_Echo.cpp diff --git a/sample/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h similarity index 91% rename from sample/TCP_Srv_Echo.h rename to code/TCP_Srv_Echo.h index 04402df..f9a032d 100644 --- a/sample/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -3,7 +3,7 @@ #ifndef TCP_SRV_ECHO_H #define TCP_SRV_ECHO_H -#include +#include "TCP_Srv_Base.h" #include class TCP_Srv_Echo : public TCP_Srv_Base diff --git a/sample/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp similarity index 100% rename from sample/TCP_Srv_Echo_main.cpp rename to code/TCP_Srv_Echo_main.cpp diff --git a/devel/qnd/useCout.h b/devel/qnd/useCout.h deleted file mode 100644 index be2f5be..0000000 --- a/devel/qnd/useCout.h +++ /dev/null @@ -1,27 +0,0 @@ -// ============================================================ -// use cout for quick and dirty temporary traces -// note -// CI application build will fail -// with this file (still) included -// and merge request will be blocked -// ============================================================ -// created by Manfred Sorgo - -#pragma once -#ifndef USECOUT_H -#define USECOUT_H - -#include -#include -using std::ostream; -using std::cout; -using std::endl; -using std::setw; -using std::left; -using std::setfill; - -#include - -#define TRACEVAR(VAR) cout << #VAR << ": " << VAR << endl; - -#endif // H_ \ No newline at end of file diff --git a/include/TCP_Srv/TraceMacros.h b/include/TCP_Srv/TraceMacros.h deleted file mode 100644 index 19495a3..0000000 --- a/include/TCP_Srv/TraceMacros.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#ifndef TRACE_MACROS_H -#define TRACE_MACROS_H - -#ifdef TRACE_ON -#include -#define TRACE(MSG) std::cout << MSG << std::endl; -#define TRACEVAR(VAR) std::cout << #VAR << ": " << VAR << std::endl; -#else -#define TRACE(MSG) -#define TRACEVAR(VAR) -#endif - -#endif // _H diff --git a/make/TCP_Srv_Echo_silent.make b/make/TCP_Srv_Echo_silent.make index 7f20b2e..b9baecb 100644 --- a/make/TCP_Srv_Echo_silent.make +++ b/make/TCP_Srv_Echo_silent.make @@ -23,7 +23,7 @@ TARGETDIR = bin TARGET = $(TARGETDIR)/TCP_Srv_Echo_silent OBJDIR = obj/TCP_Srv_Echo_silent DEFINES += -INCLUDES += -I../include +INCLUDES += FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall @@ -119,13 +119,13 @@ endif # File Rules # ############################################# -$(OBJDIR)/TCP_Srv_Echo.o: ../sample/TCP_Srv_Echo.cpp +$(OBJDIR)/TCP_Srv_Base.o: ../code/TCP_Srv_Base.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/TCP_Srv_Echo_main.o: ../sample/TCP_Srv_Echo_main.cpp +$(OBJDIR)/TCP_Srv_Echo.o: ../code/TCP_Srv_Echo.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/TCP_Srv_Base.o: ../src/TCP_Srv_Base.cpp +$(OBJDIR)/TCP_Srv_Echo_main.o: ../code/TCP_Srv_Echo_main.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" diff --git a/make/TCP_Srv_Echo_verbose.make b/make/TCP_Srv_Echo_verbose.make index ba1bcbd..3b1db67 100644 --- a/make/TCP_Srv_Echo_verbose.make +++ b/make/TCP_Srv_Echo_verbose.make @@ -23,7 +23,7 @@ TARGETDIR = bin TARGET = $(TARGETDIR)/TCP_Srv_Echo_verbose OBJDIR = obj/TCP_Srv_Echo_verbose DEFINES += -INCLUDES += -I../include +INCLUDES += FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -DTRACE_ON @@ -119,13 +119,13 @@ endif # File Rules # ############################################# -$(OBJDIR)/TCP_Srv_Echo.o: ../sample/TCP_Srv_Echo.cpp +$(OBJDIR)/TCP_Srv_Base.o: ../code/TCP_Srv_Base.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/TCP_Srv_Echo_main.o: ../sample/TCP_Srv_Echo_main.cpp +$(OBJDIR)/TCP_Srv_Echo.o: ../code/TCP_Srv_Echo.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/TCP_Srv_Base.o: ../src/TCP_Srv_Base.cpp +$(OBJDIR)/TCP_Srv_Echo_main.o: ../code/TCP_Srv_Echo_main.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" diff --git a/make/buildAll.sh b/make/buildAll.sh index 7f4a22c..d28eb5a 100644 --- a/make/buildAll.sh +++ b/make/buildAll.sh @@ -5,7 +5,7 @@ help() echo "====================================" echo "build all major makes" echo "options:" - echo "-r remove all artifacts before" + echo "-r remove all untracked artifacts" echo "-p premake5 makefiles" echo "-h this help" echo "====================================" @@ -28,11 +28,10 @@ mk() rem= exe= pre= -while getopts hrxp option; do +while getopts hrp option; do case $option in (h) help;; (r) rem=1;; - (x) exe=1;; (p) pre=1 esac done @@ -44,10 +43,8 @@ if test ! -z $rem; then git clean -dxf . 2>/dev/null >/dev/null; pre=1; fi if test ! -z $pre; then premake5 gmake2 > /dev/null; fi st=$(date +%s) -rm -f *_VS.make for fn in *.make; do mk $fn; done wait echo binaries: for b in bin/*; do echo "- $b"; done -tm "DONE" $st diff --git a/make/howto_build_VS.md b/make/howto_build_VS.md index a196cff..1f6532a 100644 --- a/make/howto_build_VS.md +++ b/make/howto_build_VS.md @@ -1,6 +1,6 @@ # Visual Studio build ## preconditions -- premake5 available +- premake5 - Visual Studio ## 1) generate VS solution using premake5 @@ -24,18 +24,14 @@ ACTIONS ### 1.2) generate solution with your VS version e.g. VS 2019 ````shell -$> premake5 vs2019 +TCP_Srv/make$> premake5 vs2019 Building configurations... Running action 'vs2019'... -Generated TCP_Srv_Base.sln... -Generated TCP_Srv_Base.vcxproj... -Generated sample_Method_Threads.sln... -Generated sample_Method_Threads.vcxproj... -Generated sample_Srv_MicroSoft_VS.sln... -Generated sample_Srv_MicroSoft_VS.vcxproj... -Generated sample_Srv_Tenouk.sln... -Generated sample_Srv_Tenouk.vcxproj... -Done (49ms). +Generated TCP_Srv_Echo_silent.sln... +Generated TCP_Srv_Echo_silent.vcxproj... +Generated TCP_Srv_Echo_verbose.sln... +Generated TCP_Srv_Echo_verbose.vcxproj... +Done (45ms). ```` ## 2) build executables in VS - open solution (*.sln) diff --git a/make/premake5.lua b/make/premake5.lua index 445aea4..d85c9d1 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -1,13 +1,10 @@ -- ============================================================ -- premake5 build rules -- ============================================================ -includesBase = { '../include' } buildOptsGcc = '-std=c++17 -pedantic-errors -Werror -Wall ' buildOptsVS = '/std:c++17 /W4 ' -dummy = { '../sample/dummy.cpp' } - -- ============================================================ -- sample TCP_Srv_Echo based on TCP_Srv_Base -- with tracing off @@ -18,9 +15,8 @@ workspace 'TCP_Srv_Echo_silent' objdir 'obj/%{prj.name}' project 'TCP_Srv_Echo_silent' - includedirs { includesBase } kind 'ConsoleApp' - files { '../src/*.cpp', '../sample/TCP_Srv_Echo*.cpp' } + files { '../code/*.cpp' } filter { 'action:vs*' } warnings 'high' @@ -42,9 +38,8 @@ workspace 'TCP_Srv_Echo_verbose' objdir 'obj/%{prj.name}' project 'TCP_Srv_Echo_verbose' - includedirs { includesBase } kind 'ConsoleApp' - files { '../src/*.cpp', '../sample/TCP_Srv_Echo*.cpp' } + files { '../code/*.cpp' } filter { 'action:vs*' } warnings 'high' diff --git a/py/echoParallelClient.py b/py/echoParallelClient.py index 86b5dd7..cdc80c3 100644 --- a/py/echoParallelClient.py +++ b/py/echoParallelClient.py @@ -23,7 +23,7 @@ def tfunc(nr:int, prot:int, addr:str, port:int, message:str, loops:int): s.connect((addr, port)) log("%02d connected." % nr) for n in range(loops): - if n > 0: sleep(1) + if n > 0: sleep(0.25) log("%02d %d / %d send: '%s'" % (nr, n + 1, loops, message)) s.sendall(message.encode()) data = s.recv(1024) From 6b0b60a56f5bd25cc5a5ac8781b34da3c174c587 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:52:21 +0200 Subject: [PATCH 18/56] removed simple python echo client --- py/echoClient.py | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 py/echoClient.py diff --git a/py/echoClient.py b/py/echoClient.py deleted file mode 100644 index af5a1b5..0000000 --- a/py/echoClient.py +++ /dev/null @@ -1,28 +0,0 @@ -# ============================================================ -# simple TCP echo client -# ============================================================ -# created by Manfred Sorgo - -from echoCommon import getSocketData -from sys import argv -from time import sleep -from select import select - -def getEcho(*args): - [s, addr, port, message, num, *x] = getSocketData(*args) - enc = message.encode() - try: - s.connect((addr, port)) - for n in range(num): - if n > 0: sleep(1) - print(f'send: "{message}"') - s.sendall(enc) - data = s.recv(1024) - print(f'recv: "{data.decode()}"') - s.close() - except Exception as e: - print(e) - -if __name__ == '__main__': - getEcho(*argv[1:]) - From 8160013483223ce673a2a3870aaaa52d5f79b931 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:54:12 +0200 Subject: [PATCH 19/56] established python prallel echo client --- py/{echoParallelClient.py => echoClient.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename py/{echoParallelClient.py => echoClient.py} (100%) diff --git a/py/echoParallelClient.py b/py/echoClient.py similarity index 100% rename from py/echoParallelClient.py rename to py/echoClient.py From 624a1bf06e669bd923d6ddd6616b6c0951d9a6b3 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 01:02:40 +0200 Subject: [PATCH 20/56] added comments --- code/TCP_Srv_Echo.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index f9a032d..d2d3d2f 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -1,22 +1,22 @@ +// ============================================================ +// sample TCP echo server class +// ============================================================ +// created by Manfred Sorgo #pragma once #ifndef TCP_SRV_ECHO_H #define TCP_SRV_ECHO_H #include "TCP_Srv_Base.h" -#include class TCP_Srv_Echo : public TCP_Srv_Base { public: inline TCP_Srv_Echo() = default; -protected: +protected: + // simple implementation of process: echo received data + // trace received data if TRACE_ON is defined void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) final; -private: - // prevent from parallel output - std::mutex mMtx; - - }; #endif // _H From a30d3e372215c60d3800db953981d8681f6bef4e Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:26:31 +0200 Subject: [PATCH 21/56] ready for CI --- .github/workflows/c-cpp.yml | 26 +++++++++++++++++++++++++ code/TCP_Srv_Base.cpp | 38 ++++++++++++++++++++++++++++--------- make/buildAll.sh | 10 ++-------- py/echoClient.py | 2 +- 4 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..e2dcb87 --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,26 @@ +# ============================================================ +# continuous integration setup +# ============================================================ +# created by Manfred Sorgo +name: C/C++ CI + +on: + push: + branches: [ "dev" ] + pull_request: + branches: [ "dev" ] + +jobs: + buildVersions: + + runs-on: ubuntu-latest + + steps: + - name: "Checkout repository" + uses: actions/checkout@v3 + with: + submodules: 'true' + - name: "build silent version" + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo_silent.make + - name: "build verbose version" + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo_verbose.make diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 004bd2d..8c0694e 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -85,8 +85,8 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) } } -// trace error and exit loop -#define TRACE_ERR_X(MSG) { cerr << endl << MSG << endl; cont = false; } +// trace error and exit the processing +#define TRACE_ERR_X(MSG) cerr << endl << MSG << endl; cont = false; void TCP_Srv_Base::run(const UINT16 port) { @@ -99,14 +99,19 @@ void TCP_Srv_Base::run(const UINT16 port) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) - TRACE_ERR_X("WSAStartup failed") + { + TRACE_ERR_X("WSAStartup failed") + } } #endif // create socket if (cont) { listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listenSocket < 0) TRACE_ERR_X("socket failed") + if (listenSocket < 0) + { + TRACE_ERR_X("socket failed") + } } // bind socket to port if (cont) @@ -117,14 +122,24 @@ void TCP_Srv_Base::run(const UINT16 port) addr.sin_port = htons(port); if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) - TRACE_ERR_X("bind failed: port " << port) + { + TRACE_ERR_X("bind failed: port " << port) + } } // listen to socket if (cont and (listen(listenSocket, SOMAXCONN) < 0)) - TRACE_ERR_X("listen failed") + { + TRACE_ERR_X("listen failed") + } // select and accept loop - TRACE("running on port " << port << endl << "press Ctrl+C to stop") + if (cont) + { + cout + << "running on port " << port << endl + << "press Ctrl+C to stop" << endl; + } + while (cont) { // select @@ -133,13 +148,18 @@ void TCP_Srv_Base::run(const UINT16 port) FD_SET(listenSocket, &lset); tval tv { tmSec, tmMic }; if (select(0, &lset, nullptr, nullptr, &tv) < 0) - TRACE_ERR_X("listen select failed") + { + TRACE_ERR_X("listen select failed") + } // accept to new client socket if listen socket is set else if (FD_ISSET(listenSocket, &lset)) { SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); - if (clientSocket < 0) TRACE_ERR_X("accept failed") + if (clientSocket < 0) + { + TRACE_ERR_X("accept failed") + } // start thread with client socket else { diff --git a/make/buildAll.sh b/make/buildAll.sh index d28eb5a..d5a31db 100644 --- a/make/buildAll.sh +++ b/make/buildAll.sh @@ -6,7 +6,6 @@ help() echo "build all major makes" echo "options:" echo "-r remove all untracked artifacts" - echo "-p premake5 makefiles" echo "-h this help" echo "====================================" exit @@ -26,21 +25,16 @@ mk() } rem= -exe= -pre= -while getopts hrp option; do +while getopts hr option; do case $option in (h) help;; (r) rem=1;; - (p) pre=1 esac done cd $(dirname $0) -if test ! -z $rem; then git clean -dxf . 2>/dev/null >/dev/null; pre=1; fi - -if test ! -z $pre; then premake5 gmake2 > /dev/null; fi +if test ! -z $rem; then git clean -dxf . 2>/dev/null >/dev/null; fi st=$(date +%s) diff --git a/py/echoClient.py b/py/echoClient.py index cdc80c3..0840ffa 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -30,7 +30,7 @@ def tfunc(nr:int, prot:int, addr:str, port:int, message:str, loops:int): log("%02d %d / %d recv: '%s'" % (nr, n + 1, loops, data.decode())) s.close() except Exception as e: - print(e) + log("%02d %s" % (nr, e)) if __name__ == '__main__': getEchoes(*argv[1:]) From 5662b60587b137d136111114d6e36830c38fec0e Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:44:45 +0200 Subject: [PATCH 22/56] substantial rework after review --- .github/workflows/c-cpp.yml | 4 +- code/TCP_Srv_Base.cpp | 18 +-- code/TCP_Srv_Echo.cpp | 2 +- ...Srv_Echo_silent.make => TCP_Srv_Echo.make} | 29 ++-- make/TCP_Srv_Echo_verbose.make | 135 ------------------ make/build.sh | 33 +++++ make/buildAll.sh | 44 ------ make/premake5.lua | 55 +++---- py/echoClient.py | 49 ++++--- py/echoCommon.py | 107 ++++++++++---- py/echoServer.py | 49 +++---- 11 files changed, 204 insertions(+), 321 deletions(-) rename make/{TCP_Srv_Echo_silent.make => TCP_Srv_Echo.make} (81%) delete mode 100644 make/TCP_Srv_Echo_verbose.make create mode 100644 make/build.sh delete mode 100644 make/buildAll.sh diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index e2dcb87..049f4d3 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -21,6 +21,6 @@ jobs: with: submodules: 'true' - name: "build silent version" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo_silent.make + run: make/build.sh -s - name: "build verbose version" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo_verbose.make + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 8c0694e..7faadcf 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -25,8 +25,8 @@ using std::endl; #include using std::setw; -// tracing -#ifdef TRACE_ON +// verbosity +#ifdef VERBOSE // general trace #define TRACE(MSG) cout << MSG << endl; // mutex locked trace with thread number @@ -86,7 +86,7 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) } // trace error and exit the processing -#define TRACE_ERR_X(MSG) cerr << endl << MSG << endl; cont = false; +#define ERR_X(MSG) cerr << endl << MSG << endl; cont = false; void TCP_Srv_Base::run(const UINT16 port) { @@ -100,7 +100,7 @@ void TCP_Srv_Base::run(const UINT16 port) WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - TRACE_ERR_X("WSAStartup failed") + ERR_X("WSAStartup failed") } } #endif @@ -110,7 +110,7 @@ void TCP_Srv_Base::run(const UINT16 port) listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket < 0) { - TRACE_ERR_X("socket failed") + ERR_X("socket failed") } } // bind socket to port @@ -123,13 +123,13 @@ void TCP_Srv_Base::run(const UINT16 port) if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { - TRACE_ERR_X("bind failed: port " << port) + ERR_X("bind failed: port " << port) } } // listen to socket if (cont and (listen(listenSocket, SOMAXCONN) < 0)) { - TRACE_ERR_X("listen failed") + ERR_X("listen failed") } // select and accept loop @@ -149,7 +149,7 @@ void TCP_Srv_Base::run(const UINT16 port) tval tv { tmSec, tmMic }; if (select(0, &lset, nullptr, nullptr, &tv) < 0) { - TRACE_ERR_X("listen select failed") + ERR_X("listen select failed") } // accept to new client socket if listen socket is set @@ -158,7 +158,7 @@ void TCP_Srv_Base::run(const UINT16 port) SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); if (clientSocket < 0) { - TRACE_ERR_X("accept failed") + ERR_X("accept failed") } // start thread with client socket else diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index 4616158..c59c974 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -8,7 +8,7 @@ using std::endl; void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t size, const UINT32 nr) { send(clientSocket, buff, size, 0); -#ifdef TRACE_ON +#ifdef VERBOSE mutexlock lock(mMtxOut); cout << std::setw(3) << nr << " <- "; for (size_t n = 0; n < size; ++n) diff --git a/make/TCP_Srv_Echo_silent.make b/make/TCP_Srv_Echo.make similarity index 81% rename from make/TCP_Srv_Echo_silent.make rename to make/TCP_Srv_Echo.make index b9baecb..72fa0e1 100644 --- a/make/TCP_Srv_Echo_silent.make +++ b/make/TCP_Srv_Echo.make @@ -1,7 +1,7 @@ # Alternative GNU Make project makefile autogenerated by Premake ifndef config - config=ci + config=silent endif ifndef verbose @@ -20,18 +20,13 @@ endif RESCOMP = windres TARGETDIR = bin -TARGET = $(TARGETDIR)/TCP_Srv_Echo_silent -OBJDIR = obj/TCP_Srv_Echo_silent -DEFINES += +TARGET = $(TARGETDIR)/TCP_Srv_Echo INCLUDES += FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) -ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) LIBS += LDDEPS += -ALL_LDFLAGS += $(LDFLAGS) -s -pthread LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) define PREBUILDCMDS endef @@ -40,6 +35,22 @@ endef define POSTBUILDCMDS endef +ifeq ($(config),silent) +OBJDIR = obj/gcc/silent +DEFINES += -DNDEBUG +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall +ALL_LDFLAGS += $(LDFLAGS) -s -pthread + +else ifeq ($(config),verbose) +OBJDIR = obj/gcc/verbose +DEFINES += -DDEBUG -DVERBOSE +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall +ALL_LDFLAGS += $(LDFLAGS) -pthread + +endif + # Per File Configurations # ############################################# @@ -65,7 +76,7 @@ all: $(TARGET) $(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) $(PRELINKCMDS) - @echo Linking TCP_Srv_Echo_silent + @echo Linking TCP_Srv_Echo $(SILENT) $(LINKCMD) $(POSTBUILDCMDS) @@ -86,7 +97,7 @@ else endif clean: - @echo Cleaning TCP_Srv_Echo_silent + @echo Cleaning TCP_Srv_Echo ifeq (posix,$(SHELLTYPE)) $(SILENT) rm -f $(TARGET) $(SILENT) rm -rf $(GENERATED) diff --git a/make/TCP_Srv_Echo_verbose.make b/make/TCP_Srv_Echo_verbose.make deleted file mode 100644 index 3b1db67..0000000 --- a/make/TCP_Srv_Echo_verbose.make +++ /dev/null @@ -1,135 +0,0 @@ -# Alternative GNU Make project makefile autogenerated by Premake - -ifndef config - config=ci -endif - -ifndef verbose - SILENT = @ -endif - -.PHONY: clean prebuild - -SHELLTYPE := posix -ifeq ($(shell echo "test"), "test") - SHELLTYPE := msdos -endif - -# Configurations -# ############################################# - -RESCOMP = windres -TARGETDIR = bin -TARGET = $(TARGETDIR)/TCP_Srv_Echo_verbose -OBJDIR = obj/TCP_Srv_Echo_verbose -DEFINES += -INCLUDES += -FORCE_INCLUDE += -ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) -ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -DTRACE_ON -ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -DTRACE_ON -ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) -LIBS += -LDDEPS += -ALL_LDFLAGS += $(LDFLAGS) -s -pthread -LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) -define PREBUILDCMDS -endef -define PRELINKCMDS -endef -define POSTBUILDCMDS -endef - -# Per File Configurations -# ############################################# - - -# File sets -# ############################################# - -GENERATED := -OBJECTS := - -GENERATED += $(OBJDIR)/TCP_Srv_Base.o -GENERATED += $(OBJDIR)/TCP_Srv_Echo.o -GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o -OBJECTS += $(OBJDIR)/TCP_Srv_Base.o -OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o -OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o - -# Rules -# ############################################# - -all: $(TARGET) - @: - -$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) - $(PRELINKCMDS) - @echo Linking TCP_Srv_Echo_verbose - $(SILENT) $(LINKCMD) - $(POSTBUILDCMDS) - -$(TARGETDIR): - @echo Creating $(TARGETDIR) -ifeq (posix,$(SHELLTYPE)) - $(SILENT) mkdir -p $(TARGETDIR) -else - $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) -endif - -$(OBJDIR): - @echo Creating $(OBJDIR) -ifeq (posix,$(SHELLTYPE)) - $(SILENT) mkdir -p $(OBJDIR) -else - $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) -endif - -clean: - @echo Cleaning TCP_Srv_Echo_verbose -ifeq (posix,$(SHELLTYPE)) - $(SILENT) rm -f $(TARGET) - $(SILENT) rm -rf $(GENERATED) - $(SILENT) rm -rf $(OBJDIR) -else - $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) - $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) - $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) -endif - -prebuild: | $(OBJDIR) - $(PREBUILDCMDS) - -ifneq (,$(PCH)) -$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) -$(GCH): $(PCH) | prebuild - @echo $(notdir $<) - $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" -$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) -ifeq (posix,$(SHELLTYPE)) - $(SILENT) touch "$@" -else - $(SILENT) echo $null >> "$@" -endif -else -$(OBJECTS): | prebuild -endif - - -# File Rules -# ############################################# - -$(OBJDIR)/TCP_Srv_Base.o: ../code/TCP_Srv_Base.cpp - @echo "$(notdir $<)" - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/TCP_Srv_Echo.o: ../code/TCP_Srv_Echo.cpp - @echo "$(notdir $<)" - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/TCP_Srv_Echo_main.o: ../code/TCP_Srv_Echo_main.cpp - @echo "$(notdir $<)" - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" - --include $(OBJECTS:%.o=%.d) -ifneq (,$(PCH)) - -include $(PCH_PLACEHOLDER).d -endif \ No newline at end of file diff --git a/make/build.sh b/make/build.sh new file mode 100644 index 0000000..a493fed --- /dev/null +++ b/make/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +help() +{ + echo "build TCP_Srv_Echo sample" + echo "options:" + echo "-s config; silent optimized for spped (default)" + echo "-v config: verbose with debug information" + echo "-k keep untracked artifacts" + echo "-h this help" + exit +} + +keep= +conf=silent +while getopts hkvs option; do + case $option in + (h) help;; + (k) keep=1;; + (v) conf=verbose;; + (s) conf=silent;; + esac +done + +cd $(dirname $0) + +git clean -dxf bin 2>/dev/null >/dev/null + +if test -z $keep; then git clean -dxf . 2>/dev/null >/dev/null; fi + +make -j -f TCP_Srv_Echo.make config=$conf + +for b in bin/*; do echo "-> $b"; done diff --git a/make/buildAll.sh b/make/buildAll.sh deleted file mode 100644 index d5a31db..0000000 --- a/make/buildAll.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -help() -{ - echo "====================================" - echo "build all major makes" - echo "options:" - echo "-r remove all untracked artifacts" - echo "-h this help" - echo "====================================" - exit -} - -tm() -{ - en=$(date +%s) - printf "%-25s[%3d]\n" $1 $(($en-$2)) -} - -mk() -{ - st=$(date +%s) - make -j -f $1 > /dev/null - tm ${1%.*} $st -} - -rem= -while getopts hr option; do - case $option in - (h) help;; - (r) rem=1;; - esac -done - -cd $(dirname $0) - -if test ! -z $rem; then git clean -dxf . 2>/dev/null >/dev/null; fi - -st=$(date +%s) - -for fn in *.make; do mk $fn; done -wait -echo binaries: -for b in bin/*; do echo "- $b"; done diff --git a/make/premake5.lua b/make/premake5.lua index d85c9d1..4472605 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -1,52 +1,37 @@ -- ============================================================ -- premake5 build rules +-- TCP_Srv_Echo based on TCP_Srv_Base +-- configurations: silent, verbose, deb -- ============================================================ +-- created by Manfred Sorgo -buildOptsGcc = '-std=c++17 -pedantic-errors -Werror -Wall ' -buildOptsVS = '/std:c++17 /W4 ' - --- ============================================================ --- sample TCP_Srv_Echo based on TCP_Srv_Base --- with tracing off --- ============================================================ -workspace 'TCP_Srv_Echo_silent' - configurations { 'ci' } +workspace 'TCP_Srv_Echo' + configurations { 'silent', 'verbose' } language 'C++' - objdir 'obj/%{prj.name}' + optimize 'On' - project 'TCP_Srv_Echo_silent' + project 'TCP_Srv_Echo' kind 'ConsoleApp' files { '../code/*.cpp' } - filter { 'action:vs*' } - warnings 'high' - buildoptions { buildOptsVS .. '/wd4100' } - targetdir 'exe' + filter { 'configurations:silent' } + defines { 'NDEBUG' } + symbols 'Off' + optimize 'Speed' - filter { 'action:gmake*' } - buildoptions { buildOptsGcc } - linkoptions { '-pthread' } - targetdir 'bin' - --- ============================================================ --- sample TCP_Srv_Echo based on TCP_Srv_Base --- with tracing on --- ============================================================ -workspace 'TCP_Srv_Echo_verbose' - configurations { 'ci' } - language 'C++' - objdir 'obj/%{prj.name}' - - project 'TCP_Srv_Echo_verbose' - kind 'ConsoleApp' - files { '../code/*.cpp' } + filter { 'configurations:verbose' } + defines { 'DEBUG', 'VERBOSE' } + symbols 'On' + optimize 'Off' filter { 'action:vs*' } + buildoptions { '/std:c++17 /W4 /wd4100' } + objdir 'obj/vs/' warnings 'high' - buildoptions { buildOptsVS .. '/DTRACE_ON' } targetdir 'exe' - + filter { 'action:gmake*' } - buildoptions { buildOptsGcc .. '-DTRACE_ON' } + buildoptions { '-std=c++17 -pedantic-errors -Werror -Wall' } + objdir 'obj/gcc/' linkoptions { '-pthread' } targetdir 'bin' diff --git a/py/echoClient.py b/py/echoClient.py index 0840ffa..158ef69 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -3,35 +3,40 @@ # ============================================================ # created by Manfred Sorgo -from echoCommon import getSocketData -from sys import argv +from echoCommon import EchoCommon from threading import Thread -from logging import info as log, basicConfig as logconfig, INFO from socket import socket, SOCK_STREAM from time import sleep -def getEchoes(*args): - [addr, port, message, loops, threads, prot] = getSocketData(*args)[1:] - logconfig(format="%(asctime)s %(message)s", level=INFO, datefmt="%H:%M:%S") - for n in range(threads): - Thread(target=tfunc, args=(n + 1, prot, addr, port, message, loops)).start() +class EchoClient(EchoCommon): + def __init__(self, *args): + super().__init__(*args) -def tfunc(nr:int, prot:int, addr:str, port:int, message:str, loops:int): - try: - log("%02d connecting ..." % nr) - s = socket(prot, SOCK_STREAM) - s.connect((addr, port)) - log("%02d connected." % nr) - for n in range(loops): + def usage(self, name): + print(f'Usage: {name} [options] [address] [port] [message] [loops] [threads]') + + def run(self): + for n in range(self.threads): + Thread(target=self.tfunc, args=(n + 1,)).start() + + def tfunc(self, nr:int): + try: + self.echo("%02d connecting ..." % nr) + s = self.getsocket() + s.connect((self.addr, self.port)) + self.echo("%02d connected." % nr) + for n in range(self.loops): if n > 0: sleep(0.25) - log("%02d %d / %d send: '%s'" % (nr, n + 1, loops, message)) - s.sendall(message.encode()) + self.echo("%02d %d / %d send: '%s'" % (nr, n + 1, self.loops, self.message)) + s.sendall(self.message.encode()) data = s.recv(1024) - log("%02d %d / %d recv: '%s'" % (nr, n + 1, loops, data.decode())) - s.close() - except Exception as e: - log("%02d %s" % (nr, e)) + self.echo("%02d %d / %d recv: '%s'" % (nr, n + 1, self.loops, data.decode())) + s.close() + except Exception as e: + self.log("%02d %s" % (nr, e)) if __name__ == '__main__': - getEchoes(*argv[1:]) + from sys import argv + cl = EchoClient(*argv[1:]) + cl.run() diff --git a/py/echoCommon.py b/py/echoCommon.py index 4faf0d7..c0b4b2a 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -1,39 +1,84 @@ # ============================================================ -# common functions for echo server and client +# common base class for echo server and client # ============================================================ # created by Manfred Sorgo import re import socket +from getopt import getopt +from sys import argv +from logging import info as logout, basicConfig as logconfig, INFO +from os.path import basename -def getSocketData(*args): - rx4 = re.compile(r'^\d+(?:\.\d+){3}$') - rx6 = re.compile(r'^[0-9a-fA-F]*(?:\:[0-9a-fA-F]*){5}$') - rxP = re.compile(r'^\d+$') - rxN = re.compile(r'^(\d+)[xX]+$') - rxT = re.compile(r'^(\d+)[tT]+$') - prot = socket.AF_INET - addr = '127.0.0.1' - port = 8080 - loops = 1 - threads = 1 - message = 'hello world' - for arg in args: - if rx4.match(arg): - addr = arg - elif rx6.match(arg): - addr = arg - prot = socket.AF_INET6 - elif rxP.match(arg): - port = int(arg) - elif rxN.match(arg): - loops = int(rxN.match(arg).group(1)) - elif rxT.match(arg): - threads = int(rxT.match(arg).group(1)) +class EchoCommon(object): + def __init__(self, *args): + self.verbose = False + opts, args = getopt(args, 'vh') + for o, v in opts: + if o == '-v': + self.verbose = True + elif o == '-h': + self.help() + + rx4 = re.compile(r'^\d+(?:\.\d+){3}$') + rx6 = re.compile(r'^[0-9a-fA-F]*(?:\:[0-9a-fA-F]*){5}$') + rxP = re.compile(r'^\d+$') + rxN = re.compile(r'^(\d+)[xX]+$') + rxT = re.compile(r'^(\d+)[tT]+$') + self.prot = socket.AF_INET + self.addr = '127.0.0.1' + self.port = 8080 + self.loops = 1 + self.threads = 1 + self.message = 'hello world' + for arg in args: + if rx4.match(arg): + self.self.addr = arg + elif rx6.match(arg): + self.addr = arg + self.prot = socket.AF_INET6 + elif rxP.match(arg): + self.port = int(arg) + elif rxN.match(arg): + self.loops = int(rxN.match(arg).group(1)) + elif rxT.match(arg): + self.threads = int(rxT.match(arg).group(1)) + else: + self.message = arg + + logconfig(format="%(asctime)s %(message)s", level=INFO, datefmt="%H:%M:%S") + + if self.verbose: + self.echo = self.log + print() + print('addr:', self.addr) + print('prot:', 'ipv6' if self.prot == socket.AF_INET6 else 'ipv4') + print('port:', self.port) else: - message = arg - print() - print('addr:', addr) - print('prot:', 'ipv6' if prot == socket.AF_INET6 else 'ipv4') - print('port:', port) + self.echo = self.silent + + def getsocket(self): + return socket.socket(self.prot, socket.SOCK_STREAM) + + def log(self, *args): + logout(' '.join([str(c) for c in args])) + + def silent(self, *args): + pass - return [socket.socket(prot, socket.SOCK_STREAM), addr, port, message, loops, threads, prot] \ No newline at end of file + def help(self): + self.usage(basename(argv[0])) + print( +"""options: + -v verbose + -h this help +syntax: | e.g. + address | + - ipv4 n.n.n.n | 1.2.3.4 + - ipv6 n:n:n:n:n:n | 1:2:3:4:5:6 + port nn.. | 8080 + loops n..x | 2x, 12X + threads n..t | 5T, 11t + message any other | "hello world" +""" + ) + exit() diff --git a/py/echoServer.py b/py/echoServer.py index 26fc25f..4140e71 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -4,63 +4,46 @@ # created by Manfred Sorgo # see: https://realpython.com/python-sockets/ -from echoCommon import getSocketData -from sys import argv +from echoCommon import EchoCommon from select import select -import signal -from os.path import basename -from getopt import getopt -class EchoServer(object): +class EchoServer(EchoCommon): def __init__(self, *args): - self.verbose = False - opts, args = getopt(args, 'vh') - for o, v in opts: - if o == '-v': - self.verbose = True - elif o == '-h': - self.help() - - for sig in ('TERM', 'INT'): - signal.signal(getattr(signal, 'SIG' + sig), lambda *any: self.done()) - - [self.sock, self.addr, self.port, *x] = getSocketData(*args) + super().__init__(*args) def run(self): try: - self.sock.bind((self.addr, self.port)) - self.sock.listen() + listenSocket = self.getsocket() + listenSocket.bind((self.addr, self.port)) + listenSocket.listen() print('Press Ctrl+C to stop') while True: - if self.verbose: print('.', end='', flush=True) for n in range(20): - rd, *x = select([self.sock], [], [], 0.05) + rd, *x = select([listenSocket], [], [], 0.05) if rd: - conn, *x = self.sock.accept() + conn, *x = listenSocket.accept() with conn: while True: data = conn.recv(1024) - if not data: + if data: + self.echo(f'< {data.decode()} >') + else: break conn.sendall(data) except Exception as e: - self.sock.close() - print(e) + listenSocket.close() + self.log(e) def done(*any): print() print('done') exit() - def help(self): - print(f'Usage: {basename(argv[0])} [options] [addr] [port]') - print("""options: - -v verbose - -h this help""") - exit() + def usage(self, name): + print(f'Usage: {name} [options] [address] [port]') if __name__ == '__main__': + from sys import argv srv = EchoServer(*argv[1:]) srv.run() - From 00b0b1c1718f93658031e9698755c0ee78267d56 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:49:59 +0200 Subject: [PATCH 23/56] CI --- .github/workflows/c-cpp.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 049f4d3..52aca10 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -5,22 +5,22 @@ name: C/C++ CI on: - push: - branches: [ "dev" ] - pull_request: - branches: [ "dev" ] + push: + branches: [ "dev" ] + pull_request: + branches: [ "dev" ] jobs: - buildVersions: + buildVersions: - runs-on: ubuntu-latest + runs-on: ubuntu-latest - steps: - - name: "Checkout repository" - uses: actions/checkout@v3 - with: - submodules: 'true' - - name: "build silent version" - run: make/build.sh -s - - name: "build verbose version" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose + steps: + - name: "Checkout repository" + uses: actions/checkout@v3 + with: + submodules: 'true' + - name: "build silent version" + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent + - name: "build verbose version" + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose From ca40c045de0b44bedc16f21256792a26a88521ae Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:52:20 +0200 Subject: [PATCH 24/56] configurations & wording --- .github/workflows/c-cpp.yml | 2 -- README.md | 4 +++- make/howto_build_VS.md | 21 ++++++++++++++------- make/premake5.lua | 10 +++++++++- py/echoClient.py | 9 ++++----- py/echoCommon.py | 4 ++-- py/echoServer.py | 8 +------- 7 files changed, 33 insertions(+), 25 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 52aca10..f848132 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -18,8 +18,6 @@ jobs: steps: - name: "Checkout repository" uses: actions/checkout@v3 - with: - submodules: 'true' - name: "build silent version" run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent - name: "build verbose version" diff --git a/README.md b/README.md index bdadf24..4f0ae7e 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# TCP_Srv \ No newline at end of file +# TCP_Srv + +... \ No newline at end of file diff --git a/make/howto_build_VS.md b/make/howto_build_VS.md index 1f6532a..5c65f01 100644 --- a/make/howto_build_VS.md +++ b/make/howto_build_VS.md @@ -27,15 +27,22 @@ e.g. VS 2019 TCP_Srv/make$> premake5 vs2019 Building configurations... Running action 'vs2019'... -Generated TCP_Srv_Echo_silent.sln... -Generated TCP_Srv_Echo_silent.vcxproj... -Generated TCP_Srv_Echo_verbose.sln... -Generated TCP_Srv_Echo_verbose.vcxproj... -Done (45ms). +Generated TCP_Srv_Echo.sln... +Generated TCP_Srv_Echo.vcxproj... +Done (41ms).. ```` ## 2) build executables in VS -- open solution (*.sln) -- build and run +- open solution (TCP_Srv/make/TCP_Srv_Echo.sln) +- select a configuration + - _silent_: + - no debug + - no output + - optimized for speed + - _verbose_: + - verbose output + - debug infos + - no optimization +- build (and run) ## 3) VS keyboard shortcuts - build: [CTRL] [shift] [B] diff --git a/make/premake5.lua b/make/premake5.lua index 4472605..6c27f23 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -1,7 +1,15 @@ -- ============================================================ -- premake5 build rules -- TCP_Srv_Echo based on TCP_Srv_Base --- configurations: silent, verbose, deb +-- configurations: +-- - silent: +-- - no debug +-- - no output +-- - optimized for speed +-- - verbose: +-- - verbose output +-- - debug infos +-- - no optimization -- ============================================================ -- created by Manfred Sorgo diff --git a/py/echoClient.py b/py/echoClient.py index 158ef69..6a99fa6 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -5,7 +5,6 @@ from echoCommon import EchoCommon from threading import Thread -from socket import socket, SOCK_STREAM from time import sleep class EchoClient(EchoCommon): @@ -21,16 +20,16 @@ def run(self): def tfunc(self, nr:int): try: - self.echo("%02d connecting ..." % nr) + self.tell("%02d connecting ..." % nr) s = self.getsocket() s.connect((self.addr, self.port)) - self.echo("%02d connected." % nr) + self.tell("%02d connected." % nr) for n in range(self.loops): if n > 0: sleep(0.25) - self.echo("%02d %d / %d send: '%s'" % (nr, n + 1, self.loops, self.message)) + self.tell("%02d %d / %d send: '%s'" % (nr, n + 1, self.loops, self.message)) s.sendall(self.message.encode()) data = s.recv(1024) - self.echo("%02d %d / %d recv: '%s'" % (nr, n + 1, self.loops, data.decode())) + self.tell("%02d %d / %d recv: '%s'" % (nr, n + 1, self.loops, data.decode())) s.close() except Exception as e: self.log("%02d %s" % (nr, e)) diff --git a/py/echoCommon.py b/py/echoCommon.py index c0b4b2a..487e05a 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -48,13 +48,13 @@ def __init__(self, *args): logconfig(format="%(asctime)s %(message)s", level=INFO, datefmt="%H:%M:%S") if self.verbose: - self.echo = self.log + self.tell = self.log print() print('addr:', self.addr) print('prot:', 'ipv6' if self.prot == socket.AF_INET6 else 'ipv4') print('port:', self.port) else: - self.echo = self.silent + self.tell = self.silent def getsocket(self): return socket.socket(self.prot, socket.SOCK_STREAM) diff --git a/py/echoServer.py b/py/echoServer.py index 4140e71..4fd9ee2 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -2,7 +2,6 @@ # simple TCP echo server # ============================================================ # created by Manfred Sorgo -# see: https://realpython.com/python-sockets/ from echoCommon import EchoCommon from select import select @@ -27,7 +26,7 @@ def run(self): while True: data = conn.recv(1024) if data: - self.echo(f'< {data.decode()} >') + self.tell(f'< {data.decode()} >') else: break conn.sendall(data) @@ -35,11 +34,6 @@ def run(self): listenSocket.close() self.log(e) - def done(*any): - print() - print('done') - exit() - def usage(self, name): print(f'Usage: {name} [options] [address] [port]') From 5ba2cf6e3ef913dea25bd509aa6e6bda98563586 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:13:02 +0200 Subject: [PATCH 25/56] syntax check CI --- .github/workflows/c-cpp.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index f848132..124d99f 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -16,9 +16,9 @@ jobs: runs-on: ubuntu-latest steps: - - name: "Checkout repository" + - name: Checkout repository uses: actions/checkout@v3 - - name: "build silent version" + - name: "build config silent" run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent - - name: "build verbose version" + - name: "build config verbose" run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose From e8d318f511be2f0d8cad21f3be20f6c3908da361 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:14:55 +0200 Subject: [PATCH 26/56] syntax check CI --- .github/workflows/c-cpp.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 124d99f..2585b5f 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - - name: "build config silent" + - name: build config "silent" run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent - - name: "build config verbose" + - name: build config "verbose" run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose From 8e6a363c5f86e6830d1f920ec18ff49a497f41fd Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:16:33 +0200 Subject: [PATCH 27/56] syntax check CI --- .github/workflows/c-cpp.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 2585b5f..ae04b96 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -6,9 +6,9 @@ name: C/C++ CI on: push: - branches: [ "dev" ] + branches: [ 'dev' ] pull_request: - branches: [ "dev" ] + branches: [ 'dev' ] jobs: buildVersions: From f1c4f908e404b4d9af564ffe9de2cfba669068db Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:23:30 +0200 Subject: [PATCH 28/56] syntax check CI --- .github/workflows/c-cpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index ae04b96..3baf5fc 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -11,7 +11,7 @@ on: branches: [ 'dev' ] jobs: - buildVersions: + build Configs: runs-on: ubuntu-latest From 28725f909370a85066baa20f1136afb21986a19f Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:24:43 +0200 Subject: [PATCH 29/56] syntax check CI --- .github/workflows/c-cpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 3baf5fc..05b1dba 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -11,7 +11,7 @@ on: branches: [ 'dev' ] jobs: - build Configs: + buildConfigs: runs-on: ubuntu-latest From e08a62edac1c5fe82ddd84fc32b3463f064f9bc0 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:25:36 +0200 Subject: [PATCH 30/56] syntax check CI --- .github/workflows/c-cpp.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 05b1dba..875241c 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -13,12 +13,12 @@ on: jobs: buildConfigs: - runs-on: ubuntu-latest + runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - name: build config "silent" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent - - name: build config "verbose" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: build config "silent" + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent + - name: build config "verbose" + run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose From 9af3e5299f46db4ed9f6e2e3d009f19b5447231b Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 13:48:35 +0200 Subject: [PATCH 31/56] review comments rework --- code/TCP_Srv_Base.cpp | 14 +++++++++----- code/TCP_Srv_Base.h | 34 +++++++++++++++++++--------------- make/TCP_Srv_Echo.make | 7 +++++++ make/build.sh | 28 +++++++++++++++++++++------- make/howto_build_VS.md | 10 +--------- make/premake5.lua | 15 ++++++++++++++- py/echoClient.py | 17 +++++++++++++++-- py/echoCommon.py | 2 +- py/echoServer.py | 31 +++++++++++++++++++++---------- 9 files changed, 108 insertions(+), 50 deletions(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 7faadcf..c8cc65e 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -59,8 +59,11 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) size_t size = recv(clientSocket, buff, sizeof(Buffer), 0); if (size > 0) { - TRACE_TM("<- " << size) - process(clientSocket, buff, size, nr); + do { + TRACE_TM("<- " << size) + process(clientSocket, buff, size, nr); + size = recv(clientSocket, buff, sizeof(Buffer), 0); + } while (size > 0); } else { @@ -91,6 +94,9 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) void TCP_Srv_Base::run(const UINT16 port) { std::cout << "..." << endl; + TRACE("timeout:" << setw(6) << SELECT_MILLI_SECONDS << " ms") + TRACE("buffer :" << setw(6) << buffSize << " bytes") + bool cont = true; SOCKET listenSocket = INVALID_SOCKET; @@ -135,9 +141,7 @@ void TCP_Srv_Base::run(const UINT16 port) // select and accept loop if (cont) { - cout - << "running on port " << port << endl - << "press Ctrl+C to stop" << endl; + cout << "port :" << setw(6) << port << endl; } while (cont) diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 818a31f..90ce362 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -7,6 +7,21 @@ #ifndef TCP_SRV_BASE_H #define TCP_SRV_BASE_H +// ============================================================ +// behavior settings by compile time defines + +// select timeout in milli seconds +#ifndef SELECT_MILLI_SECONDS +#define SELECT_MILLI_SECONDS 10 +#endif + +// buffer size for reading +#ifndef READ_BUFFER_SIZE +#define READ_BUFFER_SIZE 1024 +#endif + +// ============================================================ + #include "BaseTypes.h" #ifdef _WIN32 @@ -17,17 +32,6 @@ using SOCKET = INT32; #endif #include -#ifndef SELECT_SECONDS -#define SELECT_SECONDS 0 -#endif - -#ifndef SELECT_MILLI_SECONDS -#define SELECT_MILLI_SECONDS 10 -#endif - -#ifndef READ_BUFFER_SIZE -#define READ_BUFFER_SIZE 1024 -#endif class TCP_Srv_Base { @@ -39,10 +43,10 @@ class TCP_Srv_Base void run(const INT32 argc, const CONST_C_STRING* const argv); protected: - constexpr static UINT32 tmSec = SELECT_SECONDS; - constexpr static UINT32 tmMic = SELECT_MILLI_SECONDS * 1000; - constexpr static UINT32 bufSize = READ_BUFFER_SIZE; - using Buffer = CHAR[bufSize]; + constexpr static UINT32 tmSec = SELECT_MILLI_SECONDS / 1000; + constexpr static UINT32 tmMic = (SELECT_MILLI_SECONDS % 1000) * 1000; + constexpr static UINT32 buffSize = READ_BUFFER_SIZE; + using Buffer = CHAR[buffSize]; // process received data // must be implemented by derived class diff --git a/make/TCP_Srv_Echo.make b/make/TCP_Srv_Echo.make index 72fa0e1..b5696b8 100644 --- a/make/TCP_Srv_Echo.make +++ b/make/TCP_Srv_Echo.make @@ -49,6 +49,13 @@ ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werr ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall ALL_LDFLAGS += $(LDFLAGS) -pthread +else ifeq ($(config),vsmall) +OBJDIR = obj/gcc/vsmall +DEFINES += -DDEBUG -DVERBOSE -DSELECT_MILLI_SECONDS=1000 -DREAD_BUFFER_SIZE=16 +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall +ALL_LDFLAGS += $(LDFLAGS) -pthread + endif # Per File Configurations diff --git a/make/build.sh b/make/build.sh index a493fed..ad0e35b 100644 --- a/make/build.sh +++ b/make/build.sh @@ -4,21 +4,26 @@ help() { echo "build TCP_Srv_Echo sample" echo "options:" - echo "-s config; silent optimized for spped (default)" - echo "-v config: verbose with debug information" + echo "-S config; silent" + echo "-V config: verbose" + echo "-v config: vsmall" echo "-k keep untracked artifacts" + echo "-r run binary" echo "-h this help" exit } keep= conf=silent -while getopts hkvs option; do +run= +while getopts hkVSvr option; do case $option in - (h) help;; + (V) conf=verbose;; + (S) conf=silent;; + (v) conf=vsmall;; (k) keep=1;; - (v) conf=verbose;; - (s) conf=silent;; + (r) run=1;; + (h) help;; esac done @@ -30,4 +35,13 @@ if test -z $keep; then git clean -dxf . 2>/dev/null >/dev/null; fi make -j -f TCP_Srv_Echo.make config=$conf -for b in bin/*; do echo "-> $b"; done +if test $? -ne 0; then exit 1; fi + +bin=bin/TCP_Srv_Echo + +if test ! -z $run; then + $bin +else + echo "-> $bin" +fi + diff --git a/make/howto_build_VS.md b/make/howto_build_VS.md index 5c65f01..7a7564a 100644 --- a/make/howto_build_VS.md +++ b/make/howto_build_VS.md @@ -33,15 +33,7 @@ Done (41ms).. ```` ## 2) build executables in VS - open solution (TCP_Srv/make/TCP_Srv_Echo.sln) -- select a configuration - - _silent_: - - no debug - - no output - - optimized for speed - - _verbose_: - - verbose output - - debug infos - - no optimization +- select a configuration (see header of premake5.lua) - build (and run) ## 3) VS keyboard shortcuts diff --git a/make/premake5.lua b/make/premake5.lua index 6c27f23..15b5762 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -10,11 +10,15 @@ -- - verbose output -- - debug infos -- - no optimization +-- - vsmall: +-- - same as verbose +-- - but with small buffer sizes (16 bytes) +-- - and long select timeout (1 second) -- ============================================================ -- created by Manfred Sorgo workspace 'TCP_Srv_Echo' - configurations { 'silent', 'verbose' } + configurations { 'silent', 'verbose', 'vsmall' } language 'C++' optimize 'On' @@ -32,6 +36,15 @@ workspace 'TCP_Srv_Echo' symbols 'On' optimize 'Off' + filter { 'configurations:vsmall' } + defines { + 'DEBUG', 'VERBOSE', + 'SELECT_MILLI_SECONDS=1000', + 'READ_BUFFER_SIZE=16' + } + symbols 'On' + optimize 'Off' + filter { 'action:vs*' } buildoptions { '/std:c++17 /W4 /wd4100' } objdir 'obj/vs/' diff --git a/py/echoClient.py b/py/echoClient.py index 6a99fa6..84fd65a 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -23,13 +23,26 @@ def tfunc(self, nr:int): self.tell("%02d connecting ..." % nr) s = self.getsocket() s.connect((self.addr, self.port)) - self.tell("%02d connected." % nr) + self.tell("%02d connected" % nr) for n in range(self.loops): if n > 0: sleep(0.25) self.tell("%02d %d / %d send: '%s'" % (nr, n + 1, self.loops, self.message)) s.sendall(self.message.encode()) + s.settimeout(None) + answer = '' data = s.recv(1024) - self.tell("%02d %d / %d recv: '%s'" % (nr, n + 1, self.loops, data.decode())) + print('data:', len(data)) + try: + while len(data) > 0: + answer += data.decode() + print(answer) + s.settimeout(0.125) + data = s.recv(1024) + except Exception as t: + pass + finally: + self.tell("%02d %d / %d recv: '%s'" % (nr, n + 1, self.loops, answer)) + self.tell("%02d closing" % nr) s.close() except Exception as e: self.log("%02d %s" % (nr, e)) diff --git a/py/echoCommon.py b/py/echoCommon.py index 487e05a..59a5804 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -56,7 +56,7 @@ def __init__(self, *args): else: self.tell = self.silent - def getsocket(self): + def getsocket(self) -> socket.socket: return socket.socket(self.prot, socket.SOCK_STREAM) def log(self, *args): diff --git a/py/echoServer.py b/py/echoServer.py index 4fd9ee2..1bb60a7 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -1,10 +1,12 @@ # ============================================================ -# simple TCP echo server +# TCP echo server # ============================================================ # created by Manfred Sorgo from echoCommon import EchoCommon from select import select +from threading import Thread +from socket import socket class EchoServer(EchoCommon): def __init__(self, *args): @@ -21,19 +23,28 @@ def run(self): for n in range(20): rd, *x = select([listenSocket], [], [], 0.05) if rd: - conn, *x = listenSocket.accept() - with conn: - while True: - data = conn.recv(1024) - if data: - self.tell(f'< {data.decode()} >') - else: - break - conn.sendall(data) + Thread(target=self.tfunc, args=(listenSocket,)).start() + except Exception as e: listenSocket.close() self.log(e) + def tfunc(self, listenSocket:socket): + try: + clientSocket, *x = listenSocket.accept() + with clientSocket: + addr, port = clientSocket.getpeername() + self.tell('C', addr, port) + while True: + data = clientSocket.recv(1024) + if not data: break + self.tell(f'{port} < {data.decode()} >') + clientSocket.sendall(data) + self.tell('X', port) + clientSocket.close() + except Exception as e: + self.log('tfunc:', e) + def usage(self, name): print(f'Usage: {name} [options] [address] [port]') From fb394b350ee6a4cec94993505c65d0cdb22b3654 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:11:59 +0200 Subject: [PATCH 32/56] review comments rework --- code/TCP_Srv_Base.h | 1 - make/premake5.lua | 2 +- py/echoClient.py | 23 ++++++++++++++++------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 90ce362..3b631df 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -32,7 +32,6 @@ using SOCKET = INT32; #endif #include - class TCP_Srv_Base { public: diff --git a/make/premake5.lua b/make/premake5.lua index 15b5762..420c867 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -24,7 +24,7 @@ workspace 'TCP_Srv_Echo' project 'TCP_Srv_Echo' kind 'ConsoleApp' - files { '../code/*.cpp' } + files { '../code/TCP_Srv_*.cpp' } filter { 'configurations:silent' } defines { 'NDEBUG' } diff --git a/py/echoClient.py b/py/echoClient.py index 84fd65a..2ec00e2 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -26,27 +26,36 @@ def tfunc(self, nr:int): self.tell("%02d connected" % nr) for n in range(self.loops): if n > 0: sleep(0.25) - self.tell("%02d %d / %d send: '%s'" % (nr, n + 1, self.loops, self.message)) - s.sendall(self.message.encode()) + self.tell("%02d %d / %d send: %s" % (nr, n + 1, self.loops, self.getmessage())) + s.sendall(self.getdata()) s.settimeout(None) - answer = '' + rdata = bytes() data = s.recv(1024) - print('data:', len(data)) try: while len(data) > 0: - answer += data.decode() - print(answer) + rdata += data s.settimeout(0.125) data = s.recv(1024) except Exception as t: pass finally: - self.tell("%02d %d / %d recv: '%s'" % (nr, n + 1, self.loops, answer)) + if len(rdata) > 0: + self.tell("%02d %d / %d recv: %s" % (nr, n + 1, self.loops, self.decode(rdata))) self.tell("%02d closing" % nr) s.close() except Exception as e: self.log("%02d %s" % (nr, e)) + def decode(self, data:bytes) -> str: + return data.decode() + + def getmessage(self) -> str: + return self.message + + def getdata(self) -> bytes: + return self.message.encode() + + if __name__ == '__main__': from sys import argv cl = EchoClient(*argv[1:]) From 80b8530b1f7e6724c918b9619a793614e53a5b83 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:11:57 +0200 Subject: [PATCH 33/56] review comments rework --- .github/workflows/c-cpp.yml | 10 ++- .gitignore | 1 - code/TCP_Srv_Base.cpp | 79 +++++++++++----------- code/TCP_Srv_Base.h | 2 +- make/Makefile | 52 ++++++++++++++ make/TCP_Srv_Echo.make | 6 +- make/build.sh | 26 +++---- make/{howto_build_VS.md => howto_build.md} | 53 ++++++++++++--- make/premake5.lua | 6 +- 9 files changed, 164 insertions(+), 71 deletions(-) create mode 100644 make/Makefile rename make/{howto_build_VS.md => howto_build.md} (52%) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 875241c..74132f4 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -18,7 +18,11 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - - name: build config "silent" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=silent + - name: prepare build + run: chmod 755 make/build.sh + - name: build config "prod" + run: make/build.sh -p - name: build config "verbose" - run: cd make; git clean -dxf .; make -j -f TCP_Srv_Echo.make config=verbose + run: cd make; git clean -dxf .; make -j config=verbose + - name: build config "vsmall" + run: cd make; git clean -dxf .; make -j config=vsmall diff --git a/.gitignore b/.gitignore index d870998..ec15cce 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,6 @@ __pycache__ # premake / build -Makefile build bin obj diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index c8cc65e..b337c1c 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -36,46 +36,6 @@ using std::setw; #define TRACE_TM(MSG) #endif - -void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) -{ - bool cont = true; - TRACE_TM("CON") - - while(cont) - { - fd_set cset; - FD_ZERO(&cset); - FD_SET(clientSocket, &cset); - tval tv { tmSec, tmMic }; - if (select(0, &cset, nullptr, nullptr, &tv) < 0) - { - TRACE_TM("ERR select") - cont = false; - } - else if (FD_ISSET(clientSocket, &cset)) - { - Buffer buff; - size_t size = recv(clientSocket, buff, sizeof(Buffer), 0); - if (size > 0) - { - do { - TRACE_TM("<- " << size) - process(clientSocket, buff, size, nr); - size = recv(clientSocket, buff, sizeof(Buffer), 0); - } while (size > 0); - } - else - { - TRACE_TM("EX") - cont = false; - } - } - } - closesocket(clientSocket); - endOfThread(); -} - void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { if (argc > 1) @@ -182,6 +142,45 @@ void TCP_Srv_Base::run(const UINT16 port) #endif } +void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) +{ + bool cont = true; + TRACE_TM("CON") + + while(cont) + { + fd_set cset; + FD_ZERO(&cset); + FD_SET(clientSocket, &cset); + tval tv { tmSec, tmMic }; + if (select(0, &cset, nullptr, nullptr, &tv) < 0) + { + TRACE_TM("ERR select") + cont = false; + } + else if (FD_ISSET(clientSocket, &cset)) + { + Buffer buff; + size_t size = recv(clientSocket, buff, sizeof(Buffer), 0); + if (size > 0) + { + do { + TRACE_TM("<- " << size) + process(clientSocket, buff, size, nr); + size = recv(clientSocket, buff, sizeof(Buffer), 0); + } while (size > 0); + } + else + { + TRACE_TM("EX") + cont = false; + } + } + } + closesocket(clientSocket); + endOfThread(); +} + void TCP_Srv_Base::startThread(SOCKET clientSocket) { mutexlock lock(mMtxStat); diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 3b631df..a5a633f 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -39,7 +39,7 @@ class TCP_Srv_Base // run with given port void run(UINT16 port = 8080); // run with port from 1st CLI argument - void run(const INT32 argc, const CONST_C_STRING* const argv); + void run(INT32 argc, const CONST_C_STRING* argv); protected: constexpr static UINT32 tmSec = SELECT_MILLI_SECONDS / 1000; diff --git a/make/Makefile b/make/Makefile new file mode 100644 index 0000000..82c3200 --- /dev/null +++ b/make/Makefile @@ -0,0 +1,52 @@ +# Alternative GNU Make workspace makefile autogenerated by Premake + +ifndef config + config=prod +endif + +ifndef verbose + SILENT = @ +endif + +ifeq ($(config),prod) + TCP_Srv_Echo_config = prod + +else ifeq ($(config),verbose) + TCP_Srv_Echo_config = verbose + +else ifeq ($(config),vsmall) + TCP_Srv_Echo_config = vsmall + +else + $(error "invalid configuration $(config)") +endif + +PROJECTS := TCP_Srv_Echo + +.PHONY: all clean help $(PROJECTS) + +all: $(PROJECTS) + +TCP_Srv_Echo: +ifneq (,$(TCP_Srv_Echo_config)) + @echo "==== Building TCP_Srv_Echo ($(TCP_Srv_Echo_config)) ====" + @${MAKE} --no-print-directory -C . -f TCP_Srv_Echo.make config=$(TCP_Srv_Echo_config) +endif + +clean: + @${MAKE} --no-print-directory -C . -f TCP_Srv_Echo.make clean + +help: + @echo "Usage: make [config=name] [target]" + @echo "" + @echo "CONFIGURATIONS:" + @echo " prod" + @echo " verbose" + @echo " vsmall" + @echo "" + @echo "TARGETS:" + @echo " all (default)" + @echo " clean" + @echo " TCP_Srv_Echo" + @echo "" + @echo "For more information, see https://github.com/premake/premake-core/wiki" \ No newline at end of file diff --git a/make/TCP_Srv_Echo.make b/make/TCP_Srv_Echo.make index b5696b8..590b94c 100644 --- a/make/TCP_Srv_Echo.make +++ b/make/TCP_Srv_Echo.make @@ -1,7 +1,7 @@ # Alternative GNU Make project makefile autogenerated by Premake ifndef config - config=silent + config=prod endif ifndef verbose @@ -35,8 +35,8 @@ endef define POSTBUILDCMDS endef -ifeq ($(config),silent) -OBJDIR = obj/gcc/silent +ifeq ($(config),prod) +OBJDIR = obj/gcc/prod DEFINES += -DNDEBUG ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall diff --git a/make/build.sh b/make/build.sh index ad0e35b..7e8c87d 100644 --- a/make/build.sh +++ b/make/build.sh @@ -2,11 +2,12 @@ help() { - echo "build TCP_Srv_Echo sample" + echo "Usage: $(basename $0) options" echo "options:" - echo "-S config; silent" - echo "-V config: verbose" - echo "-v config: vsmall" + echo "-p config; prod" + echo "-v config: verbose" + echo "-s config: vsmall" + echo "" echo "-k keep untracked artifacts" echo "-r run binary" echo "-h this help" @@ -14,26 +15,27 @@ help() } keep= -conf=silent +conf= run= -while getopts hkVSvr option; do +while getopts hkrspv option; do case $option in - (V) conf=verbose;; - (S) conf=silent;; - (v) conf=vsmall;; + (v) conf=verbose;; + (p) conf=prod;; + (s) conf=vsmall;; (k) keep=1;; (r) run=1;; (h) help;; esac done -cd $(dirname $0) +if test -z $conf; then help; fi -git clean -dxf bin 2>/dev/null >/dev/null +cd $(dirname $0) if test -z $keep; then git clean -dxf . 2>/dev/null >/dev/null; fi -make -j -f TCP_Srv_Echo.make config=$conf +make config=$conf clean +make -j config=$conf if test $? -ne 0; then exit 1; fi diff --git a/make/howto_build_VS.md b/make/howto_build.md similarity index 52% rename from make/howto_build_VS.md rename to make/howto_build.md index 7a7564a..d97d70d 100644 --- a/make/howto_build_VS.md +++ b/make/howto_build.md @@ -1,10 +1,47 @@ -# Visual Studio build -## preconditions +# how to build +## gcc build +### preconditions +- make +- gcc +### make +- call make help to see available configurations +- call make clean with a configuration +- call make -j with a configuration + +````shell +$> make help +Usage: make [config=name] [target] + +CONFIGURATIONS: + silent + verbose + vsmall + +TARGETS: + all (default) + clean + +$> make config=vsmall clean +Cleaning TCP_Srv_Echo + +$> make -j config=vsmall +==== Building TCP_Srv_Echo (vsmall) ==== +Creating obj/gcc/vsmall +Creating bin +TCP_Srv_Base.cpp +TCP_Srv_Echo.cpp +TCP_Srv_Echo_main.cpp +Linking TCP_Srv_Echo +```` + + +## Visual Studio build +### preconditions - premake5 - Visual Studio -## 1) generate VS solution using premake5 -### 1.1) find out suitable VS version using premake5 --help +### 1) generate VS solution using premake5 +#### 1.1) find out suitable VS version using premake5 --help ````shell $> premake5 --help @@ -21,7 +58,7 @@ ACTIONS vs2019 Generate Visual Studio 2019 project files vs2022 Generate Visual Studio 2022 project files ```` -### 1.2) generate solution with your VS version +#### 1.2) generate solution with your VS version e.g. VS 2019 ````shell TCP_Srv/make$> premake5 vs2019 @@ -31,11 +68,11 @@ Generated TCP_Srv_Echo.sln... Generated TCP_Srv_Echo.vcxproj... Done (41ms).. ```` -## 2) build executables in VS +### 2) build executables in VS - open solution (TCP_Srv/make/TCP_Srv_Echo.sln) -- select a configuration (see header of premake5.lua) +- select a configuration (see also header of premake5.lua) - build (and run) -## 3) VS keyboard shortcuts +### 3) VS keyboard shortcuts - build: [CTRL] [shift] [B] - (build and) run: [CTRL] [F5] diff --git a/make/premake5.lua b/make/premake5.lua index 420c867..779fb39 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -2,7 +2,7 @@ -- premake5 build rules -- TCP_Srv_Echo based on TCP_Srv_Base -- configurations: --- - silent: +-- - prod: -- - no debug -- - no output -- - optimized for speed @@ -18,7 +18,7 @@ -- created by Manfred Sorgo workspace 'TCP_Srv_Echo' - configurations { 'silent', 'verbose', 'vsmall' } + configurations { 'prod', 'verbose', 'vsmall' } language 'C++' optimize 'On' @@ -26,7 +26,7 @@ workspace 'TCP_Srv_Echo' kind 'ConsoleApp' files { '../code/TCP_Srv_*.cpp' } - filter { 'configurations:silent' } + filter { 'configurations:prod' } defines { 'NDEBUG' } symbols 'Off' optimize 'Speed' From 8ce9127c18384c5d386fc3bfd5265f2b7e52852a Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:14:37 +0200 Subject: [PATCH 34/56] review comments rework --- .github/workflows/c-cpp.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 74132f4..93785aa 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -23,6 +23,6 @@ jobs: - name: build config "prod" run: make/build.sh -p - name: build config "verbose" - run: cd make; git clean -dxf .; make -j config=verbose + run: make/build.sh -v - name: build config "vsmall" - run: cd make; git clean -dxf .; make -j config=vsmall + run: make/build.sh -s From b76ebdf2eca0e1de3bf7f97a0c5cb9afab2bc51b Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 2 Apr 2024 19:31:50 +0200 Subject: [PATCH 35/56] review comments rework --- code/TCP_Srv_Base.cpp | 9 +++++++++ code/TCP_Srv_Base.h | 7 ++++++- code/TCP_Srv_Echo.h | 5 +++-- make/TCP_Srv_Echo.make | 2 +- make/howto_build.md | 2 +- make/premake5.lua | 5 ++--- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index b337c1c..a92c13f 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -187,6 +187,7 @@ void TCP_Srv_Base::startThread(SOCKET clientSocket) ++mCnt; ++mNum; std::thread(&TCP_Srv_Base::tm, this, clientSocket, mNum).detach(); + displayThreads(); } void TCP_Srv_Base::endOfThread() @@ -200,5 +201,13 @@ void TCP_Srv_Base::endOfThread() mNum = 0; TRACE("--- no clients ---") } + displayThreads(); } +} + +void TCP_Srv_Base::displayThreads() const +{ +#ifndef VERBOSE + cout << "threads:" << setw(6) << mCnt << '\r'; +#endif } \ No newline at end of file diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index a5a633f..6a7a4b2 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -42,9 +42,12 @@ class TCP_Srv_Base void run(INT32 argc, const CONST_C_STRING* argv); protected: + // timeval seconds constexpr static UINT32 tmSec = SELECT_MILLI_SECONDS / 1000; + // timeval microseconds constexpr static UINT32 tmMic = (SELECT_MILLI_SECONDS % 1000) * 1000; - constexpr static UINT32 buffSize = READ_BUFFER_SIZE; + // (minimum) buffer size + constexpr static UINT32 buffSize = READ_BUFFER_SIZE < 4 ? 4 : READ_BUFFER_SIZE; using Buffer = CHAR[buffSize]; // process received data @@ -68,6 +71,8 @@ class TCP_Srv_Base void startThread(SOCKET clientSocket); // decrease thread count, reset thread number when count is 0 void endOfThread(); + // display current number of threads + void displayThreads() const; }; #endif // _H \ No newline at end of file diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index d2d3d2f..81dd1e7 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -14,8 +14,9 @@ class TCP_Srv_Echo : public TCP_Srv_Base public: inline TCP_Srv_Echo() = default; protected: - // simple implementation of process: echo received data - // trace received data if TRACE_ON is defined + // simple implementation of process: + // - echo received data + // - trace received data if VERBOSE is defined void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) final; }; diff --git a/make/TCP_Srv_Echo.make b/make/TCP_Srv_Echo.make index 590b94c..6825a38 100644 --- a/make/TCP_Srv_Echo.make +++ b/make/TCP_Srv_Echo.make @@ -51,7 +51,7 @@ ALL_LDFLAGS += $(LDFLAGS) -pthread else ifeq ($(config),vsmall) OBJDIR = obj/gcc/vsmall -DEFINES += -DDEBUG -DVERBOSE -DSELECT_MILLI_SECONDS=1000 -DREAD_BUFFER_SIZE=16 +DEFINES += -DDEBUG -DVERBOSE -DSELECT_MILLI_SECONDS=1000 -DREAD_BUFFER_SIZE=8 ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall ALL_LDFLAGS += $(LDFLAGS) -pthread diff --git a/make/howto_build.md b/make/howto_build.md index d97d70d..07c7280 100644 --- a/make/howto_build.md +++ b/make/howto_build.md @@ -13,7 +13,7 @@ $> make help Usage: make [config=name] [target] CONFIGURATIONS: - silent + prod verbose vsmall diff --git a/make/premake5.lua b/make/premake5.lua index 779fb39..56f8e1a 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -12,7 +12,7 @@ -- - no optimization -- - vsmall: -- - same as verbose --- - but with small buffer sizes (16 bytes) +-- - but with small buffer sizes (8 bytes) -- - and long select timeout (1 second) -- ============================================================ -- created by Manfred Sorgo @@ -20,7 +20,6 @@ workspace 'TCP_Srv_Echo' configurations { 'prod', 'verbose', 'vsmall' } language 'C++' - optimize 'On' project 'TCP_Srv_Echo' kind 'ConsoleApp' @@ -40,7 +39,7 @@ workspace 'TCP_Srv_Echo' defines { 'DEBUG', 'VERBOSE', 'SELECT_MILLI_SECONDS=1000', - 'READ_BUFFER_SIZE=16' + 'READ_BUFFER_SIZE=8' } symbols 'On' optimize 'Off' From e5fc5ea83111775d40e2f183c4884743ebc84a6a Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:37:45 +0200 Subject: [PATCH 36/56] review comments rework --- code/TCP_Srv_Base.cpp | 58 +++++++++++++++++++++++++++++--------- code/TCP_Srv_Base.h | 21 ++++++++++++-- code/TCP_Srv_Echo.cpp | 31 ++++++++++++++++++-- code/TCP_Srv_Echo.h | 8 ++++++ code/TCP_Srv_Echo_main.cpp | 5 +--- make/build.sh | 16 +++++------ 6 files changed, 108 insertions(+), 31 deletions(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index a92c13f..1e4c633 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -19,12 +19,16 @@ using tval = timeval; #include // atoi #include -using std::cout; -using std::cerr; -using std::endl; +using std::cout, std::cerr, std::endl, std::flush; #include using std::setw; +#include +using std::regex, std::regex_match; + +#include +using std::filesystem::path; + // verbosity #ifdef VERBOSE // general trace @@ -40,7 +44,31 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { if (argc > 1) { - run(static_cast(atoi(argv[1]))); + // check for help: exit if called + regex rxHelp {"^-[hH]$"}; + for (INT32 n = 1; n < argc; ++n) + { + if (regex_match(argv[n], rxHelp)) + { + help(path(argv[0]).filename().string()); + return; + } + } + // no help call in args - check for port or other args + UINT16 port = defPort; + regex rxPort {"^\\d{2,5}$"}; + for (INT32 n = 1; n < argc; ++n) + { + if (regex_match(argv[n], rxPort)) + { + port = static_cast(atoi(argv[n])); + } + else if (not handlearg(argv[n])) + { + return; + } + } + run(port); } else { @@ -48,9 +76,6 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) } } -// trace error and exit the processing -#define ERR_X(MSG) cerr << endl << MSG << endl; cont = false; - void TCP_Srv_Base::run(const UINT16 port) { std::cout << "..." << endl; @@ -66,7 +91,8 @@ void TCP_Srv_Base::run(const UINT16 port) WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - ERR_X("WSAStartup failed") + cerr << "WSAStartup failed" << endl; + cont = false; } } #endif @@ -76,7 +102,8 @@ void TCP_Srv_Base::run(const UINT16 port) listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket < 0) { - ERR_X("socket failed") + cerr << "socket failed" << endl; + cont = false; } } // bind socket to port @@ -89,13 +116,15 @@ void TCP_Srv_Base::run(const UINT16 port) if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { - ERR_X("bind failed: port " << port) + cerr << "bind failed: port " << port << endl; + cont = false; } } // listen to socket if (cont and (listen(listenSocket, SOMAXCONN) < 0)) { - ERR_X("listen failed") + cerr << "listen failed" << endl; + cont = false; } // select and accept loop @@ -113,7 +142,8 @@ void TCP_Srv_Base::run(const UINT16 port) tval tv { tmSec, tmMic }; if (select(0, &lset, nullptr, nullptr, &tv) < 0) { - ERR_X("listen select failed") + cerr << "listen select failed" << endl; + cont = false; } // accept to new client socket if listen socket is set @@ -122,7 +152,7 @@ void TCP_Srv_Base::run(const UINT16 port) SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); if (clientSocket < 0) { - ERR_X("accept failed") + cerr << "accept failed" << endl; } // start thread with client socket else @@ -208,6 +238,6 @@ void TCP_Srv_Base::endOfThread() void TCP_Srv_Base::displayThreads() const { #ifndef VERBOSE - cout << "threads:" << setw(6) << mCnt << '\r'; + cout << "threads:" << setw(6) << mCnt << '\r' << flush; #endif } \ No newline at end of file diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 6a7a4b2..9c29560 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -31,13 +31,14 @@ using SOCKET = INT32; #endif #include +#include class TCP_Srv_Base { public: inline TCP_Srv_Base() = default; // run with given port - void run(UINT16 port = 8080); + void run(UINT16 port = defPort); // run with port from 1st CLI argument void run(INT32 argc, const CONST_C_STRING* argv); @@ -53,11 +54,25 @@ class TCP_Srv_Base // process received data // must be implemented by derived class virtual void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) = 0; - + + // handle unmatched argument + // to be implemented by derived class + inline virtual bool handlearg(CONST_C_STRING argv) + { + return true; + } + + // display help with called filename (rvalue reference for efficiency) + // to be implemented by derived class + inline virtual void help(const std::string&& argv0) const {} + // prevent from parallel output std::mutex mMtxOut; using mutexlock = std::unique_lock; + // default port + constexpr static UINT16 defPort = 8080; + private: // thread method void tm(SOCKET clientSocket, UINT32 nr); @@ -72,7 +87,9 @@ class TCP_Srv_Base // decrease thread count, reset thread number when count is 0 void endOfThread(); // display current number of threads + // (unless VERBOSE is defined) void displayThreads() const; + }; #endif // _H \ No newline at end of file diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index c59c974..aa1d477 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -1,10 +1,37 @@ #include "TCP_Srv_Echo.h" #include -using std::cout; -using std::endl; +using std::cout, std::cerr, std::endl; #include +#include + +bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) +{ + if (std::setlocale(LC_ALL, argv) == nullptr) + { + cerr << "setlocale failed: " << argv << endl; + return false; + } +#ifdef VERBOSE + else + { + cout << "locale : " << argv << endl; + } +#endif + return true; +} + +void TCP_Srv_Echo::help(const std::string&& argv0) const +{ + cout + << endl + << "usage : " << argv0 << " [-h] [port] [locale]" << endl + << "-h : this help" << endl + << "port : 2-5 digits, default: " << defPort << endl + << "locale: see setlocale" << endl; +} + void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t size, const UINT32 nr) { send(clientSocket, buff, size, 0); diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index 81dd1e7..947e624 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -13,11 +13,19 @@ class TCP_Srv_Echo : public TCP_Srv_Base { public: inline TCP_Srv_Echo() = default; + protected: // simple implementation of process: // - echo received data // - trace received data if VERBOSE is defined void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) final; + + // handle unmatched argument (as locale to set) + bool handlearg(CONST_C_STRING argv) final; + + // display help + void help(const std::string&& argv0) const final; + }; #endif // _H diff --git a/code/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp index 477b18e..2ea21b0 100644 --- a/code/TCP_Srv_Echo_main.cpp +++ b/code/TCP_Srv_Echo_main.cpp @@ -1,10 +1,7 @@ #include "TCP_Srv_Echo.h" -#include -#include -#include + int main(const INT32 argc, const CONST_C_STRING* const argv) { - std::setlocale(LC_ALL, "de_DE.UTF-8"); TCP_Srv_Echo srv; srv.run(argc, argv); return 0; diff --git a/make/build.sh b/make/build.sh index 7e8c87d..2f53079 100644 --- a/make/build.sh +++ b/make/build.sh @@ -2,38 +2,36 @@ help() { - echo "Usage: $(basename $0) options" + echo "Usage: $(basename $0) options [port]" echo "options:" echo "-p config; prod" echo "-v config: verbose" echo "-s config: vsmall" echo "" - echo "-k keep untracked artifacts" echo "-r run binary" echo "-h this help" exit } -keep= conf= run= +port= while getopts hkrspv option; do case $option in (v) conf=verbose;; (p) conf=prod;; (s) conf=vsmall;; - (k) keep=1;; (r) run=1;; (h) help;; esac done +shift $(($OPTIND - 1)) + if test -z $conf; then help; fi cd $(dirname $0) -if test -z $keep; then git clean -dxf . 2>/dev/null >/dev/null; fi - make config=$conf clean make -j config=$conf @@ -41,9 +39,9 @@ if test $? -ne 0; then exit 1; fi bin=bin/TCP_Srv_Echo -if test ! -z $run; then - $bin -else +if test -z $run; then echo "-> $bin" +else + $bin $* fi From baa3aa1d416e75f1e9213e12d7a232160bac19db Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:28:44 +0200 Subject: [PATCH 37/56] review comments rework --- code/TCP_Srv_Base.cpp | 44 +++++++++++++++++++++++++++++++------------ code/TCP_Srv_Base.h | 20 ++++++++++++++++---- code/TCP_Srv_Echo.cpp | 35 +++++++++++++++++----------------- code/TCP_Srv_Echo.h | 8 +++++--- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 1e4c633..8d931f4 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -29,15 +29,11 @@ using std::regex, std::regex_match; #include using std::filesystem::path; -// verbosity +// general trace macro #ifdef VERBOSE -// general trace #define TRACE(MSG) cout << MSG << endl; -// mutex locked trace with thread number -#define TRACE_TM(MSG) { mutexlock lock(mMtxOut); TRACE(setw(3) << nr << ' ' << MSG) } #else #define TRACE(MSG) -#define TRACE_TM(MSG) #endif void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) @@ -82,7 +78,9 @@ void TCP_Srv_Base::run(const UINT16 port) TRACE("timeout:" << setw(6) << SELECT_MILLI_SECONDS << " ms") TRACE("buffer :" << setw(6) << buffSize << " bytes") + // indicator for continuation bool cont = true; + // listen socket SOCKET listenSocket = INVALID_SOCKET; #ifdef _WIN32 @@ -91,7 +89,7 @@ void TCP_Srv_Base::run(const UINT16 port) WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - cerr << "WSAStartup failed" << endl; + cerr << "ERR WSAStartup" << endl; cont = false; } } @@ -102,7 +100,7 @@ void TCP_Srv_Base::run(const UINT16 port) listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket < 0) { - cerr << "socket failed" << endl; + cerr << "ERR socket" << endl; cont = false; } } @@ -116,14 +114,14 @@ void TCP_Srv_Base::run(const UINT16 port) if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { - cerr << "bind failed: port " << port << endl; + cerr << "ERR bind: port " << port << endl; cont = false; } } // listen to socket if (cont and (listen(listenSocket, SOMAXCONN) < 0)) { - cerr << "listen failed" << endl; + cerr << "ERR listen" << endl; cont = false; } @@ -142,7 +140,7 @@ void TCP_Srv_Base::run(const UINT16 port) tval tv { tmSec, tmMic }; if (select(0, &lset, nullptr, nullptr, &tv) < 0) { - cerr << "listen select failed" << endl; + cerr << "ERR listen select" << endl; cont = false; } @@ -152,7 +150,8 @@ void TCP_Srv_Base::run(const UINT16 port) SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); if (clientSocket < 0) { - cerr << "accept failed" << endl; + cerr << "ERR accept" << endl; + cont = false; } // start thread with client socket else @@ -174,6 +173,13 @@ void TCP_Srv_Base::run(const UINT16 port) void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) { +// locally used mutex locked trace with thread number +#ifdef VERBOSE +#define TRACE_TM(MSG) { mutexlock lock(mMtxOut); TRACE(setw(3) << nr << ' ' << MSG) } +#else +#define TRACE_TM(MSG) +#endif + bool cont = true; TRACE_TM("CON") @@ -209,6 +215,8 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) } closesocket(clientSocket); endOfThread(); + +#undef TRACE_TM } void TCP_Srv_Base::startThread(SOCKET clientSocket) @@ -240,4 +248,16 @@ void TCP_Srv_Base::displayThreads() const #ifndef VERBOSE cout << "threads:" << setw(6) << mCnt << '\r' << flush; #endif -} \ No newline at end of file +} + +void TCP_Srv_Base::help(const std::string&& argv0) const +{ + cout + << endl + << "usage : " << argv0 << " [-h] [port]"; + addusage(cout); + cout << endl + << "-h : this help" << endl + << "port : 2-5 digits, default: " << defPort << endl; + addhelp(cout); +} diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 9c29560..1d3d5ae 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -56,15 +56,19 @@ class TCP_Srv_Base virtual void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) = 0; // handle unmatched argument - // to be implemented by derived class + // can be implemented by derived class inline virtual bool handlearg(CONST_C_STRING argv) { return true; } - // display help with called filename (rvalue reference for efficiency) - // to be implemented by derived class - inline virtual void help(const std::string&& argv0) const {} + // add usage item to help + // can be implemented by derived class + virtual void addusage(std::ostream& os) const {} + + // add help item to help + // can be implemented by derived class + virtual void addhelp(std::ostream& os) const {} // prevent from parallel output std::mutex mMtxOut; @@ -73,6 +77,12 @@ class TCP_Srv_Base // default port constexpr static UINT16 defPort = 8080; +#ifdef VERBOSE + constexpr static bool verbose = true; +#else + constexpr static bool verbose = false; +#endif + private: // thread method void tm(SOCKET clientSocket, UINT32 nr); @@ -90,6 +100,8 @@ class TCP_Srv_Base // (unless VERBOSE is defined) void displayThreads() const; + // display help with called filename (rvalue reference for efficiency) + void help(const std::string&& argv0) const; }; #endif // _H \ No newline at end of file diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index aa1d477..dc25bb8 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -3,7 +3,6 @@ #include using std::cout, std::cerr, std::endl; #include - #include bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) @@ -13,35 +12,35 @@ bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) cerr << "setlocale failed: " << argv << endl; return false; } -#ifdef VERBOSE - else + if constexpr (verbose) { cout << "locale : " << argv << endl; } -#endif return true; } -void TCP_Srv_Echo::help(const std::string&& argv0) const +void TCP_Srv_Echo::addusage(std::ostream& os) const +{ + os << " [locale]"; +} + +void TCP_Srv_Echo::addhelp(std::ostream& os) const { - cout - << endl - << "usage : " << argv0 << " [-h] [port] [locale]" << endl - << "-h : this help" << endl - << "port : 2-5 digits, default: " << defPort << endl - << "locale: see setlocale" << endl; + os << "locale: see setlocale" << endl; } void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t size, const UINT32 nr) { send(clientSocket, buff, size, 0); -#ifdef VERBOSE - mutexlock lock(mMtxOut); - cout << std::setw(3) << nr << " <- "; - for (size_t n = 0; n < size; ++n) + + if constexpr (verbose) { - cout << buff[n]; + mutexlock lock(mMtxOut); + cout << std::setw(3) << nr << " <- "; + for (size_t n = 0; n < size; ++n) + { + cout << buff[n]; + } + cout << " ->" << endl; } - cout << " ->" << endl; -#endif } diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index 947e624..cd16357 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -20,11 +20,13 @@ class TCP_Srv_Echo : public TCP_Srv_Base // - trace received data if VERBOSE is defined void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) final; - // handle unmatched argument (as locale to set) + // handle unused argument as locale to set + // does not make a big difference with gcc linux + // but with msvc windows bool handlearg(CONST_C_STRING argv) final; - // display help - void help(const std::string&& argv0) const final; + void addusage(std::ostream& os) const final; + void addhelp(std::ostream& os) const final; }; From 1fb17968803b4259480e83f2a8a01054e4af0ae0 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:01:50 +0200 Subject: [PATCH 38/56] review comments rework --- code/TCP_Srv_Base.cpp | 38 ++++++++++++++++++++++++++------------ py/echoCommon.py | 10 +++++----- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 8d931f4..06d6cdb 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -125,12 +125,13 @@ void TCP_Srv_Base::run(const UINT16 port) cont = false; } - // select and accept loop + // display port if successful sofar if (cont) { cout << "port :" << setw(6) << port << endl; } + // select and accept loop while (cont) { // select @@ -138,6 +139,7 @@ void TCP_Srv_Base::run(const UINT16 port) FD_ZERO(&lset); FD_SET(listenSocket, &lset); tval tv { tmSec, tmMic }; + if (select(0, &lset, nullptr, nullptr, &tv) < 0) { cerr << "ERR listen select" << endl; @@ -160,9 +162,8 @@ void TCP_Srv_Base::run(const UINT16 port) } } } - // should not be reached: - // clean up in case of error - if (listenSocket != INVALID_SOCKET) + // only reached in case of error: clean up + if (not listenSocket < 0) { closesocket(listenSocket); } @@ -173,39 +174,51 @@ void TCP_Srv_Base::run(const UINT16 port) void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) { -// locally used mutex locked trace with thread number -#ifdef VERBOSE -#define TRACE_TM(MSG) { mutexlock lock(mMtxOut); TRACE(setw(3) << nr << ' ' << MSG) } -#else -#define TRACE_TM(MSG) -#endif + // locally used mutex locked trace macro with thread number + #ifdef VERBOSE + #define TRACE_TM(MSG) { mutexlock lock(mMtxOut); TRACE(setw(3) << nr << ' ' << MSG) } + #else + #define TRACE_TM(MSG) + #endif - bool cont = true; + TRACE_TM("CON") + // indicator for continuation + bool cont = true; + + // select and receive loop while(cont) { + // select fd_set cset; FD_ZERO(&cset); FD_SET(clientSocket, &cset); tval tv { tmSec, tmMic }; + if (select(0, &cset, nullptr, nullptr, &tv) < 0) { TRACE_TM("ERR select") cont = false; } + // receive from client socket when select indicates read possible else if (FD_ISSET(clientSocket, &cset)) { Buffer buff; size_t size = recv(clientSocket, buff, sizeof(Buffer), 0); + + // if 1st recv delivers bytes client ist sending if (size > 0) { + // continue recv until no more bytes are sent do { TRACE_TM("<- " << size) process(clientSocket, buff, size, nr); size = recv(clientSocket, buff, sizeof(Buffer), 0); } while (size > 0); } + + // otherwise client has closed connection else { TRACE_TM("EX") @@ -216,7 +229,8 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) closesocket(clientSocket); endOfThread(); -#undef TRACE_TM + // end of locally used trace macro + #undef TRACE_TM } void TCP_Srv_Base::startThread(SOCKET clientSocket) diff --git a/py/echoCommon.py b/py/echoCommon.py index 59a5804..ec820eb 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -73,11 +73,11 @@ def help(self): -h this help syntax: | e.g. address | - - ipv4 n.n.n.n | 1.2.3.4 - - ipv6 n:n:n:n:n:n | 1:2:3:4:5:6 - port nn.. | 8080 - loops n..x | 2x, 12X - threads n..t | 5T, 11t + - ipv4 n.n.n.n | 127.2.3.4 + - ipv6 b:b:b:b:b:b | 1:0F::4:5:6 + port 1-5 digits | 8080 + loops digits + X | 2x, 12X + threads digits + T | 5T, 11t message any other | "hello world" """ ) From 639da32777a9e3849f46ce40dad449c6b0114ce7 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:05:16 +0200 Subject: [PATCH 39/56] CI error fixed --- code/TCP_Srv_Base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 06d6cdb..2603d70 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -163,7 +163,7 @@ void TCP_Srv_Base::run(const UINT16 port) } } // only reached in case of error: clean up - if (not listenSocket < 0) + if (listenSocket >= 0) { closesocket(listenSocket); } From ed5b27e6e1eaa0bd1a928d9b6ff103330fed420f Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:09:47 +0200 Subject: [PATCH 40/56] CI error fixed --- code/TCP_Srv_Base.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 2603d70..4935d43 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -180,7 +180,6 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) #else #define TRACE_TM(MSG) #endif - TRACE_TM("CON") From fc5f57e3faefd5d58ab9e40e64b1a3bdb5f1b4cd Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:44:05 +0200 Subject: [PATCH 41/56] review comments rework, elimination of macros, Trace --- basics.md | 26 ++++++++++++-- code/TCP_Srv_Base.cpp | 71 +++++++++++++++++++------------------- code/TCP_Srv_Base.h | 15 ++------ code/TCP_Srv_Echo.cpp | 26 ++++++++------ code/Trace.cpp | 6 ++++ code/Trace.h | 77 ++++++++++++++++++++++++++++++++++++++++++ make/TCP_Srv_Echo.make | 5 +++ make/premake5.lua | 6 ++-- py/echoClient.py | 13 ++++--- py/echoCommon.py | 37 ++++++++++++-------- py/echoServer.py | 6 ++-- 11 files changed, 199 insertions(+), 89 deletions(-) create mode 100644 code/Trace.cpp create mode 100644 code/Trace.h diff --git a/basics.md b/basics.md index 73e0763..aac405a 100644 --- a/basics.md +++ b/basics.md @@ -36,8 +36,10 @@ int select( const timeval * timeout // [in] ); ``` -### server socket +### server with threads ``` + listenSocket + | socket() | bind() @@ -45,6 +47,8 @@ int select( listen() ________| | | +| fd_set +| | | FD_ZERO() | | | FD_SET() @@ -55,7 +59,7 @@ ________| | | | | N Y |______| | -| accept() -> client socket thread +| accept() -> clientSocket, thread(clientSocket) |___________| ``` @@ -92,3 +96,21 @@ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: data = s.recv(1024) print(f'received {data}') ``` +### server with threads +``` + listenSocket + | + socket() + | + bind() + | + listen() +________| +| | +| select() +| | | +| N Y +|______| | +| |-> thread(listenSocket), accept() -> clientSocket +|__________| +``` diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 4935d43..eb5ce63 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -1,4 +1,5 @@ #include "TCP_Srv_Base.h" +#include "Trace.h" #ifdef _WIN32 // timevel can be used const in VS @@ -6,6 +7,7 @@ using tval = const timeval; // required lib for Winsock #pragma comment(lib, "ws2_32") #else +#include #include #include #define closesocket close @@ -15,7 +17,6 @@ using tval = timeval; #endif #include -#include #include // atoi #include @@ -29,17 +30,11 @@ using std::regex, std::regex_match; #include using std::filesystem::path; -// general trace macro -#ifdef VERBOSE -#define TRACE(MSG) cout << MSG << endl; -#else -#define TRACE(MSG) -#endif - void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { if (argc > 1) { + bool cont = true; // check for help: exit if called regex rxHelp {"^-[hH]$"}; for (INT32 n = 1; n < argc; ++n) @@ -47,24 +42,32 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) if (regex_match(argv[n], rxHelp)) { help(path(argv[0]).filename().string()); - return; + cont = false; + break; } } // no help call in args - check for port or other args - UINT16 port = defPort; - regex rxPort {"^\\d{2,5}$"}; - for (INT32 n = 1; n < argc; ++n) + if (cont) { - if (regex_match(argv[n], rxPort)) + UINT16 port = defPort; + regex rxPort {"^\\d{2,5}$"}; + for (INT32 n = 1; n < argc; ++n) { - port = static_cast(atoi(argv[n])); + if (regex_match(argv[n], rxPort)) + { + port = static_cast(atoi(argv[n])); + } + else if (not handlearg(argv[n])) + { + cont = false; + break; + } } - else if (not handlearg(argv[n])) + if (cont) { - return; + run(port); } } - run(port); } else { @@ -75,8 +78,9 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) void TCP_Srv_Base::run(const UINT16 port) { std::cout << "..." << endl; - TRACE("timeout:" << setw(6) << SELECT_MILLI_SECONDS << " ms") - TRACE("buffer :" << setw(6) << buffSize << " bytes") + Trace() + << "timeout:" << setw(6) << SELECT_MILLI_SECONDS << " ms" << endl + << "buffer :" << setw(6) << buffSize << " bytes" << endl; // indicator for continuation bool cont = true; @@ -174,14 +178,9 @@ void TCP_Srv_Base::run(const UINT16 port) void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) { - // locally used mutex locked trace macro with thread number - #ifdef VERBOSE - #define TRACE_TM(MSG) { mutexlock lock(mMtxOut); TRACE(setw(3) << nr << ' ' << MSG) } - #else - #define TRACE_TM(MSG) - #endif - - TRACE_TM("CON") + { + TraceLock(nr) << "CON" << endl; + } // indicator for continuation bool cont = true; @@ -197,7 +196,7 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) if (select(0, &cset, nullptr, nullptr, &tv) < 0) { - TRACE_TM("ERR select") + TraceLock(nr) << "ERR select" << endl; cont = false; } // receive from client socket when select indicates read possible @@ -211,7 +210,7 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) { // continue recv until no more bytes are sent do { - TRACE_TM("<- " << size) + TraceLock(nr) << "<- " << size << endl; process(clientSocket, buff, size, nr); size = recv(clientSocket, buff, sizeof(Buffer), 0); } while (size > 0); @@ -220,16 +219,13 @@ void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) // otherwise client has closed connection else { - TRACE_TM("EX") + TraceLock(nr) << "EX" << endl; cont = false; } } } closesocket(clientSocket); endOfThread(); - - // end of locally used trace macro - #undef TRACE_TM } void TCP_Srv_Base::startThread(SOCKET clientSocket) @@ -250,7 +246,7 @@ void TCP_Srv_Base::endOfThread() if (mCnt == 0) { mNum = 0; - TRACE("--- no clients ---") + Trace() << "--- no clients ---" << endl; } displayThreads(); } @@ -258,9 +254,10 @@ void TCP_Srv_Base::endOfThread() void TCP_Srv_Base::displayThreads() const { -#ifndef VERBOSE - cout << "threads:" << setw(6) << mCnt << '\r' << flush; -#endif + if constexpr (not Trace::verbose) + { + cout << "threads:" << setw(6) << mCnt << '\r' << flush; + } } void TCP_Srv_Base::help(const std::string&& argv0) const diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 1d3d5ae..ef3af7f 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -27,7 +27,6 @@ #ifdef _WIN32 #include #else -#include using SOCKET = INT32; #endif #include @@ -57,32 +56,24 @@ class TCP_Srv_Base // handle unmatched argument // can be implemented by derived class - inline virtual bool handlearg(CONST_C_STRING argv) + inline virtual bool handlearg(CONST_C_STRING) { return true; } // add usage item to help // can be implemented by derived class - virtual void addusage(std::ostream& os) const {} + virtual void addusage(std::ostream&) const {} // add help item to help // can be implemented by derived class - virtual void addhelp(std::ostream& os) const {} + virtual void addhelp(std::ostream&) const {} - // prevent from parallel output - std::mutex mMtxOut; using mutexlock = std::unique_lock; // default port constexpr static UINT16 defPort = 8080; -#ifdef VERBOSE - constexpr static bool verbose = true; -#else - constexpr static bool verbose = false; -#endif - private: // thread method void tm(SOCKET clientSocket, UINT32 nr); diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index dc25bb8..bd4933f 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -1,4 +1,9 @@ #include "TCP_Srv_Echo.h" +#include "Trace.h" + +#ifndef _WIN32 +#include +#endif #include using std::cout, std::cerr, std::endl; @@ -7,16 +12,17 @@ using std::cout, std::cerr, std::endl; bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) { + bool ok = true; if (std::setlocale(LC_ALL, argv) == nullptr) { - cerr << "setlocale failed: " << argv << endl; - return false; + cerr << "ERR setlocale: " << argv << endl; + ok = false; } - if constexpr (verbose) + else { - cout << "locale : " << argv << endl; + Trace() << "locale : " << argv << endl; } - return true; + return ok; } void TCP_Srv_Echo::addusage(std::ostream& os) const @@ -33,14 +39,14 @@ void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t { send(clientSocket, buff, size, 0); - if constexpr (verbose) + if constexpr (Trace::verbose) { - mutexlock lock(mMtxOut); - cout << std::setw(3) << nr << " <- "; + TraceLock tr(nr); + tr << "<- "; for (size_t n = 0; n < size; ++n) { - cout << buff[n]; + tr << buff[n]; } - cout << " ->" << endl; + tr << " ->" << endl; } } diff --git a/code/Trace.cpp b/code/Trace.cpp new file mode 100644 index 0000000..648e0e8 --- /dev/null +++ b/code/Trace.cpp @@ -0,0 +1,6 @@ + +#ifdef VERBOSE +#include "Trace.h" +std::mutex TraceLock::mMutex; + +#endif // VERBOSE \ No newline at end of file diff --git a/code/Trace.h b/code/Trace.h new file mode 100644 index 0000000..9241922 --- /dev/null +++ b/code/Trace.h @@ -0,0 +1,77 @@ +// ============================================================ +// Tracing dependent on VERBOSE compile define +// intended to avoid macro usage +// ============================================================ +// created by Manfred Sorgo + +#pragma once +#ifndef TRACE_H +#define TRACE_H + +#include "BaseTypes.h" +#include + +#include +#include +#include + +// class Trace blocks (std::cout) output within object scope if VERBOSE is not defined +// if VERBOSE is defined, Trace behaves like std::cout +class Trace +{ +public: +// avoid macro usage +// use: +// constexpr if (Trace::verbose) { ... } +// constexpr if (not Trace::verbose) { ... } +// to eliminate code from compilation +// in opposite to macros there won't be compiler warnings of unused variables +#ifdef VERBOSE + constexpr static bool verbose = true; +#else + constexpr static bool verbose = false; +#endif + Trace() + { + if constexpr (not verbose) + { + std::cout.setstate(std::ios_base::badbit); + } + } + ~Trace() + { + if constexpr (not verbose) + { + std::cout.clear(); + } + } + template + inline std::ostream& operator<<(const T& t) + { + return std::cout << t; + } +}; + +// class TraceLock behaves like class Trace +// in addition, if VERBOSE is defined +// parallel output (of other TraceLock objects) +// is blocked by a mutex lock within object scope +class TraceLock : public Trace +{ +public: +#ifdef VERBOSE + TraceLock() : Trace(), mLock(mMutex) + {} + TraceLock(const UINT32 nr) : TraceLock() + { + *this << std::setw(3) << nr << ' '; + } +private: + static std::mutex mMutex; + std::unique_lock mLock; +#else + TraceLock(const UINT32 = 0) {} +#endif +}; + +#endif // _H diff --git a/make/TCP_Srv_Echo.make b/make/TCP_Srv_Echo.make index 6825a38..c285896 100644 --- a/make/TCP_Srv_Echo.make +++ b/make/TCP_Srv_Echo.make @@ -71,9 +71,11 @@ OBJECTS := GENERATED += $(OBJDIR)/TCP_Srv_Base.o GENERATED += $(OBJDIR)/TCP_Srv_Echo.o GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o +GENERATED += $(OBJDIR)/Trace.o OBJECTS += $(OBJDIR)/TCP_Srv_Base.o OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o +OBJECTS += $(OBJDIR)/Trace.o # Rules # ############################################# @@ -146,6 +148,9 @@ $(OBJDIR)/TCP_Srv_Echo.o: ../code/TCP_Srv_Echo.cpp $(OBJDIR)/TCP_Srv_Echo_main.o: ../code/TCP_Srv_Echo_main.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/Trace.o: ../code/Trace.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -include $(OBJECTS:%.o=%.d) ifneq (,$(PCH)) diff --git a/make/premake5.lua b/make/premake5.lua index 56f8e1a..910a7af 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -23,7 +23,7 @@ workspace 'TCP_Srv_Echo' project 'TCP_Srv_Echo' kind 'ConsoleApp' - files { '../code/TCP_Srv_*.cpp' } + files { '../code/*.cpp' } filter { 'configurations:prod' } defines { 'NDEBUG' } @@ -45,9 +45,9 @@ workspace 'TCP_Srv_Echo' optimize 'Off' filter { 'action:vs*' } - buildoptions { '/std:c++17 /W4 /wd4100' } - objdir 'obj/vs/' warnings 'high' + buildoptions { '/std:c++17 /W4' } + objdir 'obj/vs/' targetdir 'exe' filter { 'action:gmake*' } diff --git a/py/echoClient.py b/py/echoClient.py index 2ec00e2..851533e 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -20,13 +20,12 @@ def run(self): def tfunc(self, nr:int): try: - self.tell("%02d connecting ..." % nr) s = self.getsocket() s.connect((self.addr, self.port)) - self.tell("%02d connected" % nr) + self.tell('%02d CON' % nr) for n in range(self.loops): if n > 0: sleep(0.25) - self.tell("%02d %d / %d send: %s" % (nr, n + 1, self.loops, self.getmessage())) + self.tell('%02d %d / %d send: %s' % (nr, n + 1, self.loops, self.getmessage())) s.sendall(self.getdata()) s.settimeout(None) rdata = bytes() @@ -36,15 +35,15 @@ def tfunc(self, nr:int): rdata += data s.settimeout(0.125) data = s.recv(1024) - except Exception as t: + except Exception: pass finally: if len(rdata) > 0: - self.tell("%02d %d / %d recv: %s" % (nr, n + 1, self.loops, self.decode(rdata))) - self.tell("%02d closing" % nr) + self.tell('%02d %d / %d recv: %s' % (nr, n + 1, self.loops, self.decode(rdata))) s.close() + self.tell('%02d EX' % nr) except Exception as e: - self.log("%02d %s" % (nr, e)) + self.log('%02d %s' % (nr, e)) def decode(self, data:bytes) -> str: return data.decode() diff --git a/py/echoCommon.py b/py/echoCommon.py index ec820eb..7c4418d 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -4,50 +4,57 @@ # created by Manfred Sorgo import re import socket -from getopt import getopt from sys import argv from logging import info as logout, basicConfig as logconfig, INFO from os.path import basename class EchoCommon(object): + # init with cli arguments regardless of order def __init__(self, *args): - self.verbose = False - opts, args = getopt(args, 'vh') - for o, v in opts: - if o == '-v': - self.verbose = True - elif o == '-h': - self.help() - + # ipv4 address rx4 = re.compile(r'^\d+(?:\.\d+){3}$') + # ipv6 address rx6 = re.compile(r'^[0-9a-fA-F]*(?:\:[0-9a-fA-F]*){5}$') + # port rxP = re.compile(r'^\d+$') - rxN = re.compile(r'^(\d+)[xX]+$') + # loops + rxL = re.compile(r'^(\d+)[xX]+$') + # threads rxT = re.compile(r'^(\d+)[tT]+$') + # verbose + rxV = re.compile(r'^-[vV]+$') + # help + rxH = re.compile(r'^-[hH]+$') + self.prot = socket.AF_INET self.addr = '127.0.0.1' self.port = 8080 self.loops = 1 self.threads = 1 self.message = 'hello world' + verbose = False for arg in args: - if rx4.match(arg): + if rxH.match(arg): + self.help() + elif rx4.match(arg): self.self.addr = arg elif rx6.match(arg): self.addr = arg self.prot = socket.AF_INET6 elif rxP.match(arg): self.port = int(arg) - elif rxN.match(arg): - self.loops = int(rxN.match(arg).group(1)) + elif rxL.match(arg): + self.loops = int(rxL.match(arg).group(1)) elif rxT.match(arg): self.threads = int(rxT.match(arg).group(1)) + elif rxV.match(arg): + verbose = True else: self.message = arg - logconfig(format="%(asctime)s %(message)s", level=INFO, datefmt="%H:%M:%S") + logconfig(format='%(asctime)s %(message)s', level=INFO, datefmt='%H:%M:%S') - if self.verbose: + if verbose: self.tell = self.log print() print('addr:', self.addr) diff --git a/py/echoServer.py b/py/echoServer.py index 1bb60a7..a22a063 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -1,5 +1,5 @@ # ============================================================ -# TCP echo server +# multi thread TCP echo server # ============================================================ # created by Manfred Sorgo @@ -34,14 +34,14 @@ def tfunc(self, listenSocket:socket): clientSocket, *x = listenSocket.accept() with clientSocket: addr, port = clientSocket.getpeername() - self.tell('C', addr, port) + self.tell('CON', addr, port) while True: data = clientSocket.recv(1024) if not data: break self.tell(f'{port} < {data.decode()} >') clientSocket.sendall(data) - self.tell('X', port) clientSocket.close() + self.tell('EX', port) except Exception as e: self.log('tfunc:', e) From 79c8e5ab0e348b417a7d2a3221c261a353897729 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 4 Apr 2024 23:35:46 +0200 Subject: [PATCH 42/56] just for the sake of it: ask ChatGPT --- ChatGPT/ChatGPT_dialog_1.md | 146 ++++++++++++++++++++++++++++++++++++ ChatGPT/main.cpp | 0 ChatGPT/server.cpp | 71 ++++++++++++++++++ ChatGPT/server.h | 33 ++++++++ 4 files changed, 250 insertions(+) create mode 100644 ChatGPT/ChatGPT_dialog_1.md create mode 100644 ChatGPT/main.cpp create mode 100644 ChatGPT/server.cpp create mode 100644 ChatGPT/server.h diff --git a/ChatGPT/ChatGPT_dialog_1.md b/ChatGPT/ChatGPT_dialog_1.md new file mode 100644 index 0000000..151cba2 --- /dev/null +++ b/ChatGPT/ChatGPT_dialog_1.md @@ -0,0 +1,146 @@ +# ChatGPT dialog +## 1st question +_c++ multi thread tcp server header and source_ + +## 1st reply +Below is a simple example of a multi-threaded TCP server implemented in C++. This example demonstrates a basic TCP server that can handle multiple client connections simultaneously using threading. + +**Server Header (server.h):** +````cpp +#ifndef SERVER_H +#define SERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TCPServer { +public: + TCPServer(int port); + ~TCPServer(); + + void Start(); + void Stop(); + +private: + void Listen(); + void HandleClient(int clientSocket); + + int port; + int serverSocket; + std::vector clientThreads; + std::atomic running; +}; + +#endif // SERVER_H +```` +**Server Source (server.cpp):** +````cpp +#include "server.h" + +TCPServer::TCPServer(int port) : port(port), running(false) {} + +TCPServer::~TCPServer() { + Stop(); +} + +void TCPServer::Start() { + running = true; + + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == -1) { + std::cerr << "Error creating socket\n"; + return; + } + + sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = INADDR_ANY; + serverAddr.sin_port = htons(port); + + if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { + std::cerr << "Bind failed\n"; + return; + } + + if (listen(serverSocket, 5) < 0) { + std::cerr << "Listen failed\n"; + return; + } + + std::thread listenerThread(&TCPServer::Listen, this); + listenerThread.detach(); +} + +void TCPServer::Stop() { + running = false; + close(serverSocket); + + for (auto& thread : clientThreads) { + thread.join(); + } +} + +void TCPServer::Listen() { + while (running) { + sockaddr_in clientAddr; + socklen_t clientAddrLen = sizeof(clientAddr); + int clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientAddrLen); + if (clientSocket < 0) { + std::cerr << "Accept failed\n"; + continue; + } + + std::thread clientThread(&TCPServer::HandleClient, this, clientSocket); + clientThreads.push_back(std::move(clientThread)); + } +} + +void TCPServer::HandleClient(int clientSocket) { + // Handle client communication here + // For demonstration, just echo received messages back to the client + char buffer[1024]; + int bytesRead; + while ((bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { + send(clientSocket, buffer, bytesRead, 0); + } + + close(clientSocket); +} +```` +## 2nd question +_great, how about a main.cpp for this?_ +## 2nd reply (1st of 2) +_Certainly! Here's a simple main.cpp file that demonstrates how to create an instance of the TCPServer class and start the server:_ +````cpp +#include "server.h" + +int main() { + // Port number to listen on + int port = 8080; + + // Create an instance of the server + TCPServer server(port); + + // Start the server + server.Start(); + + // Wait for user input to stop the server + std::cout << "Server is running. Press any key to stop.\n"; + std::cin.get(); + + // Stop the server + server.Stop(); + + std::cout << "Server stopped.\n"; + return 0; +} +```` + diff --git a/ChatGPT/main.cpp b/ChatGPT/main.cpp new file mode 100644 index 0000000..e69de29 diff --git a/ChatGPT/server.cpp b/ChatGPT/server.cpp new file mode 100644 index 0000000..fd615fc --- /dev/null +++ b/ChatGPT/server.cpp @@ -0,0 +1,71 @@ +#include "server.h" + +TCPServer::TCPServer(int port) : port(port), running(false) {} + +TCPServer::~TCPServer() { + Stop(); +} + +void TCPServer::Start() { + running = true; + + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == -1) { + std::cerr << "Error creating socket\n"; + return; + } + + sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = INADDR_ANY; + serverAddr.sin_port = htons(port); + + if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { + std::cerr << "Bind failed\n"; + return; + } + + if (listen(serverSocket, 5) < 0) { + std::cerr << "Listen failed\n"; + return; + } + + std::thread listenerThread(&TCPServer::Listen, this); + listenerThread.detach(); +} + +void TCPServer::Stop() { + running = false; + close(serverSocket); + + for (auto& thread : clientThreads) { + thread.join(); + } +} + +void TCPServer::Listen() { + while (running) { + sockaddr_in clientAddr; + socklen_t clientAddrLen = sizeof(clientAddr); + int clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientAddrLen); + if (clientSocket < 0) { + std::cerr << "Accept failed\n"; + continue; + } + + std::thread clientThread(&TCPServer::HandleClient, this, clientSocket); + clientThreads.push_back(std::move(clientThread)); + } +} + +void TCPServer::HandleClient(int clientSocket) { + // Handle client communication here + // For demonstration, just echo received messages back to the client + char buffer[1024]; + int bytesRead; + while ((bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { + send(clientSocket, buffer, bytesRead, 0); + } + + close(clientSocket); +} diff --git a/ChatGPT/server.h b/ChatGPT/server.h new file mode 100644 index 0000000..ce5e96d --- /dev/null +++ b/ChatGPT/server.h @@ -0,0 +1,33 @@ +#ifndef SERVER_H +#define SERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TCPServer { +public: + TCPServer(int port); + ~TCPServer(); + + void Start(); + void Stop(); + +private: + void Listen(); + void HandleClient(int clientSocket); + + int port; + int serverSocket; + std::vector clientThreads; + std::atomic running; +}; + +#endif // SERVER_H From 714130a9cfc5f5a3fca404c7980236a519f66a35 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Thu, 4 Apr 2024 23:46:29 +0200 Subject: [PATCH 43/56] just for the sake of it: ask ChatGPT --- ...{ChatGPT_dialog_1.md => ChatGPT_dialog.md} | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) rename ChatGPT/{ChatGPT_dialog_1.md => ChatGPT_dialog.md} (53%) diff --git a/ChatGPT/ChatGPT_dialog_1.md b/ChatGPT/ChatGPT_dialog.md similarity index 53% rename from ChatGPT/ChatGPT_dialog_1.md rename to ChatGPT/ChatGPT_dialog.md index 151cba2..14ce4a8 100644 --- a/ChatGPT/ChatGPT_dialog_1.md +++ b/ChatGPT/ChatGPT_dialog.md @@ -143,4 +143,125 @@ int main() { return 0; } ```` +## 3rd question +_please get me this for Winsock_ + +## 3rd answer +Sure! Here's how you can modify the server code to work with Winsock on Windows: + +**Server Header (server.h):** +````cpp +#ifndef SERVER_H +#define SERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class TCPServer { +public: + TCPServer(int port); + ~TCPServer(); + + void Start(); + void Stop(); + +private: + void Listen(); + void HandleClient(SOCKET clientSocket); + + int port; + SOCKET serverSocket; + std::vector clientThreads; + std::atomic running; +}; + +#endif // SERVER_H +```` +**Server Source (server.cpp):** +````cpp +#include "server.h" + +TCPServer::TCPServer(int port) : port(port), running(false) { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "WSAStartup failed\n"; + } +} + +TCPServer::~TCPServer() { + Stop(); + WSACleanup(); +} + +void TCPServer::Start() { + running = true; + + serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverSocket == INVALID_SOCKET) { + std::cerr << "Error creating socket\n"; + return; + } + + sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = INADDR_ANY; + serverAddr.sin_port = htons(port); + + if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { + std::cerr << "Bind failed\n"; + return; + } + + if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) { + std::cerr << "Listen failed\n"; + return; + } + + std::thread listenerThread(&TCPServer::Listen, this); + listenerThread.detach(); +} + +void TCPServer::Stop() { + running = false; + closesocket(serverSocket); + + for (auto& thread : clientThreads) { + thread.join(); + } +} + +void TCPServer::Listen() { + while (running) { + sockaddr_in clientAddr; + int clientAddrLen = sizeof(clientAddr); + SOCKET clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientAddrLen); + if (clientSocket == INVALID_SOCKET) { + std::cerr << "Accept failed\n"; + continue; + } + + std::thread clientThread(&TCPServer::HandleClient, this, clientSocket); + clientThreads.push_back(std::move(clientThread)); + } +} + +void TCPServer::HandleClient(SOCKET clientSocket) { + // Handle client communication here + // For demonstration, just echo received messages back to the client + char buffer[1024]; + int bytesRead; + while ((bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { + send(clientSocket, buffer, bytesRead, 0); + } + + closesocket(clientSocket); +} +```` +The main.cpp file remains the same. Make sure to compile the server code on Windows using a compatible compiler (e.g., Visual Studio) with the appropriate linking flags for the Winsock library (-lws2_32). From 1ad695454c468c02d1a0d619f794f898a01f73f9 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:52:05 +0200 Subject: [PATCH 44/56] review comments rework, correction select --- .gitignore | 4 +- ChatGPT/EchoSrv.make | 130 ++++++++++++++++ ChatGPT/Makefile | 44 ++++++ ChatGPT/main.cpp | 22 +++ ChatGPT/premake5.lua | 12 ++ basics.md | 9 +- code/TCP_Srv_Base.cpp | 181 +++++++++-------------- code/TCP_Srv_Base.h | 19 +-- code/TCP_Srv_Echo.cpp | 37 +++-- code/TCP_Srv_Echo.h | 5 +- code/TCP_Srv_Echo_main.cpp | 10 +- code/Trace.cpp | 5 +- code/Trace.h | 63 +++++--- make/{TCP_Srv_Echo.make => EchoSrv.make} | 12 +- make/Makefile | 20 +-- make/build.sh | 35 +++-- make/howto_build.md | 51 ++++++- make/premake5.lua | 5 +- py/echoCommon.py | 4 +- 19 files changed, 465 insertions(+), 203 deletions(-) create mode 100644 ChatGPT/EchoSrv.make create mode 100644 ChatGPT/Makefile create mode 100644 ChatGPT/premake5.lua rename make/{TCP_Srv_Echo.make => EchoSrv.make} (95%) diff --git a/.gitignore b/.gitignore index ec15cce..d3f5b5e 100644 --- a/.gitignore +++ b/.gitignore @@ -44,11 +44,9 @@ exe *.vcxproj.* *.vcxproj *.sln -*.make +# *.make # general tmp.* tmp_.* -# sample -!TCP_Srv*.make \ No newline at end of file diff --git a/ChatGPT/EchoSrv.make b/ChatGPT/EchoSrv.make new file mode 100644 index 0000000..cb03b0a --- /dev/null +++ b/ChatGPT/EchoSrv.make @@ -0,0 +1,130 @@ +# Alternative GNU Make project makefile autogenerated by Premake + +ifndef config + config=chatgpt +endif + +ifndef verbose + SILENT = @ +endif + +.PHONY: clean prebuild + +SHELLTYPE := posix +ifeq ($(shell echo "test"), "test") + SHELLTYPE := msdos +endif + +# Configurations +# ############################################# + +RESCOMP = windres +TARGETDIR = bin +TARGET = $(TARGETDIR)/EchoSrv +OBJDIR = obj/gcc +DEFINES += +INCLUDES += +FORCE_INCLUDE += +ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall +ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) +LIBS += +LDDEPS += +ALL_LDFLAGS += $(LDFLAGS) -s -pthread +LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) +define PREBUILDCMDS +endef +define PRELINKCMDS +endef +define POSTBUILDCMDS +endef + +# Per File Configurations +# ############################################# + + +# File sets +# ############################################# + +GENERATED := +OBJECTS := + +GENERATED += $(OBJDIR)/main.o +GENERATED += $(OBJDIR)/server.o +OBJECTS += $(OBJDIR)/main.o +OBJECTS += $(OBJDIR)/server.o + +# Rules +# ############################################# + +all: $(TARGET) + @: + +$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) + $(PRELINKCMDS) + @echo Linking EchoSrv + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif + +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + +clean: + @echo Cleaning EchoSrv +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(GENERATED) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif + +prebuild: | $(OBJDIR) + $(PREBUILDCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) +$(GCH): $(PCH) | prebuild + @echo $(notdir $<) + $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) touch "$@" +else + $(SILENT) echo $null >> "$@" +endif +else +$(OBJECTS): | prebuild +endif + + +# File Rules +# ############################################# + +$(OBJDIR)/main.o: main.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/server.o: server.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(PCH_PLACEHOLDER).d +endif \ No newline at end of file diff --git a/ChatGPT/Makefile b/ChatGPT/Makefile new file mode 100644 index 0000000..1ce31b2 --- /dev/null +++ b/ChatGPT/Makefile @@ -0,0 +1,44 @@ +# Alternative GNU Make workspace makefile autogenerated by Premake + +ifndef config + config=chatgpt +endif + +ifndef verbose + SILENT = @ +endif + +ifeq ($(config),chatgpt) + EchoSrv_config = chatgpt + +else + $(error "invalid configuration $(config)") +endif + +PROJECTS := EchoSrv + +.PHONY: all clean help $(PROJECTS) + +all: $(PROJECTS) + +EchoSrv: +ifneq (,$(EchoSrv_config)) + @echo "==== Building EchoSrv ($(EchoSrv_config)) ====" + @${MAKE} --no-print-directory -C . -f EchoSrv.make config=$(EchoSrv_config) +endif + +clean: + @${MAKE} --no-print-directory -C . -f EchoSrv.make clean + +help: + @echo "Usage: make [config=name] [target]" + @echo "" + @echo "CONFIGURATIONS:" + @echo " chatgpt" + @echo "" + @echo "TARGETS:" + @echo " all (default)" + @echo " clean" + @echo " EchoSrv" + @echo "" + @echo "For more information, see https://github.com/premake/premake-core/wiki" \ No newline at end of file diff --git a/ChatGPT/main.cpp b/ChatGPT/main.cpp index e69de29..8b289af 100644 --- a/ChatGPT/main.cpp +++ b/ChatGPT/main.cpp @@ -0,0 +1,22 @@ +#include "server.h" + +int main() { + // Port number to listen on + int port = 8080; + + // Create an instance of the server + TCPServer server(port); + + // Start the server + server.Start(); + + // Wait for user input to stop the server + std::cout << "Server is running. Press any key to stop.\n"; + std::cin.get(); + + // Stop the server + server.Stop(); + + std::cout << "Server stopped.\n"; + return 0; +} diff --git a/ChatGPT/premake5.lua b/ChatGPT/premake5.lua new file mode 100644 index 0000000..03c0710 --- /dev/null +++ b/ChatGPT/premake5.lua @@ -0,0 +1,12 @@ +workspace 'EchoSrv_ChatGPT' + filter { 'action:gmake*' } + configurations { 'ChatGPT' } + language 'C++' + + project 'EchoSrv' + kind 'ConsoleApp' + files { '*.cpp' } + buildoptions { '-std=c++17 -pedantic-errors -Werror -Wall' } + objdir 'obj/gcc/' + linkoptions { '-pthread' } + targetdir 'bin' diff --git a/basics.md b/basics.md index aac405a..0170dfb 100644 --- a/basics.md +++ b/basics.md @@ -58,9 +58,12 @@ ________| | FD_ISSET() | | | | N Y -|______| | -| accept() -> clientSocket, thread(clientSocket) -|___________| +| | | +| | accept() -> clientSocket, thread(clientSocket) +| |_____| +| | +| other_tasks() +|_________| ``` ## threads diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index eb5ce63..d8b7131 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -2,8 +2,6 @@ #include "Trace.h" #ifdef _WIN32 -// timevel can be used const in VS -using tval = const timeval; // required lib for Winsock #pragma comment(lib, "ws2_32") #else @@ -12,8 +10,6 @@ using tval = const timeval; #include #define closesocket close constexpr SOCKET INVALID_SOCKET = -1; -// timevel must be non const gcc -using tval = timeval; #endif #include @@ -30,49 +26,33 @@ using std::regex, std::regex_match; #include using std::filesystem::path; +using mutexlock = std::unique_lock; + void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { if (argc > 1) { - bool cont = true; - // check for help: exit if called + UINT16 port = defPort; + regex rxPort {"^\\d{2,5}$"}; regex rxHelp {"^-[hH]$"}; - for (INT32 n = 1; n < argc; ++n) + bool cont = true; + for (INT32 n = 1; cont and n < argc; ++n) { if (regex_match(argv[n], rxHelp)) { help(path(argv[0]).filename().string()); cont = false; - break; } } // no help call in args - check for port or other args - if (cont) + for (INT32 n = 1; cont and n < argc; ++n) { - UINT16 port = defPort; - regex rxPort {"^\\d{2,5}$"}; - for (INT32 n = 1; n < argc; ++n) - { - if (regex_match(argv[n], rxPort)) - { - port = static_cast(atoi(argv[n])); - } - else if (not handlearg(argv[n])) - { - cont = false; - break; - } - } - if (cont) - { - run(port); - } + if (regex_match(argv[n], rxPort)) port = static_cast(atoi(argv[n])); + else cont = handlearg(argv[n]); } + if (cont) run(port); } - else - { - run(); - } + else run(); } void TCP_Srv_Base::run(const UINT16 port) @@ -83,7 +63,7 @@ void TCP_Srv_Base::run(const UINT16 port) << "buffer :" << setw(6) << buffSize << " bytes" << endl; // indicator for continuation - bool cont = true; + bool ok = true; // listen socket SOCKET listenSocket = INVALID_SOCKET; @@ -94,22 +74,22 @@ void TCP_Srv_Base::run(const UINT16 port) if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { cerr << "ERR WSAStartup" << endl; - cont = false; + ok = false; } } #endif // create socket - if (cont) + if (ok) { listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket < 0) { cerr << "ERR socket" << endl; - cont = false; + ok = false; } } // bind socket to port - if (cont) + if (ok) { sockaddr_in addr; addr.sin_family = AF_INET; @@ -119,52 +99,62 @@ void TCP_Srv_Base::run(const UINT16 port) if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { cerr << "ERR bind: port " << port << endl; - cont = false; + ok = false; } } // listen to socket - if (cont and (listen(listenSocket, SOMAXCONN) < 0)) + if (ok and (listen(listenSocket, SOMAXCONN) < 0)) { cerr << "ERR listen" << endl; - cont = false; + ok = false; } // display port if successful sofar - if (cont) - { - cout << "port :" << setw(6) << port << endl; - } + if (ok) cout << "port :" << setw(6) << port << endl; - // select and accept loop - while (cont) + // select and other tasks loop + while (ok) { - // select - fd_set lset; - FD_ZERO(&lset); - FD_SET(listenSocket, &lset); - tval tv { tmSec, tmMic }; - if (select(0, &lset, nullptr, nullptr, &tv) < 0) - { - cerr << "ERR listen select" << endl; - cont = false; - } - - // accept to new client socket if listen socket is set - else if (FD_ISSET(listenSocket, &lset)) + // select and accept loop + bool clients = false; + do { - SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); - if (clientSocket < 0) + clients = false; + // select + fd_set lset; + FD_ZERO(&lset); + FD_SET(listenSocket, &lset); + timeval tv { tmSec, tmMic}; + + if (select(listenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) { - cerr << "ERR accept" << endl; - cont = false; + cerr << "ERR listen select" << endl; + ok = false; } - // start thread with client socket - else + + + // accept to new client socket if listen socket is set + else if (FD_ISSET(listenSocket, &lset)) { - startThread(clientSocket); + SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); + + if (clientSocket < 0) + { + cerr << "ERR accept" << endl; + ok = false; + } + // start thread with client socket + else + { + startThread(clientSocket); + // clients = true; + } } - } + } while (clients); + + // other tasks + other_tasks(); } // only reached in case of error: clean up if (listenSocket >= 0) @@ -176,54 +166,19 @@ void TCP_Srv_Base::run(const UINT16 port) #endif } -void TCP_Srv_Base::tm(SOCKET clientSocket, const UINT32 nr) +void TCP_Srv_Base::tm(const SOCKET clientSocket, const UINT32 nr) { - { - TraceLock(nr) << "CON" << endl; - } - - // indicator for continuation - bool cont = true; + // brackets required for scope of TraceLock object + { TraceLock(nr) << "CON" << endl; } - // select and receive loop - while(cont) + Buffer buff; + size_t size = 0; + while ((size = recv(clientSocket, buff, sizeof(Buffer), 0)) > 0) { - // select - fd_set cset; - FD_ZERO(&cset); - FD_SET(clientSocket, &cset); - tval tv { tmSec, tmMic }; - - if (select(0, &cset, nullptr, nullptr, &tv) < 0) - { - TraceLock(nr) << "ERR select" << endl; - cont = false; - } - // receive from client socket when select indicates read possible - else if (FD_ISSET(clientSocket, &cset)) - { - Buffer buff; - size_t size = recv(clientSocket, buff, sizeof(Buffer), 0); - - // if 1st recv delivers bytes client ist sending - if (size > 0) - { - // continue recv until no more bytes are sent - do { - TraceLock(nr) << "<- " << size << endl; - process(clientSocket, buff, size, nr); - size = recv(clientSocket, buff, sizeof(Buffer), 0); - } while (size > 0); - } - - // otherwise client has closed connection - else - { - TraceLock(nr) << "EX" << endl; - cont = false; - } - } + { TraceLock(nr) << "<- " << size << endl; } + process(clientSocket, buff, size, nr); } + { TraceLock(nr) << (size == 0 ? "EX" : "ERR read") << endl; } closesocket(clientSocket); endOfThread(); } @@ -254,7 +209,7 @@ void TCP_Srv_Base::endOfThread() void TCP_Srv_Base::displayThreads() const { - if constexpr (not Trace::verbose) + if constexpr (not Trace::isOn) { cout << "threads:" << setw(6) << mCnt << '\r' << flush; } @@ -265,9 +220,9 @@ void TCP_Srv_Base::help(const std::string&& argv0) const cout << endl << "usage : " << argv0 << " [-h] [port]"; - addusage(cout); + addusage(); cout << endl << "-h : this help" << endl << "port : 2-5 digits, default: " << defPort << endl; - addhelp(cout); + addhelp(); } diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index ef3af7f..df38f0c 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -38,7 +38,7 @@ class TCP_Srv_Base inline TCP_Srv_Base() = default; // run with given port void run(UINT16 port = defPort); - // run with port from 1st CLI argument + // run with CLI arguments void run(INT32 argc, const CONST_C_STRING* argv); protected: @@ -54,27 +54,28 @@ class TCP_Srv_Base // must be implemented by derived class virtual void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) = 0; - // handle unmatched argument + // handle unmatched CLI argument // can be implemented by derived class inline virtual bool handlearg(CONST_C_STRING) { return true; } - // add usage item to help + // add usage item to std::out // can be implemented by derived class - virtual void addusage(std::ostream&) const {} + inline virtual void addusage() const {} - // add help item to help + // add help item to std::out // can be implemented by derived class - virtual void addhelp(std::ostream&) const {} + inline virtual void addhelp() const {} - using mutexlock = std::unique_lock; + // other tasks to be done in main loop + // can be implemented by derived class + inline virtual void other_tasks() {} +private: // default port constexpr static UINT16 defPort = 8080; - -private: // thread method void tm(SOCKET clientSocket, UINT32 nr); // thread count diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index bd4933f..d08f972 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -6,40 +6,40 @@ #endif #include -using std::cout, std::cerr, std::endl; -#include +using std::cerr, std::endl; #include bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) { bool ok = true; - if (std::setlocale(LC_ALL, argv) == nullptr) + // locale only required if trace is on + if constexpr (Trace::isOn) { - cerr << "ERR setlocale: " << argv << endl; - ok = false; - } - else - { - Trace() << "locale : " << argv << endl; + if (std::setlocale(LC_ALL, argv) == nullptr) + { + cerr << "ERR setlocale: " << argv << endl; + ok = false; + } + else Trace() << "locale : " << argv << endl; } return ok; } -void TCP_Srv_Echo::addusage(std::ostream& os) const +void TCP_Srv_Echo::addusage() const { - os << " [locale]"; + Trace() << " [locale]"; } -void TCP_Srv_Echo::addhelp(std::ostream& os) const +void TCP_Srv_Echo::addhelp() const { - os << "locale: see setlocale" << endl; + Trace() << "locale: see setlocale" << endl; } void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t size, const UINT32 nr) { send(clientSocket, buff, size, 0); - if constexpr (Trace::verbose) + if constexpr (Trace::isOn) { TraceLock tr(nr); tr << "<- "; @@ -50,3 +50,12 @@ void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t tr << " ->" << endl; } } + +#include +#include +#include +void TCP_Srv_Echo::other_tasks() +{ + { TraceLock() << " other tasks: " << (std::time(nullptr) % 500) << endl; } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} \ No newline at end of file diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index cd16357..a4a5352 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -25,9 +25,10 @@ class TCP_Srv_Echo : public TCP_Srv_Base // but with msvc windows bool handlearg(CONST_C_STRING argv) final; - void addusage(std::ostream& os) const final; - void addhelp(std::ostream& os) const final; + void addusage() const final; + void addhelp() const final; + void other_tasks() final; }; #endif // _H diff --git a/code/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp index 2ea21b0..c03cab3 100644 --- a/code/TCP_Srv_Echo_main.cpp +++ b/code/TCP_Srv_Echo_main.cpp @@ -1,8 +1,14 @@ +// ============================================================ +// sample TCP echo server class main +// ============================================================ +// created by Manfred Sorgo + #include "TCP_Srv_Echo.h" int main(const INT32 argc, const CONST_C_STRING* const argv) { TCP_Srv_Echo srv; srv.run(argc, argv); - return 0; -} \ No newline at end of file + // only reached in case of failure + return 1; +} diff --git a/code/Trace.cpp b/code/Trace.cpp index 648e0e8..4b4311a 100644 --- a/code/Trace.cpp +++ b/code/Trace.cpp @@ -1,6 +1,7 @@ -#ifdef VERBOSE #include "Trace.h" +#ifdef VERBOSE std::mutex TraceLock::mMutex; - +#else +NullStream Trace::mNull; #endif // VERBOSE \ No newline at end of file diff --git a/code/Trace.h b/code/Trace.h index 9241922..a45e107 100644 --- a/code/Trace.h +++ b/code/Trace.h @@ -15,53 +15,78 @@ #include #include -// class Trace blocks (std::cout) output within object scope if VERBOSE is not defined -// if VERBOSE is defined, Trace behaves like std::cout +#include + +// NullBuffer and NullStream are used to prevent from output +// thanks to ChatGPT for this +class NullBuffer : public std::streambuf +{ +public: + inline int overflow(int c) override + { + return c; // Do nothing with the character + } +}; + +class NullStream : public std::ostream +{ +public: + inline NullStream() : std::ostream(&buffer) {} + +private: + NullBuffer buffer; +}; + +// class Trace acts as std::cout if VERBOSE is defined +// otherwise as NullStream with no output class Trace { public: // avoid macro usage // use: -// constexpr if (Trace::verbose) { ... } -// constexpr if (not Trace::verbose) { ... } +// constexpr if (Trace::isOn) { ... } +// constexpr if (not Trace::isOn) { ... } // to eliminate code from compilation // in opposite to macros there won't be compiler warnings of unused variables #ifdef VERBOSE - constexpr static bool verbose = true; + constexpr static bool isOn = true; #else - constexpr static bool verbose = false; + constexpr static bool isOn = false; #endif - Trace() - { - if constexpr (not verbose) - { - std::cout.setstate(std::ios_base::badbit); - } - } - ~Trace() + inline Trace() = default; + +#ifdef VERBOSE + template + inline std::ostream& operator<<(const T& t) { - if constexpr (not verbose) - { - std::cout.clear(); - } + return std::cout << t; } +#else template inline std::ostream& operator<<(const T& t) { - return std::cout << t; + return mNull << t; } +private: + static NullStream mNull; +#endif }; // class TraceLock behaves like class Trace // in addition, if VERBOSE is defined // parallel output (of other TraceLock objects) // is blocked by a mutex lock within object scope +// use brackets to limit scope of TraceLock object +// to avoid deadlocks +// e.g. +// { TraceLock() << "hello world" << std::endl; } class TraceLock : public Trace { public: #ifdef VERBOSE TraceLock() : Trace(), mLock(mMutex) {} + // start trace with number TraceLock(const UINT32 nr) : TraceLock() { *this << std::setw(3) << nr << ' '; diff --git a/make/TCP_Srv_Echo.make b/make/EchoSrv.make similarity index 95% rename from make/TCP_Srv_Echo.make rename to make/EchoSrv.make index c285896..ee85172 100644 --- a/make/TCP_Srv_Echo.make +++ b/make/EchoSrv.make @@ -19,8 +19,6 @@ endif # ############################################# RESCOMP = windres -TARGETDIR = bin -TARGET = $(TARGETDIR)/TCP_Srv_Echo INCLUDES += FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) @@ -36,6 +34,8 @@ define POSTBUILDCMDS endef ifeq ($(config),prod) +TARGETDIR = bin +TARGET = $(TARGETDIR)/EchoSrv_prod OBJDIR = obj/gcc/prod DEFINES += -DNDEBUG ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall @@ -43,6 +43,8 @@ ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Wer ALL_LDFLAGS += $(LDFLAGS) -s -pthread else ifeq ($(config),verbose) +TARGETDIR = bin +TARGET = $(TARGETDIR)/EchoSrv_verbose OBJDIR = obj/gcc/verbose DEFINES += -DDEBUG -DVERBOSE ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall @@ -50,6 +52,8 @@ ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors - ALL_LDFLAGS += $(LDFLAGS) -pthread else ifeq ($(config),vsmall) +TARGETDIR = bin +TARGET = $(TARGETDIR)/EchoSrv_vsmall OBJDIR = obj/gcc/vsmall DEFINES += -DDEBUG -DVERBOSE -DSELECT_MILLI_SECONDS=1000 -DREAD_BUFFER_SIZE=8 ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall @@ -85,7 +89,7 @@ all: $(TARGET) $(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) $(PRELINKCMDS) - @echo Linking TCP_Srv_Echo + @echo Linking EchoSrv $(SILENT) $(LINKCMD) $(POSTBUILDCMDS) @@ -106,7 +110,7 @@ else endif clean: - @echo Cleaning TCP_Srv_Echo + @echo Cleaning EchoSrv ifeq (posix,$(SHELLTYPE)) $(SILENT) rm -f $(TARGET) $(SILENT) rm -rf $(GENERATED) diff --git a/make/Makefile b/make/Makefile index 82c3200..18819f0 100644 --- a/make/Makefile +++ b/make/Makefile @@ -9,32 +9,32 @@ ifndef verbose endif ifeq ($(config),prod) - TCP_Srv_Echo_config = prod + EchoSrv_config = prod else ifeq ($(config),verbose) - TCP_Srv_Echo_config = verbose + EchoSrv_config = verbose else ifeq ($(config),vsmall) - TCP_Srv_Echo_config = vsmall + EchoSrv_config = vsmall else $(error "invalid configuration $(config)") endif -PROJECTS := TCP_Srv_Echo +PROJECTS := EchoSrv .PHONY: all clean help $(PROJECTS) all: $(PROJECTS) -TCP_Srv_Echo: -ifneq (,$(TCP_Srv_Echo_config)) - @echo "==== Building TCP_Srv_Echo ($(TCP_Srv_Echo_config)) ====" - @${MAKE} --no-print-directory -C . -f TCP_Srv_Echo.make config=$(TCP_Srv_Echo_config) +EchoSrv: +ifneq (,$(EchoSrv_config)) + @echo "==== Building EchoSrv ($(EchoSrv_config)) ====" + @${MAKE} --no-print-directory -C . -f EchoSrv.make config=$(EchoSrv_config) endif clean: - @${MAKE} --no-print-directory -C . -f TCP_Srv_Echo.make clean + @${MAKE} --no-print-directory -C . -f EchoSrv.make clean help: @echo "Usage: make [config=name] [target]" @@ -47,6 +47,6 @@ help: @echo "TARGETS:" @echo " all (default)" @echo " clean" - @echo " TCP_Srv_Echo" + @echo " EchoSrv" @echo "" @echo "For more information, see https://github.com/premake/premake-core/wiki" \ No newline at end of file diff --git a/make/build.sh b/make/build.sh index 2f53079..ec5a137 100644 --- a/make/build.sh +++ b/make/build.sh @@ -2,25 +2,29 @@ help() { - echo "Usage: $(basename $0) options [port]" + echo "Usage: $(basename $0) options [port] [locale]" echo "options:" - echo "-p config; prod" - echo "-v config: verbose" - echo "-s config: vsmall" - echo "" - echo "-r run binary" - echo "-h this help" + echo "configurations (select one):" + echo " -p config; prod" + echo " -v config: verbose" + echo " -s config: vsmall" + echo "behaviour:" + echo " -c clean all untracked artifacts" + echo " -r run binary with [port] [locale]" + echo " -h this help" exit } conf= run= port= -while getopts hkrspv option; do +clean= +while getopts pvscrh option; do case $option in - (v) conf=verbose;; (p) conf=prod;; + (v) conf=verbose;; (s) conf=vsmall;; + (c) clean=1;; (r) run=1;; (h) help;; esac @@ -32,16 +36,23 @@ if test -z $conf; then help; fi cd $(dirname $0) -make config=$conf clean +if test -v $clean; then git clean -dfx . 2>/dev/null >/dev/null; fi + +make config=$conf clean >/dev/null make -j config=$conf -if test $? -ne 0; then exit 1; fi +if test $? -ne 0; then exit $?; fi -bin=bin/TCP_Srv_Echo +bin=bin/EchoSrv_$conf + +if test ! -f $bin; then exit 1; fi if test -z $run; then echo "-> $bin" else + echo "" + echo "starting $bin $*" + echo "" $bin $* fi diff --git a/make/howto_build.md b/make/howto_build.md index 07c7280..d1f84c3 100644 --- a/make/howto_build.md +++ b/make/howto_build.md @@ -9,7 +9,7 @@ - call make -j with a configuration ````shell -$> make help +TCP_Srv/make$> make help Usage: make [config=name] [target] CONFIGURATIONS: @@ -21,10 +21,10 @@ TARGETS: all (default) clean -$> make config=vsmall clean +TCP_Srv/make$> make config=vsmall clean Cleaning TCP_Srv_Echo -$> make -j config=vsmall +TCP_Srv/make$> make -j config=vsmall ==== Building TCP_Srv_Echo (vsmall) ==== Creating obj/gcc/vsmall Creating bin @@ -33,7 +33,44 @@ TCP_Srv_Echo.cpp TCP_Srv_Echo_main.cpp Linking TCP_Srv_Echo ```` +### use build.sh +call help +````shell +TCP_Srv/make$> ./build.sh -h +Usage: build.sh options [port] [locale] +options: +configurations (select one): + -p config; prod + -v config: verbose + -s config: vsmall +behaviour: + -c clean all untracked artifacts + -r run binary with [port] [locale] + -h this help +```` +build (and run) e.g.: +- configuration _verbose_ +- port 8090 +- locale de_DE.UTF-8 +````shell +TCP_Srv/make$> ./build.sh -vr 8090 de_DE.UTF-8 +==== Building TCP_Srv_Echo (verbose) ==== +Creating obj/gcc/verbose +Creating bin +TCP_Srv_Base.cpp +TCP_Srv_Echo.cpp +TCP_Srv_Echo_main.cpp +Trace.cpp +Linking TCP_Srv_Echo +starting bin/EchoSrv_verbose 8090 de_DE.UTF-8 + +locale : de_DE.UTF-8 +... +timeout: 10 ms +buffer : 1024 bytes +port : 8090 +```` ## Visual Studio build ### preconditions @@ -64,12 +101,12 @@ e.g. VS 2019 TCP_Srv/make$> premake5 vs2019 Building configurations... Running action 'vs2019'... -Generated TCP_Srv_Echo.sln... -Generated TCP_Srv_Echo.vcxproj... -Done (41ms).. +Generated EchoSrv.sln... +Generated EchoSrv.vcxproj... +Done (42ms). ```` ### 2) build executables in VS -- open solution (TCP_Srv/make/TCP_Srv_Echo.sln) +- open solution (TCP_Srv/make/EchoSrv.sln) - select a configuration (see also header of premake5.lua) - build (and run) diff --git a/make/premake5.lua b/make/premake5.lua index 910a7af..cda9ea0 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -17,11 +17,12 @@ -- ============================================================ -- created by Manfred Sorgo -workspace 'TCP_Srv_Echo' +workspace 'EchoSrv' configurations { 'prod', 'verbose', 'vsmall' } language 'C++' + targetsuffix '_%{cfg.name}' - project 'TCP_Srv_Echo' + project 'EchoSrv' kind 'ConsoleApp' files { '../code/*.cpp' } diff --git a/py/echoCommon.py b/py/echoCommon.py index 7c4418d..f64810c 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -55,13 +55,15 @@ def __init__(self, *args): logconfig(format='%(asctime)s %(message)s', level=INFO, datefmt='%H:%M:%S') if verbose: - self.tell = self.log + self.tell = self.log + self.whisper = self.silent print() print('addr:', self.addr) print('prot:', 'ipv6' if self.prot == socket.AF_INET6 else 'ipv4') print('port:', self.port) else: self.tell = self.silent + self.whisper = self.log def getsocket(self) -> socket.socket: return socket.socket(self.prot, socket.SOCK_STREAM) From 0e152bdf2a3b087aad9750fa6ab2fc8c178e9b4c Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sat, 6 Apr 2024 20:35:08 +0200 Subject: [PATCH 45/56] build, Trace --- .github/workflows/c-cpp.yml | 8 +--- code/TCP_Srv_Base.cpp | 52 +++++++++++++---------- code/TCP_Srv_Base.h | 20 +++++---- code/TCP_Srv_Echo.cpp | 32 ++++++++++---- code/TCP_Srv_Echo.h | 16 ++++--- code/TCP_Srv_Echo_main.cpp | 10 ++++- code/Trace.cpp | 2 - code/Trace.h | 38 +++++------------ make/build.sh | 83 ++++++++++++++++++++++++------------- 9 files changed, 150 insertions(+), 111 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 93785aa..f6f693a 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -20,9 +20,5 @@ jobs: uses: actions/checkout@v3 - name: prepare build run: chmod 755 make/build.sh - - name: build config "prod" - run: make/build.sh -p - - name: build config "verbose" - run: make/build.sh -v - - name: build config "vsmall" - run: make/build.sh -s + - name: build + run: make/build.sh -c diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index d8b7131..2f40124 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -9,7 +9,6 @@ #include #include #define closesocket close -constexpr SOCKET INVALID_SOCKET = -1; #endif #include @@ -48,7 +47,7 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) for (INT32 n = 1; cont and n < argc; ++n) { if (regex_match(argv[n], rxPort)) port = static_cast(atoi(argv[n])); - else cont = handlearg(argv[n]); + else cont = handleArg(argv[n]); } if (cont) run(port); } @@ -64,9 +63,7 @@ void TCP_Srv_Base::run(const UINT16 port) // indicator for continuation bool ok = true; - // listen socket - SOCKET listenSocket = INVALID_SOCKET; - + #ifdef _WIN32 // check for Windows Sockets version 2.2 { @@ -81,8 +78,8 @@ void TCP_Srv_Base::run(const UINT16 port) // create socket if (ok) { - listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listenSocket < 0) + mListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mListenSocket < 0) { cerr << "ERR socket" << endl; ok = false; @@ -96,22 +93,26 @@ void TCP_Srv_Base::run(const UINT16 port) addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); - if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) + if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { cerr << "ERR bind: port " << port << endl; ok = false; } } // listen to socket - if (ok and (listen(listenSocket, SOMAXCONN) < 0)) + if (ok and (listen(mListenSocket, SOMAXCONN) < 0)) { cerr << "ERR listen" << endl; ok = false; } // display port if successful sofar - if (ok) cout << "port :" << setw(6) << port << endl; - + if (ok) + { + cout << "port :" << setw(6) << port << endl; + displayThreads(); + } + // select and other tasks loop while (ok) { @@ -124,10 +125,10 @@ void TCP_Srv_Base::run(const UINT16 port) // select fd_set lset; FD_ZERO(&lset); - FD_SET(listenSocket, &lset); + FD_SET(mListenSocket, &lset); timeval tv { tmSec, tmMic}; - if (select(listenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) + if (select(mListenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) { cerr << "ERR listen select" << endl; ok = false; @@ -135,9 +136,9 @@ void TCP_Srv_Base::run(const UINT16 port) // accept to new client socket if listen socket is set - else if (FD_ISSET(listenSocket, &lset)) + else if (FD_ISSET(mListenSocket, &lset)) { - SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); + SOCKET clientSocket = accept(mListenSocket, nullptr, nullptr); if (clientSocket < 0) { @@ -148,25 +149,30 @@ void TCP_Srv_Base::run(const UINT16 port) else { startThread(clientSocket); - // clients = true; + clients = true; } } } while (clients); // other tasks - other_tasks(); + otherTasks(); } // only reached in case of error: clean up - if (listenSocket >= 0) +} + +void TCP_Srv_Base::cleanup() +{ + Trace() << endl << "cleanup" << endl; + if (mListenSocket >= 0) { - closesocket(listenSocket); + closesocket(mListenSocket); } #ifdef _WIN32 WSACleanup(); #endif } -void TCP_Srv_Base::tm(const SOCKET clientSocket, const UINT32 nr) +void TCP_Srv_Base::handleClient(const SOCKET clientSocket, const UINT32 nr) { // brackets required for scope of TraceLock object { TraceLock(nr) << "CON" << endl; } @@ -188,7 +194,7 @@ void TCP_Srv_Base::startThread(SOCKET clientSocket) mutexlock lock(mMtxStat); ++mCnt; ++mNum; - std::thread(&TCP_Srv_Base::tm, this, clientSocket, mNum).detach(); + std::thread(&TCP_Srv_Base::handleClient, this, clientSocket, mNum).detach(); displayThreads(); } @@ -220,9 +226,9 @@ void TCP_Srv_Base::help(const std::string&& argv0) const cout << endl << "usage : " << argv0 << " [-h] [port]"; - addusage(); + addUsage(); cout << endl << "-h : this help" << endl << "port : 2-5 digits, default: " << defPort << endl; - addhelp(); + addHelp(); } diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index df38f0c..b3d6d8e 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -28,6 +28,7 @@ #include #else using SOCKET = INT32; +constexpr SOCKET INVALID_SOCKET = -1; #endif #include #include @@ -35,13 +36,16 @@ using SOCKET = INT32; class TCP_Srv_Base { public: - inline TCP_Srv_Base() = default; // run with given port void run(UINT16 port = defPort); // run with CLI arguments void run(INT32 argc, const CONST_C_STRING* argv); + virtual void cleanup(); + + protected: + inline TCP_Srv_Base() = default; // timeval seconds constexpr static UINT32 tmSec = SELECT_MILLI_SECONDS / 1000; // timeval microseconds @@ -56,28 +60,30 @@ class TCP_Srv_Base // handle unmatched CLI argument // can be implemented by derived class - inline virtual bool handlearg(CONST_C_STRING) + inline virtual bool handleArg(CONST_C_STRING) { return true; } // add usage item to std::out // can be implemented by derived class - inline virtual void addusage() const {} + inline virtual void addUsage() const {} // add help item to std::out // can be implemented by derived class - inline virtual void addhelp() const {} + inline virtual void addHelp() const {} // other tasks to be done in main loop // can be implemented by derived class - inline virtual void other_tasks() {} + inline virtual void otherTasks() {} private: // default port constexpr static UINT16 defPort = 8080; - // thread method - void tm(SOCKET clientSocket, UINT32 nr); + // listen socket + SOCKET mListenSocket = INVALID_SOCKET; + // client thread method + void handleClient(SOCKET clientSocket, UINT32 nr); // thread count UINT32 mCnt = 0; // thread number diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index d08f972..447be6f 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -7,9 +7,17 @@ #include using std::cerr, std::endl; +#include +using std::setw; #include -bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) +TCP_Srv_Echo& TCP_Srv_Echo::instance() +{ + static TCP_Srv_Echo instance; + return instance; +} + +bool TCP_Srv_Echo::handleArg(const CONST_C_STRING argv) { bool ok = true; // locale only required if trace is on @@ -25,12 +33,12 @@ bool TCP_Srv_Echo::handlearg(const CONST_C_STRING argv) return ok; } -void TCP_Srv_Echo::addusage() const +void TCP_Srv_Echo::addUsage() const { Trace() << " [locale]"; } -void TCP_Srv_Echo::addhelp() const +void TCP_Srv_Echo::addHelp() const { Trace() << "locale: see setlocale" << endl; } @@ -51,11 +59,17 @@ void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t } } -#include -#include -#include -void TCP_Srv_Echo::other_tasks() +void TCP_Srv_Echo::otherTasks() { - { TraceLock() << " other tasks: " << (std::time(nullptr) % 500) << endl; } - std::this_thread::sleep_for(std::chrono::milliseconds(500)); + if constexpr (Trace::isOn) + { + static UINT32 cnt = 0; + static UINT32 dsp = 0; + if (++cnt == 100) + { + cnt = 0; + ++dsp; + TraceLock() << "--- other tasks " << setw(5) << dsp << "00" << endl; + } + } } \ No newline at end of file diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index a4a5352..560eefb 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -12,7 +12,11 @@ class TCP_Srv_Echo : public TCP_Srv_Base { public: - inline TCP_Srv_Echo() = default; + static TCP_Srv_Echo& instance(); + + + TCP_Srv_Echo(const TCP_Srv_Echo&) = delete; + TCP_Srv_Echo& operator=(const TCP_Srv_Echo&) = delete; protected: // simple implementation of process: @@ -23,12 +27,14 @@ class TCP_Srv_Echo : public TCP_Srv_Base // handle unused argument as locale to set // does not make a big difference with gcc linux // but with msvc windows - bool handlearg(CONST_C_STRING argv) final; + bool handleArg(CONST_C_STRING argv) final; - void addusage() const final; - void addhelp() const final; + void addUsage() const final; + void addHelp() const final; - void other_tasks() final; + void otherTasks() final; +private: + inline TCP_Srv_Echo() = default; }; #endif // _H diff --git a/code/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp index c03cab3..bd4dc62 100644 --- a/code/TCP_Srv_Echo_main.cpp +++ b/code/TCP_Srv_Echo_main.cpp @@ -4,11 +4,17 @@ // created by Manfred Sorgo #include "TCP_Srv_Echo.h" +#include +void signalHandler(int) +{ + TCP_Srv_Echo::instance().cleanup(); + exit(0); +} int main(const INT32 argc, const CONST_C_STRING* const argv) { - TCP_Srv_Echo srv; - srv.run(argc, argv); + signal(SIGINT, signalHandler); + TCP_Srv_Echo::instance().run(argc, argv); // only reached in case of failure return 1; } diff --git a/code/Trace.cpp b/code/Trace.cpp index 4b4311a..ed8e663 100644 --- a/code/Trace.cpp +++ b/code/Trace.cpp @@ -2,6 +2,4 @@ #include "Trace.h" #ifdef VERBOSE std::mutex TraceLock::mMutex; -#else -NullStream Trace::mNull; #endif // VERBOSE \ No newline at end of file diff --git a/code/Trace.h b/code/Trace.h index a45e107..3a30e6c 100644 --- a/code/Trace.h +++ b/code/Trace.h @@ -32,45 +32,27 @@ class NullStream : public std::ostream { public: inline NullStream() : std::ostream(&buffer) {} - + constexpr static bool isOn = false; private: NullBuffer buffer; }; -// class Trace acts as std::cout if VERBOSE is defined -// otherwise as NullStream with no output -class Trace +// ostream class acting as std::cout +// thanks to ChatGPT for this +class OutStream : public std::ostream { public: -// avoid macro usage -// use: -// constexpr if (Trace::isOn) { ... } -// constexpr if (not Trace::isOn) { ... } -// to eliminate code from compilation -// in opposite to macros there won't be compiler warnings of unused variables -#ifdef VERBOSE + inline OutStream() : std::ostream(std::cout.rdbuf()) {} constexpr static bool isOn = true; -#else - constexpr static bool isOn = false; -#endif - inline Trace() = default; +}; +// class Trace acts as std::cout if VERBOSE is defined +// otherwise as NullStream with no output #ifdef VERBOSE - template - inline std::ostream& operator<<(const T& t) - { - return std::cout << t; - } +using Trace = OutStream; #else - template - inline std::ostream& operator<<(const T& t) - { - return mNull << t; - } -private: - static NullStream mNull; +using Trace = NullStream; #endif -}; // class TraceLock behaves like class Trace // in addition, if VERBOSE is defined diff --git a/make/build.sh b/make/build.sh index ec5a137..65f307c 100644 --- a/make/build.sh +++ b/make/build.sh @@ -1,58 +1,83 @@ #!/bin/bash +configs="prod verbose vsmall" + help() { echo "Usage: $(basename $0) options [port] [locale]" echo "options:" - echo "configurations (select one):" - echo " -p config; prod" - echo " -v config: verbose" - echo " -s config: vsmall" - echo "behaviour:" - echo " -c clean all untracked artifacts" - echo " -r run binary with [port] [locale]" - echo " -h this help" + echo "-c clean ignored artifacts before" + echo "-r run binary with [port] [locale]" + echo " with in $configs" + echo "-p premake5 makefiles" + echo "-h this help" exit } -conf= run= -port= clean= -while getopts pvscrh option; do +pre= +while getopts cr:ph option; do case $option in - (p) conf=prod;; - (v) conf=verbose;; - (s) conf=vsmall;; (c) clean=1;; - (r) run=1;; + (r) run=$OPTARG;; + (p) pre=1;; (h) help;; esac done shift $(($OPTIND - 1)) -if test -z $conf; then help; fi +function tm() +{ + en=$(date +%s) + printf "%-10s[%3d]\n" $1 $(($en-$2)) +} -cd $(dirname $0) +function mkconfig +{ + st=$(date +%s) + make -j config=$1 >/dev/null + if test $? -ne 0; then return 1; fi + tm ${1%.*} $st + return 0 +} -if test -v $clean; then git clean -dfx . 2>/dev/null >/dev/null; fi +cd $(dirname $0) -make config=$conf clean >/dev/null -make -j config=$conf +if test ! -z $pre; then premake5 gmake2; fi -if test $? -ne 0; then exit $?; fi +if test ! -z $clean; then git clean -dfXq . ; fi +echo building congigurations ... -bin=bin/EchoSrv_$conf +pids=() +for config in $configs; do + mkconfig $config & pids+=($!) +done -if test ! -f $bin; then exit 1; fi +ecode=0 +for pid in ${pids[*]}; do + if ! wait $pid; then ecode=1; fi +done if test -z $run; then - echo "-> $bin" -else - echo "" - echo "starting $bin $*" - echo "" - $bin $* + builds=$(ls bin/* 2>/dev/null) + if test ! -z "$builds"; then + echo built: + for b in $builds; do + echo - $b + done + fi + exit $ecode; +fi + +bin=$(ls bin/*_$run 2>/dev/null | head -n 1) +if test -z $bin; then + echo "no binary for config $run" + exit 1 fi +echo "" +echo "starting $bin $*" +echo "" +$bin $* From 763d89cf0617962ab3e46a44f5dc576f45b4e290 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 7 Apr 2024 00:48:52 +0200 Subject: [PATCH 46/56] better tracing; removed cleanup --- code/TCP_Srv_Base.cpp | 25 ++++++--------- code/TCP_Srv_Base.h | 5 +-- code/TCP_Srv_Echo_main.cpp | 8 ----- code/Trace.cpp | 4 +-- code/Trace.h | 62 +++++++++++++++++++++----------------- 5 files changed, 46 insertions(+), 58 deletions(-) diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 2f40124..aa06c99 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -78,8 +78,8 @@ void TCP_Srv_Base::run(const UINT16 port) // create socket if (ok) { - mListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (mListenSocket < 0) + listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listenSocket < 0) { cerr << "ERR socket" << endl; ok = false; @@ -93,14 +93,14 @@ void TCP_Srv_Base::run(const UINT16 port) addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); - if (bind(mListenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) + if (bind(listenSocket, (const sockaddr*)&addr, sizeof(addr)) < 0) { cerr << "ERR bind: port " << port << endl; ok = false; } } // listen to socket - if (ok and (listen(mListenSocket, SOMAXCONN) < 0)) + if (ok and (listen(listenSocket, SOMAXCONN) < 0)) { cerr << "ERR listen" << endl; ok = false; @@ -125,10 +125,10 @@ void TCP_Srv_Base::run(const UINT16 port) // select fd_set lset; FD_ZERO(&lset); - FD_SET(mListenSocket, &lset); + FD_SET(listenSocket, &lset); timeval tv { tmSec, tmMic}; - if (select(mListenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) + if (select(listenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) { cerr << "ERR listen select" << endl; ok = false; @@ -136,9 +136,9 @@ void TCP_Srv_Base::run(const UINT16 port) // accept to new client socket if listen socket is set - else if (FD_ISSET(mListenSocket, &lset)) + else if (FD_ISSET(listenSocket, &lset)) { - SOCKET clientSocket = accept(mListenSocket, nullptr, nullptr); + SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); if (clientSocket < 0) { @@ -158,14 +158,9 @@ void TCP_Srv_Base::run(const UINT16 port) otherTasks(); } // only reached in case of error: clean up -} - -void TCP_Srv_Base::cleanup() -{ - Trace() << endl << "cleanup" << endl; - if (mListenSocket >= 0) + if (listenSocket >= 0) { - closesocket(mListenSocket); + closesocket(listenSocket); } #ifdef _WIN32 WSACleanup(); diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index b3d6d8e..3f181da 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -41,9 +41,6 @@ class TCP_Srv_Base // run with CLI arguments void run(INT32 argc, const CONST_C_STRING* argv); - virtual void cleanup(); - - protected: inline TCP_Srv_Base() = default; // timeval seconds @@ -81,7 +78,7 @@ class TCP_Srv_Base // default port constexpr static UINT16 defPort = 8080; // listen socket - SOCKET mListenSocket = INVALID_SOCKET; + SOCKET listenSocket = INVALID_SOCKET; // client thread method void handleClient(SOCKET clientSocket, UINT32 nr); // thread count diff --git a/code/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp index bd4dc62..f046599 100644 --- a/code/TCP_Srv_Echo_main.cpp +++ b/code/TCP_Srv_Echo_main.cpp @@ -4,16 +4,8 @@ // created by Manfred Sorgo #include "TCP_Srv_Echo.h" -#include -void signalHandler(int) -{ - TCP_Srv_Echo::instance().cleanup(); - exit(0); -} - int main(const INT32 argc, const CONST_C_STRING* const argv) { - signal(SIGINT, signalHandler); TCP_Srv_Echo::instance().run(argc, argv); // only reached in case of failure return 1; diff --git a/code/Trace.cpp b/code/Trace.cpp index ed8e663..f0dba41 100644 --- a/code/Trace.cpp +++ b/code/Trace.cpp @@ -1,5 +1,3 @@ #include "Trace.h" -#ifdef VERBOSE -std::mutex TraceLock::mMutex; -#endif // VERBOSE \ No newline at end of file +std::mutex LockedStream::mMutex; diff --git a/code/Trace.h b/code/Trace.h index 3a30e6c..face4dd 100644 --- a/code/Trace.h +++ b/code/Trace.h @@ -18,7 +18,7 @@ #include // NullBuffer and NullStream are used to prevent from output -// thanks to ChatGPT for this +// thanks to ChatGPT for the basics of this class NullBuffer : public std::streambuf { public: @@ -31,54 +31,60 @@ class NullBuffer : public std::streambuf class NullStream : public std::ostream { public: - inline NullStream() : std::ostream(&buffer) {} constexpr static bool isOn = false; + inline NullStream(UINT32 = 0) : std::ostream(&mBuffer) {} private: - NullBuffer buffer; + NullBuffer mBuffer; }; // ostream class acting as std::cout -// thanks to ChatGPT for this +// thanks to ChatGPT for the basics of this class OutStream : public std::ostream { public: - inline OutStream() : std::ostream(std::cout.rdbuf()) {} constexpr static bool isOn = true; + inline OutStream() : std::ostream(std::cout.rdbuf()) {} }; -// class Trace acts as std::cout if VERBOSE is defined -// otherwise as NullStream with no output -#ifdef VERBOSE -using Trace = OutStream; -#else -using Trace = NullStream; -#endif - -// class TraceLock behaves like class Trace -// in addition, if VERBOSE is defined -// parallel output (of other TraceLock objects) +// class LockedStream behaves like class OutStream +// in addition, parallel output (of other LockedStream objects) // is blocked by a mutex lock within object scope -// use brackets to limit scope of TraceLock object +// use brackets to limit scope of LockedStream object // to avoid deadlocks // e.g. -// { TraceLock() << "hello world" << std::endl; } -class TraceLock : public Trace +// { LockedStream() << "hello world" << std::endl; } +class LockedStream : public OutStream { public: -#ifdef VERBOSE - TraceLock() : Trace(), mLock(mMutex) - {} - // start trace with number - TraceLock(const UINT32 nr) : TraceLock() + LockedStream() : OutStream(), mLock(mMutex) {} + // since threading is involved, a (thread) number can be added to the output + LockedStream(const UINT32 nr) : LockedStream() { - *this << std::setw(3) << nr << ' '; + *this << std::setw(3) << nr << ' '; } private: static std::mutex mMutex; - std::unique_lock mLock; + std::unique_lock mLock; +}; + +// if VERBOSE is defined +// - class Trace is OutStream +// - class TraceLock is LockedStream +// otherwise +// - class Trace is NullStream +// - class TraceLock is NullStream +// avoid macro usage +// use: +// constexpr if (Trace::verbose) { ... } +// constexpr if (not Trace::verbose) { ... } +// to eliminate greater bits of output code from compilation +// in opposite to macros there won't be compiler warnings of unused variables +#ifdef VERBOSE +using Trace = OutStream; +using TraceLock = LockedStream; #else - TraceLock(const UINT32 = 0) {} +using Trace = NullStream; +using TraceLock = NullStream; #endif -}; #endif // _H From 2212327309fbb121798f33431f1547b871055de6 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 7 Apr 2024 18:06:32 +0200 Subject: [PATCH 47/56] build, GPT for gcc & VS --- ChatGPT/{EchoSrv.make => EchoSrvGPT.make} | 0 ChatGPT/Makefile | 16 +++---- ChatGPT/main.cpp | 2 +- ChatGPT/premake5.lua | 20 ++++++--- ChatGPT/server.cpp | 53 +++++++++++++++++------ ChatGPT/server.h | 30 ++++++++----- code/TCP_Srv_Base.cpp | 33 +++++++++----- code/TCP_Srv_Base.h | 14 +++--- code/TCP_Srv_Echo.cpp | 32 +++++++++----- code/TCP_Srv_Echo.h | 8 +++- make/build.sh | 28 +++++++++--- make/premake5.lua | 2 +- 12 files changed, 156 insertions(+), 82 deletions(-) rename ChatGPT/{EchoSrv.make => EchoSrvGPT.make} (100%) diff --git a/ChatGPT/EchoSrv.make b/ChatGPT/EchoSrvGPT.make similarity index 100% rename from ChatGPT/EchoSrv.make rename to ChatGPT/EchoSrvGPT.make diff --git a/ChatGPT/Makefile b/ChatGPT/Makefile index 1ce31b2..0e7b10d 100644 --- a/ChatGPT/Makefile +++ b/ChatGPT/Makefile @@ -9,26 +9,26 @@ ifndef verbose endif ifeq ($(config),chatgpt) - EchoSrv_config = chatgpt + EchoSrvGPT_config = chatgpt else $(error "invalid configuration $(config)") endif -PROJECTS := EchoSrv +PROJECTS := EchoSrvGPT .PHONY: all clean help $(PROJECTS) all: $(PROJECTS) -EchoSrv: -ifneq (,$(EchoSrv_config)) - @echo "==== Building EchoSrv ($(EchoSrv_config)) ====" - @${MAKE} --no-print-directory -C . -f EchoSrv.make config=$(EchoSrv_config) +EchoSrvGPT: +ifneq (,$(EchoSrvGPT_config)) + @echo "==== Building EchoSrvGPT ($(EchoSrvGPT_config)) ====" + @${MAKE} --no-print-directory -C . -f EchoSrvGPT.make config=$(EchoSrvGPT_config) endif clean: - @${MAKE} --no-print-directory -C . -f EchoSrv.make clean + @${MAKE} --no-print-directory -C . -f EchoSrvGPT.make clean help: @echo "Usage: make [config=name] [target]" @@ -39,6 +39,6 @@ help: @echo "TARGETS:" @echo " all (default)" @echo " clean" - @echo " EchoSrv" + @echo " EchoSrvGPT" @echo "" @echo "For more information, see https://github.com/premake/premake-core/wiki" \ No newline at end of file diff --git a/ChatGPT/main.cpp b/ChatGPT/main.cpp index 8b289af..e75b5d3 100644 --- a/ChatGPT/main.cpp +++ b/ChatGPT/main.cpp @@ -2,7 +2,7 @@ int main() { // Port number to listen on - int port = 8080; + const uint16_t port = 8080; // Create an instance of the server TCPServer server(port); diff --git a/ChatGPT/premake5.lua b/ChatGPT/premake5.lua index 03c0710..a739d6e 100644 --- a/ChatGPT/premake5.lua +++ b/ChatGPT/premake5.lua @@ -1,12 +1,18 @@ -workspace 'EchoSrv_ChatGPT' - filter { 'action:gmake*' } - configurations { 'ChatGPT' } - language 'C++' +workspace 'EchoSrvGPT' + configurations { 'ChatGPT' } + language 'C++' - project 'EchoSrv' - kind 'ConsoleApp' - files { '*.cpp' } + project 'EchoSrvGPT' + kind 'ConsoleApp' + files { '*.cpp' } + filter { 'action:gmake*' } buildoptions { '-std=c++17 -pedantic-errors -Werror -Wall' } objdir 'obj/gcc/' linkoptions { '-pthread' } targetdir 'bin' + + filter { 'action:vs*' } + warnings 'high' + buildoptions { '/std:c++17 /W4' } + objdir 'obj/vs/' + targetdir 'exe' diff --git a/ChatGPT/server.cpp b/ChatGPT/server.cpp index fd615fc..32cf358 100644 --- a/ChatGPT/server.cpp +++ b/ChatGPT/server.cpp @@ -1,6 +1,18 @@ #include "server.h" -TCPServer::TCPServer(int port) : port(port), running(false) {} +#ifdef _WIN32 +// required lib for Winsock +#pragma comment(lib, "ws2_32") +#else +#include +#include +#include +#define closesocket close +#endif + +using std::cerr, std::cout, std::endl; + +TCPServer::TCPServer(uint16_t port) : port(port), running(false) {} TCPServer::~TCPServer() { Stop(); @@ -9,9 +21,21 @@ TCPServer::~TCPServer() { void TCPServer::Start() { running = true; - serverSocket = socket(AF_INET, SOCK_STREAM, 0); - if (serverSocket == -1) { - std::cerr << "Error creating socket\n"; +#ifdef _WIN32 + // check for Windows Sockets version 2.2 + { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + cerr << "ERR WSAStartup" << endl; + return; + } + } +#endif + + serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverSocket == INVALID_SOCKET) { + cerr << "Error creating socket" << endl; return; } @@ -21,12 +45,12 @@ void TCPServer::Start() { serverAddr.sin_port = htons(port); if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { - std::cerr << "Bind failed\n"; + cerr << "Bind failed" << endl; return; } if (listen(serverSocket, 5) < 0) { - std::cerr << "Listen failed\n"; + cerr << "Listen failed" << endl; return; } @@ -36,20 +60,22 @@ void TCPServer::Start() { void TCPServer::Stop() { running = false; - close(serverSocket); + closesocket(serverSocket); +#ifdef _WIN32 + WSACleanup(); +#endif for (auto& thread : clientThreads) { thread.join(); } + clientThreads.clear(); } void TCPServer::Listen() { while (running) { - sockaddr_in clientAddr; - socklen_t clientAddrLen = sizeof(clientAddr); - int clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientAddrLen); + SOCKET clientSocket = accept(serverSocket, nullptr, nullptr); if (clientSocket < 0) { - std::cerr << "Accept failed\n"; + if (running) cerr << "Accept failed" << endl; continue; } @@ -58,7 +84,7 @@ void TCPServer::Listen() { } } -void TCPServer::HandleClient(int clientSocket) { +void TCPServer::HandleClient(SOCKET clientSocket) { // Handle client communication here // For demonstration, just echo received messages back to the client char buffer[1024]; @@ -67,5 +93,6 @@ void TCPServer::HandleClient(int clientSocket) { send(clientSocket, buffer, bytesRead, 0); } - close(clientSocket); + closesocket(clientSocket); } + diff --git a/ChatGPT/server.h b/ChatGPT/server.h index ce5e96d..0322a00 100644 --- a/ChatGPT/server.h +++ b/ChatGPT/server.h @@ -1,20 +1,26 @@ #ifndef SERVER_H #define SERVER_H +#include +#include +#include +#include #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include + +#ifdef _WIN32 +#include +#else +using SOCKET = int; +constexpr SOCKET INVALID_SOCKET = -1; +#endif + class TCPServer { public: - TCPServer(int port); + TCPServer(uint16_t port); ~TCPServer(); void Start(); @@ -22,12 +28,12 @@ class TCPServer { private: void Listen(); - void HandleClient(int clientSocket); + void HandleClient(SOCKET clientSocket); - int port; - int serverSocket; + const uint16_t port; + SOCKET serverSocket; std::vector clientThreads; - std::atomic running; + std::atomic running = false; }; #endif // SERVER_H diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index aa06c99..f268be0 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -22,9 +22,7 @@ using std::setw; #include using std::regex, std::regex_match; -#include -using std::filesystem::path; - +using std::string; using mutexlock = std::unique_lock; void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) @@ -39,7 +37,7 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) { if (regex_match(argv[n], rxHelp)) { - help(path(argv[0]).filename().string()); + showHelp(argv[0]); cont = false; } } @@ -61,6 +59,8 @@ void TCP_Srv_Base::run(const UINT16 port) << "timeout:" << setw(6) << SELECT_MILLI_SECONDS << " ms" << endl << "buffer :" << setw(6) << buffSize << " bytes" << endl; + // listen socket + SOCKET listenSocket = INVALID_SOCKET; // indicator for continuation bool ok = true; @@ -216,14 +216,23 @@ void TCP_Srv_Base::displayThreads() const } } -void TCP_Srv_Base::help(const std::string&& argv0) const +string TCP_Srv_Base::usage(const CONST_C_STRING argv0) const +{ + return "usage : " + string(argv0) + " [-h] [port]"; +} + +string TCP_Srv_Base::help() const +{ + return + "-h : this help\n" + "port : 2-5 digits, default: " + std::to_string(defPort) + + "\n"; +} + +void TCP_Srv_Base::showHelp(const CONST_C_STRING argv0) const { cout << endl - << "usage : " << argv0 << " [-h] [port]"; - addUsage(); - cout << endl - << "-h : this help" << endl - << "port : 2-5 digits, default: " << defPort << endl; - addHelp(); -} + << usage(argv0) << endl + << help(); + } diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index 3f181da..fc58700 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -62,13 +62,11 @@ class TCP_Srv_Base return true; } - // add usage item to std::out - // can be implemented by derived class - inline virtual void addUsage() const {} + // usage line string with argv0 + virtual std::string usage(CONST_C_STRING argv0) const; - // add help item to std::out - // can be implemented by derived class - inline virtual void addHelp() const {} + // help text string + virtual std::string help() const; // other tasks to be done in main loop // can be implemented by derived class @@ -77,8 +75,6 @@ class TCP_Srv_Base private: // default port constexpr static UINT16 defPort = 8080; - // listen socket - SOCKET listenSocket = INVALID_SOCKET; // client thread method void handleClient(SOCKET clientSocket, UINT32 nr); // thread count @@ -96,7 +92,7 @@ class TCP_Srv_Base void displayThreads() const; // display help with called filename (rvalue reference for efficiency) - void help(const std::string&& argv0) const; + void showHelp(CONST_C_STRING argv0) const; }; #endif // _H \ No newline at end of file diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index 447be6f..92c8a53 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -11,6 +11,10 @@ using std::cerr, std::endl; using std::setw; #include +#include + +using std::string; + TCP_Srv_Echo& TCP_Srv_Echo::instance() { static TCP_Srv_Echo instance; @@ -32,15 +36,15 @@ bool TCP_Srv_Echo::handleArg(const CONST_C_STRING argv) } return ok; } - -void TCP_Srv_Echo::addUsage() const +string TCP_Srv_Echo::usage(const CONST_C_STRING argv0) const { - Trace() << " [locale]"; + return TCP_Srv_Base::usage(argv0) + " [locale]"; } -void TCP_Srv_Echo::addHelp() const +string TCP_Srv_Echo::help() const { - Trace() << "locale: see setlocale" << endl; + return TCP_Srv_Base::help() + + "locale: e.g. de_DE.UTF-8\n"; } void TCP_Srv_Echo::process(const SOCKET clientSocket, Buffer buff, const size_t size, const UINT32 nr) @@ -63,13 +67,19 @@ void TCP_Srv_Echo::otherTasks() { if constexpr (Trace::isOn) { - static UINT32 cnt = 0; - static UINT32 dsp = 0; - if (++cnt == 100) + using hClock = std::chrono::high_resolution_clock; + constexpr static auto per = 5000; + constexpr static auto num = SELECT_MILLI_SECONDS > per ? 1 : per / SELECT_MILLI_SECONDS; + static auto start = hClock::now(); + static auto cnt = 0; + if (++cnt == num) { - cnt = 0; - ++dsp; - TraceLock() << "--- other tasks " << setw(5) << dsp << "00" << endl; + const auto now = hClock::now(); + const auto dur = std::chrono::duration_cast(now - start); + const auto avrg = (dur.count() + num / 2) / num; + TraceLock() << "- other tasks every " << avrg << " ms" << endl; + cnt = 0; + start = now; } } } \ No newline at end of file diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index 560eefb..a976cce 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -29,8 +29,12 @@ class TCP_Srv_Echo : public TCP_Srv_Base // but with msvc windows bool handleArg(CONST_C_STRING argv) final; - void addUsage() const final; - void addHelp() const final; + // usage line string + std::string usage(CONST_C_STRING argv0) const final; + + // help text string + std::string help() const final; + void otherTasks() final; private: diff --git a/make/build.sh b/make/build.sh index 65f307c..0f1f8cb 100644 --- a/make/build.sh +++ b/make/build.sh @@ -1,6 +1,20 @@ #!/bin/bash -configs="prod verbose vsmall" +# extract all configurations from make help +getcfgs() +{ + take=0 + for item in $(make help); do + if [[ $item =~ CONFIG.*: ]]; then take=1 + elif [[ $item =~ : ]]; then take=0 + elif test $take -eq 1; then + cfgs="$cfgs $item" + cfgh="$cfgh, $item" + fi + done + cfgs=${cfgs:1} + cfgh=${cfgh:2} +} help() { @@ -8,12 +22,15 @@ help() echo "options:" echo "-c clean ignored artifacts before" echo "-r run binary with [port] [locale]" - echo " with in $configs" + echo " with in $cfgh" echo "-p premake5 makefiles" echo "-h this help" exit } +cd $(dirname $0) +getcfgs + run= clean= pre= @@ -21,7 +38,7 @@ while getopts cr:ph option; do case $option in (c) clean=1;; (r) run=$OPTARG;; - (p) pre=1;; + (p) pre=1;clean=1;; (h) help;; esac done @@ -43,15 +60,14 @@ function mkconfig return 0 } -cd $(dirname $0) +if test ! -z $clean; then git clean -dfXq . ; fi if test ! -z $pre; then premake5 gmake2; fi -if test ! -z $clean; then git clean -dfXq . ; fi echo building congigurations ... pids=() -for config in $configs; do +for config in $cfgs; do mkconfig $config & pids+=($!) done diff --git a/make/premake5.lua b/make/premake5.lua index cda9ea0..55e58b3 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -13,7 +13,7 @@ -- - vsmall: -- - same as verbose -- - but with small buffer sizes (8 bytes) --- - and long select timeout (1 second) +-- - and long select timeout (1000 ms) -- ============================================================ -- created by Manfred Sorgo From e6a927af2d9b0900048e8f5444cf9f44726639cf Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 7 Apr 2024 19:18:59 +0200 Subject: [PATCH 48/56] review rework --- py/echoClient.py | 4 ++-- py/echoCommon.py | 5 ++++- py/echoServer.py | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/py/echoClient.py b/py/echoClient.py index 851533e..c5c3712 100644 --- a/py/echoClient.py +++ b/py/echoClient.py @@ -11,8 +11,8 @@ class EchoClient(EchoCommon): def __init__(self, *args): super().__init__(*args) - def usage(self, name): - print(f'Usage: {name} [options] [address] [port] [message] [loops] [threads]') + def usage(self, name) -> str: + return super().usage(name) + ' [message] [loops] [threads]' def run(self): for n in range(self.threads): diff --git a/py/echoCommon.py b/py/echoCommon.py index f64810c..95e62aa 100644 --- a/py/echoCommon.py +++ b/py/echoCommon.py @@ -74,8 +74,11 @@ def log(self, *args): def silent(self, *args): pass + def usage(self, name) -> str: + return f'Usage: {name} [options] [address] [port]' + def help(self): - self.usage(basename(argv[0])) + print(self.usage(basename(argv[0]))) print( """options: -v verbose diff --git a/py/echoServer.py b/py/echoServer.py index a22a063..f0b634c 100644 --- a/py/echoServer.py +++ b/py/echoServer.py @@ -44,9 +44,6 @@ def tfunc(self, listenSocket:socket): self.tell('EX', port) except Exception as e: self.log('tfunc:', e) - - def usage(self, name): - print(f'Usage: {name} [options] [address] [port]') if __name__ == '__main__': from sys import argv From 5f71bfa3554eaf95ac146f47a78d43aafc31899d Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 7 Apr 2024 20:01:16 +0200 Subject: [PATCH 49/56] review rework --- .gitignore | 2 -- basics.md | 8 ++++---- make/howto_build.md | 37 +++++++++++++++---------------------- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index d3f5b5e..699964d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,9 +44,7 @@ exe *.vcxproj.* *.vcxproj *.sln -# *.make # general tmp.* tmp_.* - diff --git a/basics.md b/basics.md index 0170dfb..5f8f733 100644 --- a/basics.md +++ b/basics.md @@ -56,10 +56,10 @@ ________| | select() | | | FD_ISSET() -| | | -| N Y -| | | -| | accept() -> clientSocket, thread(clientSocket) +| | | +| N Y +| | | +| | accept() -> clientSocket, thread(clientSocket) | |_____| | | | other_tasks() diff --git a/make/howto_build.md b/make/howto_build.md index d1f84c3..e692043 100644 --- a/make/howto_build.md +++ b/make/howto_build.md @@ -22,7 +22,7 @@ TARGETS: clean TCP_Srv/make$> make config=vsmall clean -Cleaning TCP_Srv_Echo +Cleaning EchoSrv TCP_Srv/make$> make -j config=vsmall ==== Building TCP_Srv_Echo (vsmall) ==== @@ -31,7 +31,7 @@ Creating bin TCP_Srv_Base.cpp TCP_Srv_Echo.cpp TCP_Srv_Echo_main.cpp -Linking TCP_Srv_Echo +Linking EchoSrv ```` ### use build.sh call help @@ -39,36 +39,29 @@ call help TCP_Srv/make$> ./build.sh -h Usage: build.sh options [port] [locale] options: -configurations (select one): - -p config; prod - -v config: verbose - -s config: vsmall -behaviour: - -c clean all untracked artifacts - -r run binary with [port] [locale] - -h this help +-c clean ignored artifacts before +-r run binary with [port] [locale] + with in prod, verbose, vsmall +-p premake5 makefiles +-h this help ```` build (and run) e.g.: - configuration _verbose_ - port 8090 - locale de_DE.UTF-8 ````shell -TCP_Srv/make$> ./build.sh -vr 8090 de_DE.UTF-8 -==== Building TCP_Srv_Echo (verbose) ==== -Creating obj/gcc/verbose -Creating bin -TCP_Srv_Base.cpp -TCP_Srv_Echo.cpp -TCP_Srv_Echo_main.cpp -Trace.cpp -Linking TCP_Srv_Echo +TCP_Srv/make$> ./build.sh -cr vsmall 8090 de_DE.UTF-8 +building congigurations ... +prod [ 5] +vsmall [ 6] +verbose [ 6] -starting bin/EchoSrv_verbose 8090 de_DE.UTF-8 +starting bin/EchoSrv_vsmall 8090 de_DE.UTF-8 locale : de_DE.UTF-8 ... -timeout: 10 ms -buffer : 1024 bytes +timeout: 1000 ms +buffer : 8 bytes port : 8090 ```` From 6d1f95ffb3054ce18c716fbcd48273b99b047712 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sun, 7 Apr 2024 23:40:30 +0200 Subject: [PATCH 50/56] review rework --- basics.md | 119 ------------------------------------- code/TCP_Srv_Base.cpp | 9 +-- code/TCP_Srv_Echo.cpp | 9 +-- code/TCP_Srv_Echo.h | 10 +--- code/TCP_Srv_Echo_main.cpp | 3 +- code/Trace.cpp | 3 + code/Trace.h | 9 ++- solutions.md | 87 +++++++++++++++++++++++++++ 8 files changed, 108 insertions(+), 141 deletions(-) delete mode 100644 basics.md create mode 100644 solutions.md diff --git a/basics.md b/basics.md deleted file mode 100644 index 5f8f733..0000000 --- a/basics.md +++ /dev/null @@ -1,119 +0,0 @@ -# c++ -## sockets -### struct timeval -- VSC -- GCC - -```cpp -struct timeval -{ - long tv_sec; /* seconds */ - long tv_usec; /* microseconds */ -}; -``` -#### fd_set / select -- VSC -- GCC - -```cpp -// reset fd_set -FD_ZERO(fd_set *fdset); -// assign socket (file descriptor) to fd_set -FD_SET(int fd, fd_set *fdset); -// check if socket has event -FD_ISSET(int fd, fd_set *fdset); -// clear socket (file descriptor) in fd_set -FD_CLR(int fd, fd_set *fdset); - -// return: number of ready sockets -// 0: timeout reached -// <0: error -int select( - int nfds, // historical, unused - fd_set* readfds, // [in, out] - fd_set* writefds, // [in, out] - fd_set* exceptfds, // [in, out] - const timeval * timeout // [in] -); -``` -### server with threads -``` - listenSocket - | - socket() - | - bind() - | - listen() -________| -| | -| fd_set -| | -| FD_ZERO() -| | -| FD_SET() -| | -| select() -| | -| FD_ISSET() -| | | -| N Y -| | | -| | accept() -> clientSocket, thread(clientSocket) -| |_____| -| | -| other_tasks() -|_________| -``` - -## threads -### Start a thread with object methods -```cpp -#include -std::thread t(&Class::method, this, arguments..); -t.detach(); -``` - - -# python -### ipv6 example -```python ->>> import socket ->>> socket.getaddrinfo('www.python.org', 80, 0, 0, socket.SOL_TCP) -[(2, 1, 6, '', ('82.94.164.162', 80)), - (10, 1, 6, '', ('2001:888:2000:d::a2', 80, 0, 0))] - ->>> ourSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) ->>> ourSocket.connect(('2001:888:2000:d::a2', 80, 0, 0)) -``` -### client example -```python -import socket - -HOST = '127.0.0.1' -PORT = 8001 - -with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.connect((HOST, PORT)) - s.sendall(b'Hello, world') - data = s.recv(1024) - print(f'received {data}') -``` -### server with threads -``` - listenSocket - | - socket() - | - bind() - | - listen() -________| -| | -| select() -| | | -| N Y -|______| | -| |-> thread(listenSocket), accept() -> clientSocket -|__________| -``` diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index f268be0..9aa1893 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -55,7 +55,7 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) void TCP_Srv_Base::run(const UINT16 port) { std::cout << "..." << endl; - Trace() + trace << "timeout:" << setw(6) << SELECT_MILLI_SECONDS << " ms" << endl << "buffer :" << setw(6) << buffSize << " bytes" << endl; @@ -202,7 +202,7 @@ void TCP_Srv_Base::endOfThread() if (mCnt == 0) { mNum = 0; - Trace() << "--- no clients ---" << endl; + trace << "--- no clients ---" << endl; } displayThreads(); } @@ -210,10 +210,7 @@ void TCP_Srv_Base::endOfThread() void TCP_Srv_Base::displayThreads() const { - if constexpr (not Trace::isOn) - { - cout << "threads:" << setw(6) << mCnt << '\r' << flush; - } + whisper << "threads:" << setw(6) << mCnt << '\r' << flush; } string TCP_Srv_Base::usage(const CONST_C_STRING argv0) const diff --git a/code/TCP_Srv_Echo.cpp b/code/TCP_Srv_Echo.cpp index 92c8a53..4b95588 100644 --- a/code/TCP_Srv_Echo.cpp +++ b/code/TCP_Srv_Echo.cpp @@ -15,12 +15,6 @@ using std::setw; using std::string; -TCP_Srv_Echo& TCP_Srv_Echo::instance() -{ - static TCP_Srv_Echo instance; - return instance; -} - bool TCP_Srv_Echo::handleArg(const CONST_C_STRING argv) { bool ok = true; @@ -32,10 +26,11 @@ bool TCP_Srv_Echo::handleArg(const CONST_C_STRING argv) cerr << "ERR setlocale: " << argv << endl; ok = false; } - else Trace() << "locale : " << argv << endl; + else trace << "locale : " << argv << endl; } return ok; } + string TCP_Srv_Echo::usage(const CONST_C_STRING argv0) const { return TCP_Srv_Base::usage(argv0) + " [locale]"; diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index a976cce..cc43971 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -12,9 +12,7 @@ class TCP_Srv_Echo : public TCP_Srv_Base { public: - static TCP_Srv_Echo& instance(); - - + inline TCP_Srv_Echo() = default; TCP_Srv_Echo(const TCP_Srv_Echo&) = delete; TCP_Srv_Echo& operator=(const TCP_Srv_Echo&) = delete; @@ -26,7 +24,7 @@ class TCP_Srv_Echo : public TCP_Srv_Base // handle unused argument as locale to set // does not make a big difference with gcc linux - // but with msvc windows + // but with msvc / windows bool handleArg(CONST_C_STRING argv) final; // usage line string @@ -35,10 +33,8 @@ class TCP_Srv_Echo : public TCP_Srv_Base // help text string std::string help() const final; - + // puts out the average time between calls void otherTasks() final; -private: - inline TCP_Srv_Echo() = default; }; #endif // _H diff --git a/code/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp index f046599..28247c1 100644 --- a/code/TCP_Srv_Echo_main.cpp +++ b/code/TCP_Srv_Echo_main.cpp @@ -6,7 +6,8 @@ #include "TCP_Srv_Echo.h" int main(const INT32 argc, const CONST_C_STRING* const argv) { - TCP_Srv_Echo::instance().run(argc, argv); + TCP_Srv_Echo srv; + srv.run(argc, argv); // only reached in case of failure return 1; } diff --git a/code/Trace.cpp b/code/Trace.cpp index f0dba41..7d170b1 100644 --- a/code/Trace.cpp +++ b/code/Trace.cpp @@ -1,3 +1,6 @@ #include "Trace.h" std::mutex LockedStream::mMutex; + +Trace trace; +Whisper whisper; \ No newline at end of file diff --git a/code/Trace.h b/code/Trace.h index face4dd..3f82f3e 100644 --- a/code/Trace.h +++ b/code/Trace.h @@ -69,10 +69,12 @@ class LockedStream : public OutStream // if VERBOSE is defined // - class Trace is OutStream -// - class TraceLock is LockedStream +// - class TraceLock is LockedStream +// - class Whisper is NullStream // otherwise // - class Trace is NullStream // - class TraceLock is NullStream +// - class Whisper is OutStream // avoid macro usage // use: // constexpr if (Trace::verbose) { ... } @@ -82,9 +84,14 @@ class LockedStream : public OutStream #ifdef VERBOSE using Trace = OutStream; using TraceLock = LockedStream; +using Whisper = NullStream; #else using Trace = NullStream; using TraceLock = NullStream; +using Whisper = OutStream; #endif +extern Trace trace; +extern Whisper whisper; + #endif // _H diff --git a/solutions.md b/solutions.md new file mode 100644 index 0000000..2131250 --- /dev/null +++ b/solutions.md @@ -0,0 +1,87 @@ +## c++ +### server with blocking accept and threads +``` + | + listenSocket + | + socket() + | + bind() + | + listen() + ______| +| | +| accept() -> clientSocket, thread(clientSocket) +|______| +``` +### server with non blocking select and threads +(implemented solution) +- can handle other tasks in main loop +``` + listenSocket + | + socket() + | + bind() + | + listen() + _______| +| | +| fd_set +| | +| FD_ZERO() +| | +| FD_SET() +| | +| select() +| | +| FD_ISSET() +| | | +| N Y +| | | +| | accept() -> clientSocket, thread(clientSocket) +| |_____| +| | +| other_tasks() +|_________| +``` +### server with blocking accept in separate thread +(ChatGPT solution) +``` + | + |-----> thread + | | + | listenSocket + | | + | socket() + | | + | bind() + | | + | listen() + | ____| + | | | + | | accept() -> clientSocket, thread(clientSocket) + | |____| + | +wait for input + | +stop() +``` +## python +### server with select and threads +``` + listenSocket + | + socket() + | + bind() + | + listen() + _______| +| | +| select() +| | | +| N Y +|______| |--> thread(listenSocket), accept() -> clientSocket +|__________| +``` From c1647fff5a49cff0a4a743c574380a26ba3008ea Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 8 Apr 2024 00:18:22 +0200 Subject: [PATCH 51/56] integrated ChatGPT solution --- ChatGPT/EchoSrvGPT.make | 130 ------------------------- ChatGPT/Makefile | 44 --------- ChatGPT/premake5.lua | 18 ---- ChatGPT/server.cpp => code/GPT_Srv.cpp | 3 +- ChatGPT/server.h => code/GPT_Srv.h | 8 +- ChatGPT/main.cpp => code/GPT_main.cpp | 3 +- make/EchoSrv.make | 77 +++++++++++++++ make/Makefile | 4 + make/premake5.lua | 22 ++++- 9 files changed, 106 insertions(+), 203 deletions(-) delete mode 100644 ChatGPT/EchoSrvGPT.make delete mode 100644 ChatGPT/Makefile delete mode 100644 ChatGPT/premake5.lua rename ChatGPT/server.cpp => code/GPT_Srv.cpp (98%) rename ChatGPT/server.h => code/GPT_Srv.h (81%) rename ChatGPT/main.cpp => code/GPT_main.cpp (91%) diff --git a/ChatGPT/EchoSrvGPT.make b/ChatGPT/EchoSrvGPT.make deleted file mode 100644 index cb03b0a..0000000 --- a/ChatGPT/EchoSrvGPT.make +++ /dev/null @@ -1,130 +0,0 @@ -# Alternative GNU Make project makefile autogenerated by Premake - -ifndef config - config=chatgpt -endif - -ifndef verbose - SILENT = @ -endif - -.PHONY: clean prebuild - -SHELLTYPE := posix -ifeq ($(shell echo "test"), "test") - SHELLTYPE := msdos -endif - -# Configurations -# ############################################# - -RESCOMP = windres -TARGETDIR = bin -TARGET = $(TARGETDIR)/EchoSrv -OBJDIR = obj/gcc -DEFINES += -INCLUDES += -FORCE_INCLUDE += -ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) -ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -std=c++17 -pedantic-errors -Werror -Wall -ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) -LIBS += -LDDEPS += -ALL_LDFLAGS += $(LDFLAGS) -s -pthread -LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) -define PREBUILDCMDS -endef -define PRELINKCMDS -endef -define POSTBUILDCMDS -endef - -# Per File Configurations -# ############################################# - - -# File sets -# ############################################# - -GENERATED := -OBJECTS := - -GENERATED += $(OBJDIR)/main.o -GENERATED += $(OBJDIR)/server.o -OBJECTS += $(OBJDIR)/main.o -OBJECTS += $(OBJDIR)/server.o - -# Rules -# ############################################# - -all: $(TARGET) - @: - -$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) - $(PRELINKCMDS) - @echo Linking EchoSrv - $(SILENT) $(LINKCMD) - $(POSTBUILDCMDS) - -$(TARGETDIR): - @echo Creating $(TARGETDIR) -ifeq (posix,$(SHELLTYPE)) - $(SILENT) mkdir -p $(TARGETDIR) -else - $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) -endif - -$(OBJDIR): - @echo Creating $(OBJDIR) -ifeq (posix,$(SHELLTYPE)) - $(SILENT) mkdir -p $(OBJDIR) -else - $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) -endif - -clean: - @echo Cleaning EchoSrv -ifeq (posix,$(SHELLTYPE)) - $(SILENT) rm -f $(TARGET) - $(SILENT) rm -rf $(GENERATED) - $(SILENT) rm -rf $(OBJDIR) -else - $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) - $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) - $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) -endif - -prebuild: | $(OBJDIR) - $(PREBUILDCMDS) - -ifneq (,$(PCH)) -$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) -$(GCH): $(PCH) | prebuild - @echo $(notdir $<) - $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" -$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) -ifeq (posix,$(SHELLTYPE)) - $(SILENT) touch "$@" -else - $(SILENT) echo $null >> "$@" -endif -else -$(OBJECTS): | prebuild -endif - - -# File Rules -# ############################################# - -$(OBJDIR)/main.o: main.cpp - @echo "$(notdir $<)" - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/server.o: server.cpp - @echo "$(notdir $<)" - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" - --include $(OBJECTS:%.o=%.d) -ifneq (,$(PCH)) - -include $(PCH_PLACEHOLDER).d -endif \ No newline at end of file diff --git a/ChatGPT/Makefile b/ChatGPT/Makefile deleted file mode 100644 index 0e7b10d..0000000 --- a/ChatGPT/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# Alternative GNU Make workspace makefile autogenerated by Premake - -ifndef config - config=chatgpt -endif - -ifndef verbose - SILENT = @ -endif - -ifeq ($(config),chatgpt) - EchoSrvGPT_config = chatgpt - -else - $(error "invalid configuration $(config)") -endif - -PROJECTS := EchoSrvGPT - -.PHONY: all clean help $(PROJECTS) - -all: $(PROJECTS) - -EchoSrvGPT: -ifneq (,$(EchoSrvGPT_config)) - @echo "==== Building EchoSrvGPT ($(EchoSrvGPT_config)) ====" - @${MAKE} --no-print-directory -C . -f EchoSrvGPT.make config=$(EchoSrvGPT_config) -endif - -clean: - @${MAKE} --no-print-directory -C . -f EchoSrvGPT.make clean - -help: - @echo "Usage: make [config=name] [target]" - @echo "" - @echo "CONFIGURATIONS:" - @echo " chatgpt" - @echo "" - @echo "TARGETS:" - @echo " all (default)" - @echo " clean" - @echo " EchoSrvGPT" - @echo "" - @echo "For more information, see https://github.com/premake/premake-core/wiki" \ No newline at end of file diff --git a/ChatGPT/premake5.lua b/ChatGPT/premake5.lua deleted file mode 100644 index a739d6e..0000000 --- a/ChatGPT/premake5.lua +++ /dev/null @@ -1,18 +0,0 @@ -workspace 'EchoSrvGPT' - configurations { 'ChatGPT' } - language 'C++' - - project 'EchoSrvGPT' - kind 'ConsoleApp' - files { '*.cpp' } - filter { 'action:gmake*' } - buildoptions { '-std=c++17 -pedantic-errors -Werror -Wall' } - objdir 'obj/gcc/' - linkoptions { '-pthread' } - targetdir 'bin' - - filter { 'action:vs*' } - warnings 'high' - buildoptions { '/std:c++17 /W4' } - objdir 'obj/vs/' - targetdir 'exe' diff --git a/ChatGPT/server.cpp b/code/GPT_Srv.cpp similarity index 98% rename from ChatGPT/server.cpp rename to code/GPT_Srv.cpp index 32cf358..3452eb4 100644 --- a/ChatGPT/server.cpp +++ b/code/GPT_Srv.cpp @@ -1,4 +1,4 @@ -#include "server.h" +#include "GPT_Srv.h" #ifdef _WIN32 // required lib for Winsock @@ -10,6 +10,7 @@ #define closesocket close #endif +#include using std::cerr, std::cout, std::endl; TCPServer::TCPServer(uint16_t port) : port(port), running(false) {} diff --git a/ChatGPT/server.h b/code/GPT_Srv.h similarity index 81% rename from ChatGPT/server.h rename to code/GPT_Srv.h index 0322a00..0d87c3f 100644 --- a/ChatGPT/server.h +++ b/code/GPT_Srv.h @@ -1,12 +1,8 @@ -#ifndef SERVER_H -#define SERVER_H +#ifndef GPT_SRV_H +#define GPT_SRV_H #include -#include #include -#include -#include -#include #include #include diff --git a/ChatGPT/main.cpp b/code/GPT_main.cpp similarity index 91% rename from ChatGPT/main.cpp rename to code/GPT_main.cpp index e75b5d3..63fdb92 100644 --- a/ChatGPT/main.cpp +++ b/code/GPT_main.cpp @@ -1,4 +1,5 @@ -#include "server.h" +#include "GPT_Srv.h" +#include int main() { // Port number to listen on diff --git a/make/EchoSrv.make b/make/EchoSrv.make index ee85172..ba843b5 100644 --- a/make/EchoSrv.make +++ b/make/EchoSrv.make @@ -60,6 +60,15 @@ ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werr ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O0 -g -std=c++17 -pedantic-errors -Werror -Wall ALL_LDFLAGS += $(LDFLAGS) -pthread +else ifeq ($(config),gpt) +TARGETDIR = bin +TARGET = $(TARGETDIR)/EchoSrv_gpt +OBJDIR = obj/gcc/gpt +DEFINES += -DNDEBUG +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -std=c++17 -pedantic-errors -Werror -Wall +ALL_LDFLAGS += $(LDFLAGS) -s -pthread + endif # Per File Configurations @@ -72,6 +81,17 @@ endif GENERATED := OBJECTS := +ifeq ($(config),prod) +GENERATED += $(OBJDIR)/TCP_Srv_Base.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o +GENERATED += $(OBJDIR)/Trace.o +OBJECTS += $(OBJDIR)/TCP_Srv_Base.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o +OBJECTS += $(OBJDIR)/Trace.o + +else ifeq ($(config),verbose) GENERATED += $(OBJDIR)/TCP_Srv_Base.o GENERATED += $(OBJDIR)/TCP_Srv_Echo.o GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o @@ -81,6 +101,24 @@ OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o OBJECTS += $(OBJDIR)/Trace.o +else ifeq ($(config),vsmall) +GENERATED += $(OBJDIR)/TCP_Srv_Base.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo.o +GENERATED += $(OBJDIR)/TCP_Srv_Echo_main.o +GENERATED += $(OBJDIR)/Trace.o +OBJECTS += $(OBJDIR)/TCP_Srv_Base.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo.o +OBJECTS += $(OBJDIR)/TCP_Srv_Echo_main.o +OBJECTS += $(OBJDIR)/Trace.o + +else ifeq ($(config),gpt) +GENERATED += $(OBJDIR)/GPT_Srv.o +GENERATED += $(OBJDIR)/GPT_main.o +OBJECTS += $(OBJDIR)/GPT_Srv.o +OBJECTS += $(OBJDIR)/GPT_main.o + +endif + # Rules # ############################################# @@ -143,6 +181,21 @@ endif # File Rules # ############################################# +ifeq ($(config),prod) +$(OBJDIR)/TCP_Srv_Base.o: ../code/TCP_Srv_Base.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Echo.o: ../code/TCP_Srv_Echo.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Echo_main.o: ../code/TCP_Srv_Echo_main.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/Trace.o: ../code/Trace.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +else ifeq ($(config),verbose) $(OBJDIR)/TCP_Srv_Base.o: ../code/TCP_Srv_Base.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" @@ -156,6 +209,30 @@ $(OBJDIR)/Trace.o: ../code/Trace.cpp @echo "$(notdir $<)" $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +else ifeq ($(config),vsmall) +$(OBJDIR)/TCP_Srv_Base.o: ../code/TCP_Srv_Base.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Echo.o: ../code/TCP_Srv_Echo.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/TCP_Srv_Echo_main.o: ../code/TCP_Srv_Echo_main.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/Trace.o: ../code/Trace.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +else ifeq ($(config),gpt) +$(OBJDIR)/GPT_Srv.o: ../code/GPT_Srv.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/GPT_main.o: ../code/GPT_main.cpp + @echo "$(notdir $<)" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +endif + -include $(OBJECTS:%.o=%.d) ifneq (,$(PCH)) -include $(PCH_PLACEHOLDER).d diff --git a/make/Makefile b/make/Makefile index 18819f0..a3e0770 100644 --- a/make/Makefile +++ b/make/Makefile @@ -17,6 +17,9 @@ else ifeq ($(config),verbose) else ifeq ($(config),vsmall) EchoSrv_config = vsmall +else ifeq ($(config),gpt) + EchoSrv_config = gpt + else $(error "invalid configuration $(config)") endif @@ -43,6 +46,7 @@ help: @echo " prod" @echo " verbose" @echo " vsmall" + @echo " gpt" @echo "" @echo "TARGETS:" @echo " all (default)" diff --git a/make/premake5.lua b/make/premake5.lua index 55e58b3..48937b5 100644 --- a/make/premake5.lua +++ b/make/premake5.lua @@ -1,7 +1,7 @@ -- ============================================================ -- premake5 build rules --- TCP_Srv_Echo based on TCP_Srv_Base -- configurations: +-- A) TCP_Srv_Echo based on TCP_Srv_Base -- - prod: -- - no debug -- - no output @@ -14,29 +14,39 @@ -- - same as verbose -- - but with small buffer sizes (8 bytes) -- - and long select timeout (1000 ms) +-- B) modified ChatGPT solution +-- - gpt: +-- - no debug +-- - no output +-- - optimized for speed -- ============================================================ -- created by Manfred Sorgo +filesTCP = { '../code/TCP_Srv_*.cpp', '../code/Trace.cpp' } +filesGPT = { '../code/GPT_*.cpp' } + workspace 'EchoSrv' - configurations { 'prod', 'verbose', 'vsmall' } + configurations { 'prod', 'verbose', 'vsmall', 'gpt' } language 'C++' targetsuffix '_%{cfg.name}' project 'EchoSrv' kind 'ConsoleApp' - files { '../code/*.cpp' } filter { 'configurations:prod' } + files { filesTCP } defines { 'NDEBUG' } symbols 'Off' optimize 'Speed' filter { 'configurations:verbose' } + files { filesTCP } defines { 'DEBUG', 'VERBOSE' } symbols 'On' optimize 'Off' filter { 'configurations:vsmall' } + files { filesTCP } defines { 'DEBUG', 'VERBOSE', 'SELECT_MILLI_SECONDS=1000', @@ -45,6 +55,12 @@ workspace 'EchoSrv' symbols 'On' optimize 'Off' + filter { 'configurations:gpt' } + files { filesGPT } + defines { 'NDEBUG' } + symbols 'Off' + optimize 'Speed' + filter { 'action:vs*' } warnings 'high' buildoptions { '/std:c++17 /W4' } From 1627c0c18c44da72846e85a3a601266e15a28745 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:05:01 +0200 Subject: [PATCH 52/56] review rework; documentation --- .../ChatGPT_dialog.md => ChatGPT_dialog.md | 0 README.md | 67 +++++++++++++++- code/GPT_Srv.cpp | 8 ++ code/GPT_Srv.h | 10 +++ code/GPT_main.cpp | 7 ++ code/TCP_Srv_Base.h | 4 +- code/TCP_Srv_Echo.h | 2 +- make/build.sh | 18 ++++- make/howto_build.md | 80 ++----------------- solutions.md | 39 ++++----- 10 files changed, 138 insertions(+), 97 deletions(-) rename ChatGPT/ChatGPT_dialog.md => ChatGPT_dialog.md (100%) diff --git a/ChatGPT/ChatGPT_dialog.md b/ChatGPT_dialog.md similarity index 100% rename from ChatGPT/ChatGPT_dialog.md rename to ChatGPT_dialog.md diff --git a/README.md b/README.md index 4f0ae7e..75ae95c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,68 @@ # TCP_Srv +Yet another C++ TCP server implementation +## task +- implement a TCP server base class +- implement a sample derived class that echoes incoming data +- one implementation that builds with gcc / make and VS +## motivation +### see what AI can do +#### VS code with github copilot +I used the 30 days trial period to find out how effectively copilot enhances / speeds up development. +#### ChatGPT +Later in the development - just for curiosity - I asked ChatGPT for a solution. +### googeled solutions in the net +What I found googeling the issue wasn't really convincing. + +for instance +- too complicated +- not suitable for both gcc and VS +### training +- modern C++ +- python +- github CI +## results +### AI +#### github copilot +Github copilot made a mixed impression from extremely helpful to completely annoying. + +Sometimes it really worked: +- just type a word - comment or code +- copilot proposes just what was intended or even better +- just a TAB and code is written + +Sometimes it didn't +- proposals where completely unusable +- the occupation of TAB can confuse + +Copilot worked as good with python as with C++. + +Since it is AI - it sure will get better by the time. +#### ChatGPT +It took two questions to get a complete implementation for gcc, and a third one to get the VS version. Both versions were not 100 percent perfect but built and ran without modification. +- see [protocol of dialog](ChatGPT_dialog.md) + +Moreover ChatGPT was extremely helpful with any other _how to_ or other question. + +For instance I asked who has to resolve comments in a github peer review. + +And what I got was substantial explanation of the process. + +#### conclusion +Once one gets used to it github copilot enhances the development to a certain degree. + +ChatGPT is a boost. It will make google and sites like StackOverflow more or less redundant. + +(The price of this is a consumption of electric energy equivalent to a town with 150000 - yes: a hundred and fifty thousand - inhabitants.) + +Anyhow: AI has changed the approach to software development +- less work with implementation issues +- more concentration on issues like + - subject + - requirements + - collaboration + +## docs +- [different server solutions](solutions.md) +- [how to build](make/howto_build.md) +- [imlementations folder](code) -... \ No newline at end of file diff --git a/code/GPT_Srv.cpp b/code/GPT_Srv.cpp index 3452eb4..e46e239 100644 --- a/code/GPT_Srv.cpp +++ b/code/GPT_Srv.cpp @@ -1,3 +1,11 @@ +// ============================================================ +// code mainly from ChatGPT +// small modifications: +// - one code for both gcc and msvc +// - some type changes to avoid warnings +// - constness +// - using statements +// ============================================================ #include "GPT_Srv.h" #ifdef _WIN32 diff --git a/code/GPT_Srv.h b/code/GPT_Srv.h index 0d87c3f..0d5d29d 100644 --- a/code/GPT_Srv.h +++ b/code/GPT_Srv.h @@ -1,3 +1,13 @@ +// ============================================================ +// code mainly from ChatGPT +// small modifications: +// - one code for both gcc and msvc +// - some type changes to avoid warnings +// - removed unused headers +// - constness +// ============================================================ + +#pragma once #ifndef GPT_SRV_H #define GPT_SRV_H diff --git a/code/GPT_main.cpp b/code/GPT_main.cpp index 63fdb92..f4bcc86 100644 --- a/code/GPT_main.cpp +++ b/code/GPT_main.cpp @@ -1,3 +1,10 @@ +// ============================================================ +// code mainly from ChatGPT +// small modifications: +// - one code for both gcc and msvc +// - some type changes to avoid warnings +// - constness +// ============================================================ #include "GPT_Srv.h" #include diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index fc58700..a2e7a21 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -42,7 +42,6 @@ class TCP_Srv_Base void run(INT32 argc, const CONST_C_STRING* argv); protected: - inline TCP_Srv_Base() = default; // timeval seconds constexpr static UINT32 tmSec = SELECT_MILLI_SECONDS / 1000; // timeval microseconds @@ -51,6 +50,9 @@ class TCP_Srv_Base constexpr static UINT32 buffSize = READ_BUFFER_SIZE < 4 ? 4 : READ_BUFFER_SIZE; using Buffer = CHAR[buffSize]; + // can only be constructed by derived class + inline TCP_Srv_Base() = default; + // process received data // must be implemented by derived class virtual void process(const SOCKET clientSocket, Buffer buff, size_t size, UINT32 nr) = 0; diff --git a/code/TCP_Srv_Echo.h b/code/TCP_Srv_Echo.h index cc43971..da04243 100644 --- a/code/TCP_Srv_Echo.h +++ b/code/TCP_Srv_Echo.h @@ -1,5 +1,5 @@ // ============================================================ -// sample TCP echo server class +// sample TCP echo server class based on TCP_Srv_Base // ============================================================ // created by Manfred Sorgo diff --git a/make/build.sh b/make/build.sh index 0f1f8cb..9f94447 100644 --- a/make/build.sh +++ b/make/build.sh @@ -1,4 +1,8 @@ #!/bin/bash +# ============================================================ +# build and run script for gcc / make +# ============================================================ +# created by Manfred Sorgo # extract all configurations from make help getcfgs() @@ -66,16 +70,22 @@ if test ! -z $pre; then premake5 gmake2; fi echo building congigurations ... +# start build all build tasks in background +# save pids in array pids=() for config in $cfgs; do mkconfig $config & pids+=($!) done -ecode=0 +# wait for all build tasks to finish +# and evaluate return code +err=0 for pid in ${pids[*]}; do - if ! wait $pid; then ecode=1; fi + if ! wait $pid; then err=1; fi done +if test $err -ne 0; then exit 1; fi + if test -z $run; then builds=$(ls bin/* 2>/dev/null) if test ! -z "$builds"; then @@ -84,12 +94,12 @@ if test -z $run; then echo - $b done fi - exit $ecode; + exit 0; fi bin=$(ls bin/*_$run 2>/dev/null | head -n 1) if test -z $bin; then - echo "no binary for config $run" + echo "no binary for config '$run'" exit 1 fi diff --git a/make/howto_build.md b/make/howto_build.md index e692043..af7f3bd 100644 --- a/make/howto_build.md +++ b/make/howto_build.md @@ -3,66 +3,14 @@ ### preconditions - make - gcc -### make -- call make help to see available configurations -- call make clean with a configuration -- call make -j with a configuration - -````shell -TCP_Srv/make$> make help -Usage: make [config=name] [target] - -CONFIGURATIONS: - prod - verbose - vsmall - -TARGETS: - all (default) - clean - -TCP_Srv/make$> make config=vsmall clean -Cleaning EchoSrv - -TCP_Srv/make$> make -j config=vsmall -==== Building TCP_Srv_Echo (vsmall) ==== -Creating obj/gcc/vsmall -Creating bin -TCP_Srv_Base.cpp -TCP_Srv_Echo.cpp -TCP_Srv_Echo_main.cpp -Linking EchoSrv -```` ### use build.sh call help ````shell TCP_Srv/make$> ./build.sh -h -Usage: build.sh options [port] [locale] -options: --c clean ignored artifacts before --r run binary with [port] [locale] - with in prod, verbose, vsmall --p premake5 makefiles --h this help ```` -build (and run) e.g.: -- configuration _verbose_ -- port 8090 -- locale de_DE.UTF-8 +then call to build, e.g. ````shell -TCP_Srv/make$> ./build.sh -cr vsmall 8090 de_DE.UTF-8 -building congigurations ... -prod [ 5] -vsmall [ 6] -verbose [ 6] - -starting bin/EchoSrv_vsmall 8090 de_DE.UTF-8 - -locale : de_DE.UTF-8 -... -timeout: 1000 ms -buffer : 8 bytes -port : 8090 +TCP_Srv/make$> ./build.sh -c ```` ## Visual Studio build @@ -71,32 +19,20 @@ port : 8090 - Visual Studio ### 1) generate VS solution using premake5 -#### 1.1) find out suitable VS version using premake5 --help +- find out suitable VS version using premake5 --help ````shell -$> premake5 --help +TCP_Srv\make> premake5 --help Usage: premake5 [options] action [arguments] - +... ACTIONS vs2005 Generate Visual Studio 2005 project files - vs2008 Generate Visual Studio 2008 project files - vs2010 Generate Visual Studio 2010 project files - vs2012 Generate Visual Studio 2012 project files - vs2013 Generate Visual Studio 2013 project files - vs2015 Generate Visual Studio 2015 project files - vs2017 Generate Visual Studio 2017 project files - vs2019 Generate Visual Studio 2019 project files + ... vs2022 Generate Visual Studio 2022 project files ```` -#### 1.2) generate solution with your VS version -e.g. VS 2019 +- generate solution with your VS version, e.g. VS 2019 ````shell -TCP_Srv/make$> premake5 vs2019 -Building configurations... -Running action 'vs2019'... -Generated EchoSrv.sln... -Generated EchoSrv.vcxproj... -Done (42ms). +TCP_Srv\make> premake5 vs2019 ```` ### 2) build executables in VS - open solution (TCP_Srv/make/EchoSrv.sln) diff --git a/solutions.md b/solutions.md index 2131250..ef59779 100644 --- a/solutions.md +++ b/solutions.md @@ -11,7 +11,7 @@ listen() ______| | | -| accept() -> clientSocket, thread(clientSocket) +| accept() -> clientSocket --> thread(clientSocket) |______| ``` ### server with non blocking select and threads @@ -39,7 +39,7 @@ | | | | N Y | | | -| | accept() -> clientSocket, thread(clientSocket) +| | accept() -> clientSocket --> thread(clientSocket) | |_____| | | | other_tasks() @@ -48,24 +48,27 @@ ### server with blocking accept in separate thread (ChatGPT solution) ``` - | - |-----> thread - | | - | listenSocket - | | - | socket() - | | - | bind() - | | - | listen() - | ____| - | | | - | | accept() -> clientSocket, thread(clientSocket) - | |____| - | + | + |----> thread + | | + | listenSocket + | | + | socket() + | | + | bind() + | | + | listen() + | ____| + | | | + | | accept() -> clientSocket --> thread(clientSocket) + | |____| + | wait for input - | + | stop() + | +join client threads + | ``` ## python ### server with select and threads From 658841e1a635be381e33681db65e1675510ee5e9 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:23:17 +0200 Subject: [PATCH 53/56] reworked issue #2 --- make/howto_build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/howto_build.md b/make/howto_build.md index af7f3bd..74c67d1 100644 --- a/make/howto_build.md +++ b/make/howto_build.md @@ -36,7 +36,7 @@ TCP_Srv\make> premake5 vs2019 ```` ### 2) build executables in VS - open solution (TCP_Srv/make/EchoSrv.sln) -- select a configuration (see also header of premake5.lua) +- select a configuration (see also header of [premake5.lua](premake5.lua)) - build (and run) ### 3) VS keyboard shortcuts From c63c09c9ddd71a3b2d4142318ad055afd93b7d0c Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Mon, 8 Apr 2024 19:18:43 +0200 Subject: [PATCH 54/56] min mod --- make/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/build.sh b/make/build.sh index 9f94447..285e384 100644 --- a/make/build.sh +++ b/make/build.sh @@ -22,7 +22,7 @@ getcfgs() help() { - echo "Usage: $(basename $0) options [port] [locale]" + echo "Usage: $(basename $0) [options] [port] [locale]" echo "options:" echo "-c clean ignored artifacts before" echo "-r run binary with [port] [locale]" From d1acd632b97b7be4002d5a8d95fc5ec8ec627bd9 Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:12:59 +0200 Subject: [PATCH 55/56] added exec --- make/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/build.sh b/make/build.sh index 285e384..de28bd3 100644 --- a/make/build.sh +++ b/make/build.sh @@ -106,4 +106,4 @@ fi echo "" echo "starting $bin $*" echo "" -$bin $* +exec $bin $* From 5384dfb3e40c76ae8d85e4d1564d73d4866eab6c Mon Sep 17 00:00:00 2001 From: sorgom <110917257+sorgom@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:01:29 +0200 Subject: [PATCH 56/56] test mode --- .github/workflows/c-cpp.yml | 2 +- code/TCP_Srv_Base.cpp | 75 ++++++++++++++++++++----------------- code/TCP_Srv_Base.h | 6 ++- code/TCP_Srv_Echo_main.cpp | 4 +- 4 files changed, 47 insertions(+), 40 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index f6f693a..8565de4 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -21,4 +21,4 @@ jobs: - name: prepare build run: chmod 755 make/build.sh - name: build - run: make/build.sh -c + run: make/build.sh -cr prod test diff --git a/code/TCP_Srv_Base.cpp b/code/TCP_Srv_Base.cpp index 9aa1893..75f5fda 100644 --- a/code/TCP_Srv_Base.cpp +++ b/code/TCP_Srv_Base.cpp @@ -30,8 +30,9 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) if (argc > 1) { UINT16 port = defPort; - regex rxPort {"^\\d{2,5}$"}; - regex rxHelp {"^-[hH]$"}; + const regex rxPort {"^\\d{2,5}$"}; + const regex rxHelp {"^-[hH]$"}; + const regex rxTest {"^test$", std::regex_constants::icase}; bool cont = true; for (INT32 n = 1; cont and n < argc; ++n) { @@ -45,6 +46,7 @@ void TCP_Srv_Base::run(const INT32 argc, const CONST_C_STRING* const argv) for (INT32 n = 1; cont and n < argc; ++n) { if (regex_match(argv[n], rxPort)) port = static_cast(atoi(argv[n])); + else if (regex_match(argv[n], rxTest)) mTest = true; else cont = handleArg(argv[n]); } if (cont) run(port); @@ -113,49 +115,52 @@ void TCP_Srv_Base::run(const UINT16 port) displayThreads(); } - // select and other tasks loop - while (ok) + if (not mTest) { - - // select and accept loop - bool clients = false; - do + // select and other tasks loop + while (ok) { - clients = false; - // select - fd_set lset; - FD_ZERO(&lset); - FD_SET(listenSocket, &lset); - timeval tv { tmSec, tmMic}; - - if (select(listenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) - { - cerr << "ERR listen select" << endl; - ok = false; - } - - // accept to new client socket if listen socket is set - else if (FD_ISSET(listenSocket, &lset)) + // select and accept loop + bool clients = false; + do { - SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); - - if (clientSocket < 0) + clients = false; + // select + fd_set lset; + FD_ZERO(&lset); + FD_SET(listenSocket, &lset); + timeval tv { tmSec, tmMic}; + + if (select(listenSocket + 1, &lset, nullptr, nullptr, &tv) < 0) { - cerr << "ERR accept" << endl; + cerr << "ERR listen select" << endl; ok = false; } - // start thread with client socket - else + + + // accept to new client socket if listen socket is set + else if (FD_ISSET(listenSocket, &lset)) { - startThread(clientSocket); - clients = true; + SOCKET clientSocket = accept(listenSocket, nullptr, nullptr); + + if (clientSocket < 0) + { + cerr << "ERR accept" << endl; + ok = false; + } + // start thread with client socket + else + { + startThread(clientSocket); + clients = true; + } } - } - } while (clients); + } while (clients); - // other tasks - otherTasks(); + // other tasks + otherTasks(); + } } // only reached in case of error: clean up if (listenSocket >= 0) diff --git a/code/TCP_Srv_Base.h b/code/TCP_Srv_Base.h index a2e7a21..4d42f26 100644 --- a/code/TCP_Srv_Base.h +++ b/code/TCP_Srv_Base.h @@ -77,16 +77,18 @@ class TCP_Srv_Base private: // default port constexpr static UINT16 defPort = 8080; - // client thread method - void handleClient(SOCKET clientSocket, UINT32 nr); // thread count UINT32 mCnt = 0; // thread number UINT32 mNum = 0; // mutex for thread count and number std::mutex mMtxStat; + // just bind test + bool mTest = false; // start thread with increased thread number and count void startThread(SOCKET clientSocket); + // client thread method + void handleClient(SOCKET clientSocket, UINT32 nr); // decrease thread count, reset thread number when count is 0 void endOfThread(); // display current number of threads diff --git a/code/TCP_Srv_Echo_main.cpp b/code/TCP_Srv_Echo_main.cpp index 28247c1..9f039d4 100644 --- a/code/TCP_Srv_Echo_main.cpp +++ b/code/TCP_Srv_Echo_main.cpp @@ -8,6 +8,6 @@ int main(const INT32 argc, const CONST_C_STRING* const argv) { TCP_Srv_Echo srv; srv.run(argc, argv); - // only reached in case of failure - return 1; + // reached in case of test mode + return 0; }