├── .gitignore ├── configure.sh ├── configure.bat ├── scripts ├── CheckAtomicInt64Support.bat ├── CheckAtomicInt64Support.sh └── CheckAtomicInt64Support.cpp ├── Examples ├── http │ ├── Picture.jpg │ ├── Picture.png │ ├── page.html │ ├── 404.html │ ├── index.html │ └── HTTP_Server.cpp ├── UDP_Chat_Client.cpp ├── ParallelFor.cpp ├── TCP_Chat_Client.cpp ├── TCP_Chat_Server_SCUP.cpp ├── RAU_Chat_Server.cpp ├── UDP_Chat_Server.cpp ├── RAU_Chat_Client.cpp ├── TCP_Chat_Server.cpp ├── TCP_Chat_Server_SC.cpp ├── TCP_BlockingFileTransmitter_Server.cpp ├── TCP_BlockingFileTransmitter_Client.cpp ├── FT_Chat_Client.cpp └── FT_Chat_Server.cpp ├── EN_Subprocess.h ├── MakeRelease.sh ├── MakeRelease.bat ├── arduino ├── Readme.txt ├── EN_TCP_Client.h └── EN_TCP_Client.cpp ├── EN_ThreadBarrier.h ├── EN_SocketOptions.cpp ├── EN_Atomic_Int64.h ├── EN_FT_Client.h ├── EN_FT_Server.h ├── EN_BackgroundTimer.h ├── EN_FT_Client.cpp ├── EN_BackgroundTimer.cpp ├── EN_ThreadBarrier.cpp ├── EN_Subprocess_Lin.h ├── EN_Subprocess_Win.h ├── EN_Atomic_Int64.cpp ├── EN_FT_Server.cpp ├── EN_ThreadCrossWalk.cpp ├── experimental └── EN_HTTP_Server.h ├── EN_SocketOptions.h ├── EN_FileTransmissionStatus.h ├── EN_ThreadCrossWalk.h ├── EN_ThreadGate.cpp ├── EN_ParallelFor.h ├── EN_UDP_Client.cpp ├── EN_FileTransmissionStatus.cpp ├── EN_Logger.h ├── EN_RAU_Client.cpp ├── EN_UDP_Client.h ├── CMakeLists.txt ├── EN_TCP_Server_SC.h ├── EN_TCP_Client.cpp ├── EN_ThreadGate.h ├── EN_RAU_Client.h ├── EN_UDP_Server.h ├── EN_RAU_Server.cpp ├── EN_Subprocess_Lin.cpp ├── EN_Logger.cpp ├── EN_TCP_Client.h ├── EN_UDP_Server.cpp ├── README.md ├── EN_TCP_Server_SCUP.h ├── EN_Subprocess_Win.cpp ├── EN_RAU_Server.h └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | build 3 | scripts/*.test 4 | .vs 5 | .vscode -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | mkdir -p build 2 | cd build 3 | cmake .. 4 | cd .. -------------------------------------------------------------------------------- /configure.bat: -------------------------------------------------------------------------------- 1 | if Not Exist build mkdir build 2 | cd build 3 | cmake .. 4 | cd .. -------------------------------------------------------------------------------- /scripts/CheckAtomicInt64Support.bat: -------------------------------------------------------------------------------- 1 | g++ CheckAtomicInt64Support.cpp -o CheckAtomicInt64Support.test -------------------------------------------------------------------------------- /scripts/CheckAtomicInt64Support.sh: -------------------------------------------------------------------------------- 1 | g++ CheckAtomicInt64Support.cpp -o CheckAtomicInt64Support.test -------------------------------------------------------------------------------- /Examples/http/Picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrognor/Easy_Network/HEAD/Examples/http/Picture.jpg -------------------------------------------------------------------------------- /Examples/http/Picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrognor/Easy_Network/HEAD/Examples/http/Picture.png -------------------------------------------------------------------------------- /scripts/CheckAtomicInt64Support.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | std::atomic_int64_t t; 6 | } -------------------------------------------------------------------------------- /EN_Subprocess.h: -------------------------------------------------------------------------------- 1 | #if defined WIN32 || defined _WIN64 2 | #include "EN_Subprocess_Win.h" 3 | #else 4 | #include "EN_Subprocess_Lin.h" 5 | #endif -------------------------------------------------------------------------------- /MakeRelease.sh: -------------------------------------------------------------------------------- 1 | make clean 2 | make release -j 3 | 4 | mkdir -p bin/EN 5 | mkdir -p bin/EN/lib 6 | mkdir -p bin/EN/include 7 | 8 | cp *.h bin/EN/include 9 | cp bin/libEasyNetwork.a bin/EN/lib/libEasyNetwork.a -------------------------------------------------------------------------------- /Examples/http/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This is page

4 | 5 |
6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /Examples/http/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

404 Not Found

4 |

There's nothing here.

5 | 6 |
7 | 8 |
9 | 10 | -------------------------------------------------------------------------------- /MakeRelease.bat: -------------------------------------------------------------------------------- 1 | make clean 2 | make release -j%NUMBER_OF_PROCESSORS% 3 | 4 | if Not Exist bin\EN mkdir bin\EN 5 | if Not Exist bin\EN\lib mkdir bin\EN\lib 6 | if Not Exist bin\EN\include mkdir bin\EN\include 7 | 8 | cp *.h bin/EN/include 9 | cp bin/libEasyNetwork.a bin/EN/lib/libEasyNetwork.a -------------------------------------------------------------------------------- /arduino/Readme.txt: -------------------------------------------------------------------------------- 1 | This code is provided without any guarantees of operation. It is needed for the convenience of working with microcontrollers, but it is very likely that it will require code completion. At a minimum, you will have to change the header file 2 | Check #if defined(ESP8266) to compile code on your board 3 | In cpp file 2 algorithms to get data from server. First check esp32 algorithm and if it works use it. If esp32 algorithm dont work use esp8266 algorithm -------------------------------------------------------------------------------- /Examples/http/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This is index

4 | 5 |
6 | 7 |
8 | 9 |

Picture png

10 | 11 | 12 |

Picture jpg

13 | 14 | 15 |

Picture svg

16 | 17 | 18 | -------------------------------------------------------------------------------- /EN_ThreadBarrier.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace EN 9 | { 10 | class EN_ThreadBarrier 11 | { 12 | private: 13 | std::mutex Mtx; 14 | std::condition_variable Cv; 15 | std::atomic_int WaitingAmount; 16 | public: 17 | EN_ThreadBarrier(); 18 | 19 | void Wait(int amount); 20 | 21 | void WaitFor(int amount, std::chrono::seconds time); 22 | }; 23 | } -------------------------------------------------------------------------------- /EN_SocketOptions.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_SocketOptions.h" 2 | 3 | namespace EN 4 | { 5 | SocketOption::SocketOption() {} 6 | 7 | SocketOption::SocketOption(int level, int optionName, int optionValue) 8 | { 9 | Level = level; 10 | OptionName = optionName; 11 | OptionValue = optionValue; 12 | } 13 | 14 | PredefinedSocketOptions::PredefinedSocketOptions(std::vector levels, std::vector optionNames, std::vector optionValues) 15 | { 16 | Levels = levels; 17 | OptionNames = optionNames; 18 | OptionValues = optionValues; 19 | } 20 | } -------------------------------------------------------------------------------- /EN_Atomic_Int64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace EN 7 | { 8 | class EN_Atomic_Int64_T 9 | { 10 | private: 11 | std::int64_t Value; 12 | std::mutex Mtx; 13 | public: 14 | void store(int64_t value); 15 | const int64_t load(); 16 | 17 | void fetch_add(int64_t value); 18 | void fetch_sub(int64_t value); 19 | }; 20 | 21 | class EN_Atomic_Uint64_T 22 | { 23 | private: 24 | std::uint64_t Value; 25 | std::mutex Mtx; 26 | public: 27 | void store(uint64_t value); 28 | uint64_t load(); 29 | 30 | void fetch_add(uint64_t value); 31 | void fetch_sub(uint64_t value); 32 | }; 33 | } -------------------------------------------------------------------------------- /EN_FT_Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EN_TCP_Client.h" 4 | 5 | namespace EN 6 | { 7 | class EN_FT_EternalClient : public EN::EN_TCP_Client 8 | { 9 | protected: 10 | void OnConnect(); 11 | 12 | void ServerMessageHandler(std::string message); 13 | 14 | void OnDisconnect(); 15 | }; 16 | 17 | class EN_FT_Client : public EN::EN_TCP_Client 18 | { 19 | private: 20 | EN_FT_EternalClient EternalFTCient; 21 | public: 22 | bool Connect(std::string ipAddr = "127.0.0.1", int tcpPort = 1111, int ftPort = 1112); 23 | 24 | void Disconnect(); 25 | 26 | void SendTest(std::string test) 27 | { 28 | EternalFTCient.SendToServer(test); 29 | } 30 | }; 31 | } -------------------------------------------------------------------------------- /Examples/UDP_Chat_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_UDP_Client.h" 2 | 3 | class MyClient : public EN::EN_UDP_Client 4 | { 5 | public: 6 | MyClient() 7 | { 8 | // ServerIpAddress = "178.21.11.82"; Default set to localhost 9 | // ServerPort = to set port. Default port is 1111 10 | } 11 | 12 | virtual void ServerMessageHandler(std::string message) override 13 | { 14 | LOG(EN::LogLevels::Info, "Message: " + message); 15 | } 16 | 17 | }; 18 | 19 | 20 | int main() 21 | { 22 | MyClient A; 23 | // Start client 24 | A.Run(); 25 | 26 | std::string msg; 27 | while (true) 28 | { 29 | // Read line from standart input 30 | getline(std::cin, msg); 31 | 32 | // Exit from while loop 33 | if (msg == "exit()") 34 | break; 35 | 36 | A.SendToServer(msg); 37 | } 38 | 39 | // Stop cient 40 | A.Stop(); 41 | } 42 | -------------------------------------------------------------------------------- /EN_FT_Server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EN_TCP_Server.h" 4 | 5 | namespace EN 6 | { 7 | class EN_FT_Server_Eternal : public EN::EN_TCP_Server 8 | { 9 | public: 10 | void SetIpAndPort(std::string ipAddress, int port); 11 | 12 | virtual void OnClientConnected(EN_SOCKET clientSocket) override; 13 | 14 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) override; 15 | 16 | virtual void OnClientDisconnect(EN_SOCKET clientSocket) override; 17 | }; 18 | 19 | class EN_FT_Server : public EN::EN_TCP_Server 20 | { 21 | private: 22 | EN_FT_Server_Eternal FTServerEternal; 23 | int FTEternalServerPort = 1112; 24 | std::thread FTServerRunThread; 25 | public: 26 | EN_FT_Server(); 27 | 28 | void Run(); 29 | 30 | void Shutdown(); 31 | }; 32 | } -------------------------------------------------------------------------------- /EN_BackgroundTimer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace EN 9 | { 10 | class EN_BackgroundTimer 11 | { 12 | private: 13 | std::thread TimerThread; 14 | std::atomic_bool IsDestroy; 15 | std::atomic_int Code; 16 | std::condition_variable CondVar; 17 | std::chrono::microseconds SleepTime; 18 | std::mutex Mtx; 19 | public: 20 | EN_BackgroundTimer(); 21 | 22 | template 23 | void StartTimer(uint64_t timeToSleep) 24 | { 25 | Mtx.lock(); 26 | while (Code.load() == 0) 27 | CondVar.notify_one(); 28 | SleepTime = T(timeToSleep); 29 | Mtx.unlock(); 30 | } 31 | 32 | bool IsSleep(); 33 | 34 | ~EN_BackgroundTimer(); 35 | }; 36 | } -------------------------------------------------------------------------------- /EN_FT_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_FT_Client.h" 2 | 3 | namespace EN 4 | { 5 | void EN_FT_EternalClient::OnConnect() 6 | { 7 | LOG(EN::LogLevels::Info, "File transmitter client connected to server!"); 8 | } 9 | 10 | void EN_FT_EternalClient::ServerMessageHandler(std::string message) 11 | { 12 | LOG(EN::LogLevels::Info, "Message from file transmitter server" + message); 13 | } 14 | 15 | void EN_FT_EternalClient::OnDisconnect() 16 | { 17 | LOG(EN::LogLevels::Info, "File transmitter client disconnected from server!"); 18 | } 19 | 20 | bool EN_FT_Client::Connect(std::string ipAddr, int tcpPort, int ftPort) 21 | { 22 | if (!EN_TCP_Client::Connect(ipAddr, tcpPort)) 23 | return false; 24 | if (!EternalFTCient.Connect(ipAddr, ftPort)) 25 | { 26 | EN_TCP_Client::Disconnect(); 27 | return false; 28 | } 29 | return true; 30 | } 31 | 32 | void EN_FT_Client::Disconnect() 33 | { 34 | EN::EN_TCP_Client::Disconnect(); 35 | EternalFTCient.Disconnect(); 36 | } 37 | } -------------------------------------------------------------------------------- /arduino/EN_TCP_Client.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_Client_H 2 | #define TCP_Client_H 3 | 4 | #if defined(ESP8266) 5 | #include 6 | #elif defined(ESP32) 7 | #include 8 | #endif 9 | 10 | #include 11 | #include "lwip/sockets.h" 12 | 13 | class TCP_Client 14 | { 15 | protected: 16 | WiFiClient Client; 17 | public: 18 | bool ConnectToWIFI(String wifiName, String wifiPassword, int attemptsCount = 20); 19 | 20 | bool ConnectToServer(String serverIP, String serverPort, int attemptsCount = 20); 21 | bool SendToServer(const std::string& msg); 22 | 23 | // Blocking call. Wait new message and put it in msg. 24 | // Return false if server was disonnected, otherwise return true 25 | bool WaitMessage(std::string& msg); 26 | 27 | // Non-blocking call. If have new message put it in msg 28 | // Return true if it was new message and put it inside msg, otherwise return true 29 | bool GetMessage(std::string& msg); 30 | 31 | void DisconnectFromServer(); 32 | bool IsConnectedToServer(); 33 | }; 34 | 35 | #endif -------------------------------------------------------------------------------- /EN_BackgroundTimer.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_BackgroundTimer.h" 2 | 3 | namespace EN 4 | { 5 | EN_BackgroundTimer::EN_BackgroundTimer() 6 | { 7 | IsDestroy.store(false); 8 | Code.store(0); 9 | TimerThread = std::thread([this] () 10 | { 11 | std::mutex condVarMtx; 12 | std::unique_lock lk(condVarMtx); 13 | 14 | while (!IsDestroy.load()) 15 | { 16 | CondVar.wait(lk); 17 | Code.store(1); 18 | Mtx.lock(); 19 | Mtx.unlock(); 20 | CondVar.wait_for(lk, SleepTime); 21 | Code.store(0); 22 | } 23 | 24 | Code.store(2); 25 | }); 26 | } 27 | 28 | bool EN_BackgroundTimer::IsSleep() 29 | { 30 | return Code.load() == 1; 31 | } 32 | 33 | EN_BackgroundTimer::~EN_BackgroundTimer() 34 | { 35 | IsDestroy.store(true); 36 | 37 | while (Code.load() != 2) 38 | CondVar.notify_one(); 39 | 40 | TimerThread.join(); 41 | } 42 | } -------------------------------------------------------------------------------- /Examples/ParallelFor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "EN_Functions.h" 5 | 6 | int main() 7 | { 8 | std::vector TestVector = { 10, 1, 1, 1, 1 }; 9 | 10 | EN::EN_ParallelFor p; 11 | p(0, (int)TestVector.size(), 2, [TestVector](int n) 12 | { 13 | EN::Delay(TestVector[n] * 1000); 14 | std::cout << "Lambda thread: " << std::this_thread::get_id() << " Iteration: " << n << std::endl; 15 | }); 16 | 17 | p(TestVector.begin(), TestVector.end(), 2, [](std::vector::iterator n) 18 | { 19 | EN::Delay(*n * 1000); 20 | std::cout << "Lambda iterator thread: " << std::this_thread::get_id() << " Element: " << *n << std::endl; 21 | }); 22 | 23 | std::set TestSet = { 10, 3, 1, 2, 7 }; 24 | p(TestSet.begin(), TestSet.end(), 2, [](std::set::iterator n) 25 | { 26 | EN::Delay(*n * 1000); 27 | std::cout << "Lambda iterator set thread: " << std::this_thread::get_id() << " Element: " << *n << std::endl; 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /EN_ThreadBarrier.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_ThreadBarrier.h" 2 | 3 | namespace EN 4 | { 5 | EN_ThreadBarrier::EN_ThreadBarrier() { WaitingAmount.store(0); } 6 | 7 | void EN_ThreadBarrier::Wait(int amount) 8 | { 9 | std::mutex sleepMtx; 10 | std::unique_lock lk(sleepMtx); 11 | 12 | WaitingAmount.fetch_add(1); 13 | 14 | if (WaitingAmount.compare_exchange_strong(amount, amount - 1)) 15 | { 16 | while (WaitingAmount.load() != 0) Cv.notify_all(); 17 | } 18 | else 19 | { 20 | Cv.wait(lk); 21 | WaitingAmount.fetch_sub(1); 22 | } 23 | } 24 | 25 | void EN_ThreadBarrier::WaitFor(int amount, std::chrono::seconds time) 26 | { 27 | std::mutex sleepMtx; 28 | std::unique_lock lk(sleepMtx); 29 | 30 | WaitingAmount.fetch_add(1); 31 | 32 | if (WaitingAmount.compare_exchange_strong(amount, amount - 1)) 33 | { 34 | while (WaitingAmount.load() != 0) Cv.notify_all(); 35 | } 36 | else 37 | { 38 | Cv.wait_for(lk, time); 39 | WaitingAmount.fetch_sub(1); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /EN_Subprocess_Lin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "EN_Atomic_Int64.h" 13 | 14 | namespace EN 15 | { 16 | class Subprocess 17 | { 18 | private: 19 | int PipeToChild[2]; 20 | int PipeFromChild[2]; 21 | pid_t Pid; 22 | std::queue MessagesQueue; 23 | std::thread Th; 24 | EN::EN_Atomic_Uint64_T DataCounter; 25 | std::condition_variable Cv; 26 | std::mutex Mtx; 27 | std::atomic_bool IsProcessEnded; 28 | 29 | public: 30 | void Launch(std::string programmName); 31 | 32 | void SendData(std::string data, bool isRequiredNewLineSymbol = true); 33 | 34 | std::string GetData(bool isRemoveNewLineSymbols = true); 35 | 36 | std::string WaitData(bool isRemoveNewLineSymbols = true); 37 | 38 | void StopProcess(std::string commandToSendToProcessToStop = "", bool isRequiredNewLineSymbol = true); 39 | 40 | bool IsData(); 41 | 42 | bool GetIsProcessEnded(); 43 | 44 | ~Subprocess(); 45 | }; 46 | } -------------------------------------------------------------------------------- /EN_Subprocess_Win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "EN_Atomic_Int64.h" 13 | 14 | namespace EN 15 | { 16 | class Subprocess 17 | { 18 | private: 19 | HANDLE pipeToChild[2] = { NULL, NULL }; 20 | HANDLE pipeFromChild[2] = { NULL, NULL }; 21 | PROCESS_INFORMATION ProcInfo; 22 | std::queue MessagesQueue; 23 | std::thread Th; 24 | EN::EN_Atomic_Uint64_T DataCounter; 25 | std::condition_variable Cv; 26 | std::mutex Mtx; 27 | std::atomic_bool IsProcessEnded; 28 | 29 | public: 30 | void Launch(std::string programmName); 31 | 32 | void SendData(std::string data, bool isRequiredNewLineSymbol = true); 33 | 34 | std::string GetData(bool isRemoveNewLineSymbols = true); 35 | 36 | std::string WaitData(bool isRemoveNewLineSymbols = true); 37 | 38 | void StopProcess(std::string commandToSendToProcessToStop = "", bool isRequiredNewLineSymbol = true); 39 | 40 | bool IsData(); 41 | 42 | bool GetIsProcessEnded(); 43 | 44 | ~Subprocess(); 45 | }; 46 | } -------------------------------------------------------------------------------- /Examples/http/HTTP_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "../../experimental/EN_HTTP_Server.h" 2 | 3 | class MyHttpServer : public EN::EN_HTTP_Server 4 | { 5 | // Handle file request parametrs 6 | void GetUrlParamsHandler(const std::string& urlParams) override {} 7 | 8 | // Gets post or get requests 9 | void HTTPRequestHandler(EN_SOCKET clientSocket, std::map parsedRequestMap, std::string requestHeader, std::string requestBody) 10 | { 11 | std::cout << requestBody << std::endl; 12 | SendToClient(clientSocket, "HTTP/1.1 200 OK\r\ncontent-length: 0\r\nconnection: closed\r\n\r\n"); 13 | } 14 | }; 15 | 16 | int main() 17 | { 18 | // MyServer A; 19 | // A.Run(); 20 | 21 | 22 | // Uncomment this code to make server standart console input. 23 | // Using this you can write logic to kick clients or shutdown server 24 | 25 | MyHttpServer A; 26 | 27 | std::thread th([&A]() 28 | { 29 | try 30 | { 31 | A.Run(); 32 | } 33 | catch (std::runtime_error& err) 34 | { 35 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 36 | } 37 | }); 38 | 39 | std::string message; 40 | 41 | while (true) 42 | { 43 | getline(std::cin, message); 44 | 45 | if (message == "f") 46 | { 47 | A.Shutdown(); 48 | break; 49 | } 50 | 51 | A.MulticastSend(message); 52 | } 53 | 54 | th.join(); 55 | } 56 | -------------------------------------------------------------------------------- /EN_Atomic_Int64.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_Atomic_Int64.h" 2 | 3 | namespace EN 4 | { 5 | void EN_Atomic_Int64_T::store(int64_t value) 6 | { 7 | Mtx.lock(); 8 | Value = value; 9 | Mtx.unlock(); 10 | } 11 | 12 | const int64_t EN_Atomic_Int64_T::load() 13 | { 14 | Mtx.lock(); 15 | int64_t value = Value; 16 | Mtx.unlock(); 17 | return value; 18 | } 19 | 20 | void EN_Atomic_Int64_T::fetch_add(int64_t value) 21 | { 22 | Mtx.lock(); 23 | Value += value; 24 | Mtx.unlock(); 25 | } 26 | 27 | void EN_Atomic_Int64_T::fetch_sub(int64_t value) 28 | { 29 | Mtx.lock(); 30 | Value -= value; 31 | Mtx.unlock(); 32 | } 33 | 34 | void EN_Atomic_Uint64_T::store(uint64_t value) 35 | { 36 | Mtx.lock(); 37 | Value = value; 38 | Mtx.unlock(); 39 | } 40 | 41 | uint64_t EN_Atomic_Uint64_T::load() 42 | { 43 | Mtx.lock(); 44 | uint64_t value = Value; 45 | Mtx.unlock(); 46 | return value; 47 | } 48 | 49 | void EN_Atomic_Uint64_T::fetch_add(uint64_t value) 50 | { 51 | Mtx.lock(); 52 | Value += value; 53 | Mtx.unlock(); 54 | } 55 | 56 | void EN_Atomic_Uint64_T::fetch_sub(uint64_t value) 57 | { 58 | Mtx.lock(); 59 | Value -= value; 60 | Mtx.unlock(); 61 | } 62 | } -------------------------------------------------------------------------------- /EN_FT_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_FT_Server.h" 2 | 3 | namespace EN 4 | { 5 | void EN_FT_Server_Eternal::SetIpAndPort(std::string ipAddress, int port) 6 | { 7 | IpAddress = ipAddress; 8 | Port = port; 9 | } 10 | 11 | void EN_FT_Server_Eternal::OnClientConnected(EN_SOCKET clientSocket) 12 | { 13 | LOG(LogLevels::Info, "FT eternal client connected. Socket descriptor: " + std::to_string(clientSocket)); 14 | } 15 | 16 | void EN_FT_Server_Eternal::ClientMessageHandler(EN_SOCKET clientSocket, std::string message) 17 | { 18 | LOG(LogLevels::Info, "Message from FT eternal client. Socket descriptor: " + std::to_string(clientSocket) + " " + message); 19 | } 20 | 21 | void EN_FT_Server_Eternal::OnClientDisconnect(EN_SOCKET clientSocket) 22 | { 23 | LOG(LogLevels::Info, "FT eternal client disconnected. Socket descriptor: " + std::to_string(clientSocket)); 24 | } 25 | 26 | EN_FT_Server::EN_FT_Server() 27 | { 28 | FTServerEternal.SetIpAndPort(IpAddress, FTEternalServerPort); 29 | } 30 | 31 | void EN_FT_Server::Run() 32 | { 33 | FTServerRunThread = std::thread ([&]() 34 | { 35 | FTServerEternal.Run(); 36 | }); 37 | 38 | EN_TCP_Server::Run(); 39 | } 40 | 41 | void EN_FT_Server::Shutdown() 42 | { 43 | FTServerEternal.Shutdown(); 44 | EN_TCP_Server::Shutdown(); 45 | FTServerRunThread.join(); 46 | } 47 | } -------------------------------------------------------------------------------- /EN_ThreadCrossWalk.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_ThreadCrossWalk.h" 2 | 3 | namespace EN 4 | { 5 | EN_ThreadCrossWalk::EN_ThreadCrossWalk() 6 | { 7 | RoadCounter.store(0); 8 | IsCondVarWaiting.store(false); 9 | } 10 | 11 | /// If the PedestrianStartCrossRoad method was called before, 12 | /// then this method will wait until the PedestrianStopCrossRoad method is called 13 | void EN_ThreadCrossWalk::CarStartCrossRoad() 14 | { 15 | Mtx.lock(); 16 | RoadMtx.lock(); 17 | RoadCounter.fetch_add(1); 18 | RoadMtx.unlock(); 19 | Mtx.unlock(); 20 | } 21 | 22 | void EN_ThreadCrossWalk::CarStopCrossRoad() 23 | { 24 | RoadMtx.lock(); 25 | RoadCounter.fetch_sub(1); 26 | 27 | if (RoadCounter.load() == 0) 28 | while (IsCondVarWaiting.load()) Cv.notify_all(); 29 | 30 | RoadMtx.unlock(); 31 | } 32 | 33 | /// If the CarStartCrossRoad methods were called before, 34 | /// then this method will wait until the last of the CarStopCrossRoad methods is called 35 | void EN_ThreadCrossWalk::PedestrianStartCrossRoad() 36 | { 37 | std::mutex mtx; 38 | std::unique_lock lk(mtx); 39 | 40 | Mtx.lock(); 41 | 42 | IsCondVarWaiting.store(true); 43 | 44 | if (RoadCounter > 0) Cv.wait(lk); 45 | 46 | IsCondVarWaiting.store(false); 47 | } 48 | 49 | void EN_ThreadCrossWalk::PedestrianStopCrossRoad() 50 | { 51 | Mtx.unlock(); 52 | } 53 | } -------------------------------------------------------------------------------- /experimental/EN_HTTP_Server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../EN_TCP_Server.h" 4 | #include "../EN_Functions.h" 5 | 6 | #include 7 | 8 | #define CHUNK_SIZE 4096 9 | 10 | namespace EN 11 | { 12 | bool HTTP_Recv(EN_SOCKET sock, std::string& message); 13 | 14 | bool HTTP_Send(EN_SOCKET sock, const std::string& message); 15 | 16 | void ParseHTTPRequest(const std::string& request, std::map& parsedRequestMap, std::string& requestHeader); 17 | 18 | class EN_HTTP_Server : public EN_TCP_Server 19 | { 20 | private: 21 | std::string WebFilesPath = ""; 22 | 23 | // Function to send responce with file to client 24 | void SendResponce(const EN_SOCKET& socket, const std::string& responce, const std::string& fileName); 25 | 26 | public: 27 | EN_HTTP_Server(); 28 | 29 | void OnClientConnected(EN_SOCKET clientSocket); 30 | 31 | void ClientMessageHandler(EN_SOCKET clientSocket, std::string message); 32 | 33 | void OnClientDisconnect(EN_SOCKET clientSocket); 34 | 35 | // Function to set custom path with web server files. By default set to same path as server executable. 36 | // Dont forget to specify last path symbol "\\" (only one) on windows and "/" on linux 37 | void SetWebFilesPath(std::string path); 38 | 39 | // Function to handle url params in GET request. 40 | virtual void GetUrlParamsHandler(const std::string& urlParams) = 0; 41 | 42 | // Function to handle requests from client 43 | virtual void HTTPRequestHandler(EN_SOCKET clientSocket, std::map parsedRequestMap, std::string requestHeader, std::string requestBody) = 0; 44 | }; 45 | } -------------------------------------------------------------------------------- /Examples/TCP_Chat_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "../EN_TCP_Client.h" 2 | 3 | 4 | class MyClient : public EN::EN_TCP_Client 5 | { 6 | public: 7 | MyClient() 8 | { 9 | // IsRunMessageHadlerThread = true; // Variable to disable starting thread with message handler 10 | } 11 | 12 | // A function to be defined by the user. It is used for logic after connection 13 | virtual void OnConnect() override 14 | { 15 | LOG(EN::LogLevels::Info, "Server connected."); 16 | } 17 | 18 | // A function to be defined by the user. It is used to process incoming messages from the server 19 | virtual void ServerMessageHandler(std::string message) override 20 | { 21 | LOG(EN::LogLevels::Info, message); 22 | } 23 | 24 | // A function to be defined by the user. Performed after disconnected from the server 25 | virtual void OnDisconnect() override 26 | { 27 | LOG(EN::LogLevels::Info, "Server disconnected."); 28 | } 29 | }; 30 | 31 | 32 | int main() 33 | { 34 | MyClient A; 35 | 36 | // Check if connection success 37 | if (A.Connect() == false) 38 | { 39 | LOG(EN::LogLevels::Info, "Failed to connect"); 40 | return 0; 41 | } 42 | 43 | std::string message; 44 | 45 | while (true) 46 | { 47 | // Get line from standart input 48 | getline(std::cin, message); 49 | 50 | // Stop while loop 51 | if (message == "f") 52 | break; 53 | 54 | // Disconnect to client 55 | if (message == "d") 56 | { 57 | A.Disconnect(); 58 | continue; 59 | } 60 | 61 | // Reconnect to client 62 | if (message == "r") 63 | A.Connect(); 64 | 65 | // Check if we still connected 66 | if (A.IsConnected()) 67 | A.SendToServer(message); 68 | else 69 | break; 70 | } 71 | 72 | // Disconnect client from server if still connected 73 | if (A.IsConnected()) 74 | A.Disconnect(); 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /Examples/TCP_Chat_Server_SCUP.cpp: -------------------------------------------------------------------------------- 1 | #include "../EN_TCP_Server_SCUP.h" 2 | 3 | // Class to save session context. 4 | struct Session 5 | { 6 | int counter = 0; 7 | }; 8 | 9 | class TCP_Server_SCUP : public EN::EN_TCP_Server_SCUP 10 | { 11 | protected: 12 | virtual void OnClientConnected(EN_SOCKET clientSocket, EN::TCP_SessionContext_SCUP& sessionContext) 13 | { 14 | ++sessionContext->counter; 15 | LOG(EN::LogLevels::Info, "SCUP client connected"); 16 | } 17 | 18 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message, EN::TCP_SessionContext_SCUP& sessionContext) 19 | { 20 | LOG(EN::LogLevels::Info, "SCUP client send message: " + message + " Message counter: " + std::to_string(sessionContext->counter)); 21 | ++sessionContext->counter; 22 | } 23 | 24 | virtual void OnClientDisconnect(EN_SOCKET clientSocket, EN::TCP_SessionContext_SCUP& sessionContext) 25 | { 26 | LOG(EN::LogLevels::Info, "SCUP client disconnected"); 27 | } 28 | public: 29 | TCP_Server_SCUP() 30 | { 31 | // SessionContext.TCP_Recv = ; 32 | } 33 | }; 34 | 35 | int main() 36 | { 37 | TCP_Server_SCUP A; 38 | start: 39 | 40 | std::thread th([&A]() 41 | { 42 | try 43 | { 44 | A.Run(); 45 | } 46 | catch (std::runtime_error& err) 47 | { 48 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 49 | LOG(EN::LogLevels::Info, "Enter f to end programm"); 50 | } 51 | }); 52 | 53 | std::string message; 54 | 55 | while (true) 56 | { 57 | getline(std::cin, message); 58 | 59 | if (message == "f") 60 | { 61 | A.Shutdown(); 62 | break; 63 | } 64 | 65 | if (message == "r") 66 | { 67 | th.join(); 68 | goto start; 69 | } 70 | } 71 | 72 | th.join(); 73 | } -------------------------------------------------------------------------------- /EN_SocketOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined WIN32 || defined _WIN64 4 | 5 | #include 6 | #include 7 | 8 | #ifndef TCP_KEEPIDLE 9 | #define TCP_KEEPIDLE 3 10 | #endif 11 | 12 | #ifndef TCP_KEEPCNT 13 | #define TCP_KEEPCNT 16 14 | #endif 15 | 16 | #ifndef TCP_KEEPINTVL 17 | #define TCP_KEEPINTVL 17 18 | #endif 19 | 20 | typedef SOCKET EN_SOCKET; 21 | 22 | #else 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | typedef int EN_SOCKET; 30 | #define INVALID_SOCKET -1 31 | #define SOCKET_ERROR -1 32 | 33 | #endif 34 | 35 | #define EN_TCP_KEEPALIVE EN::PredefinedSocketOptions({SOL_SOCKET, IPPROTO_TCP, IPPROTO_TCP, IPPROTO_TCP}, {SO_KEEPALIVE, TCP_KEEPIDLE, TCP_KEEPCNT, TCP_KEEPINTVL}, {1, 1, 1, 1}) 36 | #define EN_TCP_NODELAY EN::PredefinedSocketOptions({IPPROTO_TCP}, {TCP_NODELAY}, {1}) 37 | #define EN_TCP_REUSEADDR EN::PredefinedSocketOptions({SOL_SOCKET}, {SO_REUSEADDR}, {1}) 38 | 39 | #include 40 | 41 | namespace EN 42 | { 43 | struct SocketOption 44 | { 45 | SocketOption(); 46 | SocketOption(int level, int optionName, int optionValue); 47 | int Level; 48 | int OptionName; 49 | int OptionValue; 50 | }; 51 | 52 | /** 53 | \brief This structure is needed to predefine some convenient socket operation modes. For example, keep alive for convenient work with microcontrollers. 54 | */ 55 | struct PredefinedSocketOptions 56 | { 57 | std::vector Levels; 58 | std::vector OptionNames; 59 | std::vector OptionValues; 60 | 61 | PredefinedSocketOptions(std::vector levels, std::vector optionNames, std::vector optionValues); 62 | }; 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /Examples/RAU_Chat_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_RAU_Server.h" 2 | 3 | class RAU_Server : public EN::EN_RAU_Server 4 | { 5 | public: 6 | RAU_Server() 7 | { 8 | // IpAddress = "192.168.1.73"; Default set to localhost 9 | // Port = to set port. Default port is 1111 10 | // MaxUnreliableMessageSize = 512; Default set to 64 11 | // You have to set synchronizied it with client 12 | } 13 | 14 | void OnClientConnected(size_t ClientID) 15 | { 16 | std::cout << "Client connected! Id: " << ClientID << std::endl; 17 | SendToClient(ClientID, "Welcome. You are connected to server."); 18 | SendToClient(ClientID, "Reliable", true); 19 | SendToClient(ClientID, "Unreliable", false); 20 | } 21 | 22 | void ClientMessageHandler(std::string message, size_t ClientID) 23 | { 24 | // Important. This function is run in a separate thread. 25 | // If you want to write data to class variables, you should use mutexes or other algorithms for thread-safe code. 26 | std::cout << "msg: " << message << std::endl; 27 | 28 | // Disconnect client 29 | if (message == "TCP d" || message == "UDP d") 30 | DisconnectClient(ClientID); 31 | 32 | // Shutdown server 33 | if (message == "TCP F" || message == "UDP F") 34 | Shutdown(); 35 | 36 | for (size_t j = 0; j < GetConnectionsCount(); j++) 37 | { 38 | if(message.find("TCP") == 0) 39 | SendToClient(j, message); 40 | else SendToClient(j, message, false); 41 | } 42 | } 43 | 44 | void OnClientDisconnect(size_t ClientID) 45 | { 46 | std::cout << "Client disconnected! ID: " << ClientID << std::endl; 47 | } 48 | }; 49 | 50 | int main() 51 | { 52 | RAU_Server A; 53 | // Start server 54 | try 55 | { 56 | A.Run(); 57 | } 58 | catch (std::runtime_error& err) 59 | { 60 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 61 | } 62 | } -------------------------------------------------------------------------------- /EN_FileTransmissionStatus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "EN_Atomic_Int64.h" 7 | 8 | #ifdef NATIVE_ATOMIC_INT64_NOT_SUPPORTED 9 | typedef EN::EN_Atomic_Uint64_T AtomicUint64; 10 | #else 11 | typedef std::atomic_uint64_t AtomicUint64; 12 | #endif 13 | 14 | namespace EN 15 | { 16 | class EN_FileTransmissionStatus 17 | { 18 | private: 19 | AtomicUint64 TransferedBytes; 20 | AtomicUint64 FileSize; 21 | AtomicUint64 TransmissionSpeed; 22 | AtomicUint64 TransmissionEta; 23 | 24 | bool IsSetProgressFunction = false; 25 | std::function ProgressFunction; 26 | 27 | std::atomic_bool IsTransmissionEnded; 28 | std::atomic_bool IsTransmissionSucceed; 29 | public: 30 | EN_FileTransmissionStatus(); 31 | 32 | EN_FileTransmissionStatus(EN::EN_FileTransmissionStatus& status); 33 | 34 | void SetTransferedBytes(uint64_t transferedBytes); 35 | uint64_t GetThisSessionTransferedBytes(); 36 | 37 | void SetFileSize(uint64_t fileSize); 38 | uint64_t GetFileSize(); 39 | 40 | void SetTransmissionSpeed(uint64_t transmissionSpeed); 41 | uint64_t GetTransmissionSpeed(); 42 | 43 | void SetTransmissionEta(uint64_t transmissionEta); 44 | uint64_t GetTransmissionEta(); 45 | 46 | void SetIsTransmissionEnded(bool isTransmissionEnded); 47 | bool GetIsTransmissionEnded(); 48 | 49 | void SetIsTransmissionSucceed(bool isTransmissionSucceed); 50 | bool GetIsTransmissionSucceed(); 51 | 52 | bool GetIsSetProgressFunction(); 53 | void SetProgressFunction(std::function progressFunction); 54 | void InvokeProgressFunction(); 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /Examples/UDP_Chat_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_UDP_Server.h" 2 | 3 | class MyServer : public EN::EN_UDP_Server 4 | { 5 | public: 6 | MyServer() 7 | { 8 | // IpAddress = "192.168.1.64"; Default set to localhost 9 | // Port = to set port. Default port is 1111 10 | // You have to set synchronizied it with client 11 | // ServerBuferType = EN::Queue; Can be queue or stack 12 | // ThreadAmount = 1; Shows how many threads will process incoming messages. Default set to 2 13 | // MaxStackBuffSize = 4; Shows the maximum size of the incoming message stack. 14 | // Work only if ServerBuferType == EN::Stack 15 | } 16 | 17 | // Third parametr in milliseconds 18 | virtual void ClientMessageHandler(std::string message, std::string ClientIpAddress, long long TimeSincePackageArrived) override 19 | { 20 | // Checking how much time has passed since the arrival of the message 21 | if (TimeSincePackageArrived > 700) 22 | return; 23 | 24 | // Shutdown server 25 | if (message == "f") 26 | Shutdown(); 27 | 28 | LOG(EN::LogLevels::Info, "From: " + ClientIpAddress + " Message: " + message); 29 | 30 | SendToClient(ClientIpAddress, message); 31 | } 32 | 33 | // Function work between putting message in buffer. Return true if you want to put message in buffer 34 | virtual bool InstantClientMessageHandler(std::string message, std::string ClientIpAddress, long long TimeWhenPackageArrived) override 35 | { 36 | LOG(EN::LogLevels::Info, "Instant client message hadler. From: " + ClientIpAddress + " Message: " + message); 37 | if (message == "false") 38 | return false; 39 | return true; 40 | } 41 | }; 42 | 43 | 44 | int main() 45 | { 46 | MyServer A; 47 | // Start server 48 | try 49 | { 50 | A.Run(); 51 | } 52 | catch (std::runtime_error& err) 53 | { 54 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Examples/RAU_Chat_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_RAU_Client.h" 2 | 3 | class MyClient : public EN::EN_RAU_Client 4 | { 5 | public: 6 | MyClient() 7 | { 8 | // MaxUnreliableMessageSize = 512; Default set to 64 9 | // You have to set synchronizied it with client 10 | } 11 | 12 | // A function to be defined by the user. It is used for logic after connection 13 | void OnConnect() 14 | { 15 | SendToServer("Hello"); 16 | } 17 | 18 | // A function to be defined by the user. It is used to process incoming messages from the server 19 | void ServerMessageHandler(std::string message) 20 | { 21 | std::cout << message << std::endl; 22 | } 23 | 24 | // A function to be defined by the user. Performed after disconnected from the server 25 | void OnDisconnect() 26 | { 27 | std::cout << "Server disconnected." << std::endl; 28 | } 29 | }; 30 | 31 | 32 | 33 | int main() 34 | { 35 | MyClient A; 36 | 37 | // Connect to server 38 | if (A.Connect() == false) 39 | { 40 | std::cout << "Failed to connect" << std::endl; 41 | return 0; 42 | } 43 | 44 | std::string message; 45 | 46 | // Bool variable to change message type 47 | bool IsTCP = true; 48 | 49 | while (true) 50 | { 51 | // Read line from standart input 52 | getline(std::cin, message); 53 | 54 | // Break from while loop 55 | if (message == "f") 56 | break; 57 | 58 | // Check if we still connected 59 | if (A.IsConnected()) 60 | { 61 | // Reliable send 62 | if (IsTCP) 63 | { 64 | A.SendToServer("TCP " + message, true); 65 | IsTCP = false; 66 | } 67 | // Unreliable send 68 | else 69 | { 70 | A.SendToServer("UDP " + message, false); 71 | IsTCP = true; 72 | } 73 | } 74 | // Break from while loop in case of server disconnect 75 | else 76 | { 77 | bool IsSuccessConnection = false; 78 | for (int i = 0; i < 25; i++) 79 | { 80 | std::cout << "Attempt to reconnect. Attempt: " << i << std::endl; 81 | if (A.Connect()) 82 | { 83 | IsSuccessConnection = true; 84 | break; 85 | } 86 | EN::Delay(20); 87 | } 88 | if (!IsSuccessConnection) 89 | break; 90 | } 91 | } 92 | 93 | // Disconnect from server if still connected 94 | if (A.IsConnected()) 95 | A.Disconnect(); 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /EN_ThreadCrossWalk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace EN 8 | { 9 | /** 10 | \brief A class for synchronizing threads 11 | 12 | The work of this class can be represented using a pedestrian crossing. 13 | For simplicity, you can imagine the work of this class on a two-lane road, 14 | but it can work with any number of roads. 15 | Cars can drive on two lanes of the same road completely independently of each other. 16 | At the same time, there is a pedestrian crossing on the road, and when a pedestrian approaches it, 17 | cars will no longer be able to enter the road, 18 | the pedestrian will wait until all the cars that were on the road when he came will finish their movement. 19 | After that, he will cross the road and cars will be able to move on the road again. 20 | Now let's move on to threads. You have a lot of threads that can run in parallel, 21 | i.e. they don't have to be synchronized with each other. 22 | And there is one thread that should only work when all other threads are not working. 23 | For example, when working with some container. For simplicity, 24 | let's imagine that we are working with a vector in which there are 2 elements. 25 | 2 threads independently work with different elements of the vector. 26 | At the same time, you want to change the size of the vector. This class can help you with this. 27 | Working with reading and writing to vector cells is machines, and resizing a vector is a pedestrian 28 | */ 29 | class EN_ThreadCrossWalk 30 | { 31 | private: 32 | std::mutex Mtx, RoadMtx; 33 | std::atomic_int RoadCounter; 34 | std::atomic_bool IsCondVarWaiting; 35 | std::condition_variable Cv; 36 | public: 37 | EN_ThreadCrossWalk(); 38 | 39 | /// If the PedestrianStartCrossRoad method was called before, 40 | /// then this method will wait until the PedestrianStopCrossRoad method is called 41 | void CarStartCrossRoad(); 42 | 43 | void CarStopCrossRoad(); 44 | 45 | /// If the CarStartCrossRoad methods were called before, 46 | /// then this method will wait until the last of the CarStopCrossRoad methods is called 47 | void PedestrianStartCrossRoad(); 48 | 49 | void PedestrianStopCrossRoad(); 50 | }; 51 | } -------------------------------------------------------------------------------- /Examples/TCP_Chat_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_TCP_Server.h" 2 | 3 | class MyServer : public EN::EN_TCP_Server 4 | { 5 | public: 6 | MyServer() 7 | { 8 | // IpAddress = "192.168.1.64"; // Default set to localhost. Read description for this variable before use it. 9 | // Port = to set port. Default port is 1111 10 | } 11 | 12 | virtual void OnClientConnected(EN_SOCKET clientSocket) override 13 | { 14 | LOG(EN::LogLevels::Info, "Client connected! Socket descriptor: " + std::to_string(clientSocket)); 15 | SendToClient(clientSocket, "Welcome. You are connected to server."); 16 | } 17 | 18 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) override 19 | { 20 | // Important. This function is run in a separate thread. 21 | // If you want to write data to class variables, you should use mutexes or other algorithms for thread-safe code. 22 | LOG(EN::LogLevels::Info, "From: " + std::to_string(clientSocket) + " Message: " + message); 23 | 24 | // Disconnect client 25 | if (message == "d") 26 | DisconnectClient(clientSocket); 27 | 28 | // Shutdown server 29 | if (message == "F") 30 | { 31 | LOG(EN::LogLevels::Info, "Server was shutdown by client. Enter f to end programm or r to restart server"); 32 | Shutdown(); 33 | } 34 | 35 | // Send incoming message to all different clients 36 | // Blocking the client sockets list is necessary because while the cycle is going through, 37 | // one of the clients may disconnect and UB will occur. If the client disconnects during the lock, 38 | // its socket will not be released until the unlock occurs, and the Send method for this client returns false 39 | LockClientSockets(); 40 | for (EN_SOCKET sock : ClientSockets) 41 | { 42 | if (sock != clientSocket) 43 | SendToClient(sock, message); 44 | } 45 | UnlockClientSockets(); 46 | } 47 | 48 | virtual void OnClientDisconnect(EN_SOCKET clientSocket) override 49 | { 50 | LOG(EN::LogLevels::Info, "Client disconnected! Socket descriptor: " + std::to_string(clientSocket)); 51 | } 52 | }; 53 | 54 | 55 | int main() 56 | { 57 | // MyServer A; 58 | // A.Run(); 59 | 60 | 61 | // Uncomment this code to make server standart console input. 62 | // Using this you can write logic to kick clients or shutdown server 63 | 64 | MyServer A; 65 | start: 66 | 67 | std::thread th([&A]() 68 | { 69 | try 70 | { 71 | A.Run(); 72 | } 73 | catch (std::runtime_error& err) 74 | { 75 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 76 | LOG(EN::LogLevels::Info, "Enter f to end programm"); 77 | } 78 | }); 79 | 80 | std::string message; 81 | 82 | while (true) 83 | { 84 | getline(std::cin, message); 85 | 86 | if (message == "f") 87 | { 88 | A.Shutdown(); 89 | break; 90 | } 91 | 92 | if (message == "r") 93 | { 94 | th.join(); 95 | goto start; 96 | } 97 | 98 | A.MulticastSend(message); 99 | } 100 | 101 | th.join(); 102 | } 103 | -------------------------------------------------------------------------------- /EN_ThreadGate.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_ThreadGate.h" 2 | 3 | namespace EN 4 | { 5 | void EN_ThreadGate::Close() 6 | { 7 | std::unique_lock lk(lockMutex); 8 | mtx.lock(); 9 | if (IsActivated) 10 | { 11 | condVarMutex.lock(); 12 | mtx.unlock(); 13 | cv.wait(lk); 14 | condVarMutex.unlock(); 15 | mtx.lock(); 16 | } 17 | else 18 | { 19 | IsActivated = true; 20 | } 21 | mtx.unlock(); 22 | } 23 | 24 | void EN_ThreadGate::Open() 25 | { 26 | mtx.lock(); 27 | if (condVarMutex.try_lock()) 28 | { 29 | IsActivated = false; 30 | condVarMutex.unlock(); 31 | } 32 | else 33 | { 34 | while (condVarMutex.try_lock() != true) 35 | cv.notify_all(); 36 | 37 | condVarMutex.unlock(); 38 | } 39 | mtx.unlock(); 40 | } 41 | 42 | void EN_RecursiveThreadGate::Close() 43 | { 44 | std::unique_lock lk(lockMutex); 45 | mtx.lock(); 46 | if (ClosingAmount <= 0) 47 | { 48 | condVarMutex.lock(); 49 | mtx.unlock(); 50 | cv.wait(lk); 51 | condVarMutex.unlock(); 52 | mtx.lock(); 53 | } 54 | --ClosingAmount; 55 | mtx.unlock(); 56 | } 57 | 58 | void EN_RecursiveThreadGate::Open() 59 | { 60 | mtx.lock(); 61 | if (!condVarMutex.try_lock()) 62 | { 63 | while (condVarMutex.try_lock() != true) 64 | cv.notify_all(); 65 | 66 | condVarMutex.unlock(); 67 | } 68 | else 69 | condVarMutex.unlock(); 70 | 71 | ++ClosingAmount; 72 | mtx.unlock(); 73 | } 74 | 75 | void EN_TimeThreadGate::Close() 76 | { 77 | std::unique_lock lk(lockMutex); 78 | mtx.lock(); 79 | if (IsActivated) 80 | { 81 | condVarMutex.lock(); 82 | mtx.unlock(); 83 | cv.wait(lk); 84 | condVarMutex.unlock(); 85 | mtx.lock(); 86 | } 87 | else 88 | { 89 | IsActivated = true; 90 | } 91 | mtx.unlock(); 92 | } 93 | 94 | void EN_TimeThreadGate::Open() 95 | { 96 | mtx.lock(); 97 | if (condVarMutex.try_lock()) 98 | { 99 | IsActivated = false; 100 | condVarMutex.unlock(); 101 | } 102 | else 103 | { 104 | while (condVarMutex.try_lock() != true) 105 | cv.notify_all(); 106 | 107 | condVarMutex.unlock(); 108 | } 109 | mtx.unlock(); 110 | } 111 | 112 | void EN_TimeThreadGate::OpenIfClosed() 113 | { 114 | mtx.lock(); 115 | 116 | while (condVarMutex.try_lock() != true) 117 | cv.notify_all(); 118 | 119 | condVarMutex.unlock(); 120 | 121 | mtx.unlock(); 122 | } 123 | } -------------------------------------------------------------------------------- /Examples/TCP_Chat_Server_SC.cpp: -------------------------------------------------------------------------------- 1 | #include "../EN_TCP_Server_SC.h" 2 | 3 | // Class to save session context. 4 | struct Session 5 | { 6 | int counter = 0; 7 | }; 8 | 9 | class TCP_Server_SC : public EN::EN_TCP_Server_SC 10 | { 11 | protected: 12 | virtual void OnClientConnected(EN_SOCKET clientSocket, EN::TCP_SessionContext_SC& sessionContext) 13 | { 14 | LOG(EN::LogLevels::Info, "Client connected! Socket descriptor: " + std::to_string(clientSocket)); 15 | SendToClient(clientSocket, "Welcome. You are connected to server."); 16 | } 17 | 18 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message, EN::TCP_SessionContext_SC& sessionContext) 19 | { 20 | ++sessionContext->counter; 21 | 22 | // Important. This function is run in a separate thread. 23 | // If you want to write data to class variables, you should use mutexes or other algorithms for thread-safe code. 24 | LOG(EN::LogLevels::Info, "From: " + std::to_string(clientSocket) + " Message: " + message); 25 | 26 | // Disconnect client 27 | if (message == "d") 28 | DisconnectClient(clientSocket); 29 | 30 | // Shutdown server 31 | if (message == "F") 32 | { 33 | LOG(EN::LogLevels::Info, "Server was shutdown by client. Enter f to end programm or r to restart server"); 34 | Shutdown(); 35 | } 36 | 37 | // Send incoming message to all different clients 38 | // Blocking the client sockets list is necessary because while the cycle is going through, 39 | // one of the clients may disconnect and UB will occur. If the client disconnects during the lock, 40 | // its socket will not be released until the unlock occurs, and the Send method for this client returns false 41 | LockClientSockets(); 42 | for (EN_SOCKET sock : ClientSockets) 43 | { 44 | if (sock != clientSocket) 45 | SendToClient(sock, message); 46 | } 47 | UnlockClientSockets(); 48 | } 49 | 50 | virtual void OnClientDisconnect(EN_SOCKET clientSocket, EN::TCP_SessionContext_SC& sessionContext) 51 | { 52 | LOG(EN::LogLevels::Info, "Client disconnected! Socket descriptor: " + std::to_string(clientSocket) + ". Number of messages received: " + std::to_string(sessionContext->counter)); 53 | } 54 | 55 | public: 56 | TCP_Server_SC() 57 | { 58 | // IpAddress = "192.168.1.64"; // Default set to localhost. Read description for this variable before use it. 59 | // Port = to set port. Default port is 1111 60 | } 61 | }; 62 | 63 | int main() 64 | { 65 | TCP_Server_SC A; 66 | start: 67 | 68 | std::thread th([&A]() 69 | { 70 | try 71 | { 72 | A.Run(); 73 | } 74 | catch (std::runtime_error& err) 75 | { 76 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 77 | LOG(EN::LogLevels::Info, "Enter f to end programm"); 78 | } 79 | }); 80 | 81 | std::string message; 82 | 83 | while (true) 84 | { 85 | getline(std::cin, message); 86 | 87 | if (message == "f") 88 | { 89 | A.Shutdown(); 90 | break; 91 | } 92 | 93 | if (message == "r") 94 | { 95 | th.join(); 96 | goto start; 97 | } 98 | } 99 | 100 | th.join(); 101 | } -------------------------------------------------------------------------------- /EN_ParallelFor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace EN 9 | { 10 | /// Class implementing a parallel for 11 | class EN_ParallelFor 12 | { 13 | private: 14 | std::mutex StepMutex, ThreadFinishingMutex, ConditionVarMutex; 15 | std::condition_variable CondVar; 16 | int ThreadAmount, EndedThreads; 17 | 18 | private: 19 | 20 | // A function running in a separate thread. Calls a custom function 21 | // Gets template position and end. Its can be iterator or a numbers 22 | // Gets lambda function to invoke it 23 | template 24 | void ThreadFunc(T& Pos, const T& End, const F& lambda_func) 25 | { 26 | T LocalPos; // Local var to store thread position 27 | while (true) 28 | { 29 | StepMutex.lock(); // Start working with positions 30 | LocalPos = Pos; 31 | 32 | if (Pos != End) // Check if its last element 33 | Pos++; 34 | else 35 | { 36 | ThreadFinishingMutex.lock(); // Start working with thread killing 37 | EndedThreads++; // Increment stopped threads amount 38 | StepMutex.unlock(); // Stop working with positions 39 | break; 40 | } 41 | 42 | StepMutex.unlock(); // Stop working with positions 43 | 44 | lambda_func(LocalPos); // Invokes user function 45 | } 46 | 47 | if (EndedThreads >= ThreadAmount) // Checks if all threads ended 48 | CondVar.notify_all(); // Notify condition variable to exit from operator() 49 | ThreadFinishingMutex.unlock(); // Stop working with thread killing 50 | } 51 | 52 | public: 53 | /** 54 | \brief Method that start and execute thread pool 55 | 56 | \param[in] start Template variable to indicate the start of the cycle. Can be an iterator or a number 57 | \param[in] end Template variable to indicate the end of the cycle. Can be an iterator or a number 58 | \param[in] thread_amount The thread amount 59 | \param[in] lambda_func The user lambda function to invokes in every iterarion. 60 | The lambda function accepts one parameter of the same type as the first two parameters. 61 | This function is called for all values between start and end. 62 | */ 63 | template 64 | void operator() (T start, T end, int thread_amount, const F& lambda_func) 65 | { 66 | EndedThreads = 0; 67 | T Pos = start; T End = end; ThreadAmount = thread_amount; 68 | 69 | std::unique_lock CondVarUniqueLock(ConditionVarMutex); 70 | 71 | // Creating threads with lambdas 72 | for (int i = 0; i < thread_amount; i++) 73 | { 74 | std::thread th([this, lambda_func, &Pos, &End]() 75 | { 76 | ThreadFunc(Pos, End, lambda_func); 77 | }); 78 | th.detach(); 79 | } 80 | 81 | CondVar.wait(CondVarUniqueLock); 82 | } 83 | }; 84 | } -------------------------------------------------------------------------------- /EN_UDP_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_UDP_Client.h" 2 | 3 | namespace EN 4 | { 5 | int EN_UDP_Client::GetPort() 6 | { 7 | return ServerPort; 8 | } 9 | 10 | std::string EN_UDP_Client::GetIpAddr() 11 | { 12 | return ServerIpAddress; 13 | } 14 | 15 | EN_SOCKET EN_UDP_Client::GetSocket() 16 | { 17 | return ServerConnectionSocket; 18 | } 19 | 20 | bool EN_UDP_Client::Run() 21 | { 22 | ClientMtx.lock(); 23 | // Server address 24 | sockaddr_in ServerSockAddr; 25 | 26 | ServerSockAddr.sin_family = AF_INET; 27 | ServerSockAddr.sin_port = htons(ServerPort); 28 | 29 | ServerConnectionSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 30 | 31 | if (ServerConnectionSocket == INVALID_SOCKET) 32 | { 33 | ClientMtx.unlock(); 34 | LOG(Error, "Failed to create socket"); 35 | return false; 36 | } 37 | 38 | // Set ip address 39 | inet_pton(AF_INET, ServerIpAddress.c_str(), &ServerSockAddr.sin_addr); 40 | 41 | for (SocketOption& opt : SocketOptions) 42 | EN::SetSocketOption(ServerConnectionSocket, opt.Level, opt.OptionName, opt.OptionValue); 43 | 44 | // Get port from os 45 | UDP_Send(ServerConnectionSocket, "0.0.0.0:0", ""); 46 | 47 | std::thread ServerHandlerThread([this]() { this->ServerHandler(); }); 48 | ServerHandlerThread.detach(); 49 | ClientMtx.unlock(); 50 | return true; 51 | } 52 | 53 | void EN_UDP_Client::ServerHandler() 54 | { 55 | std::string message, ipAddress; 56 | bool operationRes; 57 | while (true) 58 | { 59 | operationRes = UDP_Recv(ServerConnectionSocket, ipAddress, message); 60 | 61 | if (!operationRes) 62 | break; 63 | 64 | ServerMessageHandler(message); 65 | } 66 | } 67 | 68 | void EN_UDP_Client::SendToServer(std::string message) 69 | { 70 | UDP_Send(ServerConnectionSocket, ServerIpAddress + ":" + std::to_string(ServerPort), message); 71 | } 72 | 73 | void EN_UDP_Client::Stop() 74 | { 75 | CloseSocket(ServerConnectionSocket); 76 | } 77 | 78 | void EN_UDP_Client::SetSocketOption(int level, int optionName, int optionValue) 79 | { 80 | ClientMtx.lock(); 81 | if (ServerConnectionSocket == INVALID_SOCKET) 82 | EN::SetSocketOption(ServerConnectionSocket, level, optionName, optionValue); 83 | else 84 | SocketOptions.push_back(SocketOption(level, optionName, optionValue)); 85 | ClientMtx.unlock(); 86 | } 87 | 88 | void EN_UDP_Client::SetSocketOption(PredefinedSocketOptions socketOptions) 89 | { 90 | ClientMtx.lock(); 91 | if (ServerConnectionSocket == INVALID_SOCKET) 92 | for (size_t i = 0; i < socketOptions.Levels.size(); ++i) 93 | EN::SetSocketOption(ServerConnectionSocket, socketOptions.Levels[i], socketOptions.OptionNames[i], socketOptions.OptionValues[i]); 94 | else 95 | for (size_t i = 0; i < socketOptions.Levels.size(); ++i) 96 | SocketOptions.push_back(SocketOption(socketOptions.Levels[i], socketOptions.OptionNames[i], socketOptions.OptionValues[i])); 97 | ClientMtx.unlock(); 98 | } 99 | 100 | void EN_UDP_Client::SetUDPSendFunction(void (*UDPSendFunction)(EN_SOCKET sock, std::string destinationAddress, const std::string& message)) 101 | { 102 | UDP_Send = UDPSendFunction; 103 | } 104 | 105 | void EN_UDP_Client::SetUDPRecvFunction(bool (*UDPRecvFunction)(EN_SOCKET sock, std::string& sourceAddress, std::string& message)) 106 | { 107 | UDP_Recv = UDPRecvFunction; 108 | } 109 | 110 | EN_UDP_Client::~EN_UDP_Client() {} 111 | } 112 | -------------------------------------------------------------------------------- /EN_FileTransmissionStatus.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_FileTransmissionStatus.h" 2 | 3 | namespace EN 4 | { 5 | EN_FileTransmissionStatus::EN_FileTransmissionStatus() 6 | { 7 | TransferedBytes.store(0); 8 | FileSize.store(0); 9 | TransmissionSpeed.store(0); 10 | TransmissionEta.store(0); 11 | IsTransmissionEnded.store(false); 12 | IsTransmissionSucceed.store(false); 13 | } 14 | 15 | EN_FileTransmissionStatus::EN_FileTransmissionStatus(EN::EN_FileTransmissionStatus& status) 16 | { 17 | TransferedBytes.store(status.TransferedBytes.load()); 18 | FileSize.store(status.FileSize.load()); 19 | TransmissionSpeed.store(status.TransmissionSpeed.load()); 20 | TransmissionEta.store(status.TransmissionEta.load()); 21 | IsTransmissionEnded.store(status.IsTransmissionEnded); 22 | IsTransmissionSucceed.store(status.IsTransmissionSucceed); 23 | 24 | IsSetProgressFunction = status.IsSetProgressFunction; 25 | ProgressFunction = status.ProgressFunction; 26 | } 27 | 28 | void EN_FileTransmissionStatus::SetTransferedBytes(uint64_t transferedBytes) 29 | { 30 | TransferedBytes.store(transferedBytes); 31 | } 32 | 33 | uint64_t EN_FileTransmissionStatus::GetThisSessionTransferedBytes() 34 | { 35 | return TransferedBytes.load(); 36 | } 37 | 38 | void EN_FileTransmissionStatus::SetFileSize(uint64_t fileSize) 39 | { 40 | FileSize.store(fileSize); 41 | } 42 | 43 | uint64_t EN_FileTransmissionStatus::GetFileSize() 44 | { 45 | return FileSize.load(); 46 | } 47 | 48 | void EN_FileTransmissionStatus::SetTransmissionSpeed(uint64_t transmissionSpeed) 49 | { 50 | TransmissionSpeed.store(transmissionSpeed); 51 | } 52 | 53 | uint64_t EN_FileTransmissionStatus::GetTransmissionSpeed() 54 | { 55 | return TransmissionSpeed.load(); 56 | } 57 | 58 | void EN_FileTransmissionStatus::SetTransmissionEta(uint64_t transmissionEta) 59 | { 60 | TransmissionEta.store(transmissionEta); 61 | } 62 | 63 | uint64_t EN_FileTransmissionStatus::GetTransmissionEta() 64 | { 65 | return TransmissionEta.load(); 66 | } 67 | 68 | void EN_FileTransmissionStatus::SetIsTransmissionEnded(bool isTransmissionEnded) 69 | { 70 | IsTransmissionEnded.store(isTransmissionEnded); 71 | } 72 | 73 | bool EN_FileTransmissionStatus::GetIsTransmissionEnded() 74 | { 75 | return IsTransmissionEnded.load(); 76 | } 77 | 78 | void EN_FileTransmissionStatus::SetIsTransmissionSucceed(bool isTransmissionSucceed) 79 | { 80 | IsTransmissionSucceed.store(isTransmissionSucceed); 81 | } 82 | 83 | bool EN_FileTransmissionStatus::GetIsTransmissionSucceed() 84 | { 85 | return IsTransmissionSucceed.load(); 86 | } 87 | 88 | bool EN_FileTransmissionStatus::GetIsSetProgressFunction() 89 | { 90 | return IsSetProgressFunction; 91 | } 92 | 93 | void EN_FileTransmissionStatus::SetProgressFunction(std::function progressFunction) 94 | { 95 | IsSetProgressFunction = true; 96 | ProgressFunction = progressFunction; 97 | } 98 | 99 | void EN_FileTransmissionStatus::InvokeProgressFunction() 100 | { 101 | if (IsSetProgressFunction) 102 | ProgressFunction(TransferedBytes.load(), FileSize.load(), TransmissionSpeed.load(), TransmissionEta.load()); 103 | } 104 | } -------------------------------------------------------------------------------- /EN_Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined WIN32 || defined _WIN64 8 | #include 9 | #include 10 | #endif 11 | 12 | #include "EN_Functions.h" 13 | 14 | #ifndef DISABLE_LOGGER 15 | #define LOG(logLevel, messageString) EN::LogFunc(logLevel, messageString); 16 | #else 17 | #define LOG(logLevel, messageString) 18 | #endif 19 | 20 | // Dont use this. Diable logger using -DDISABLE_LOGGER key when compile project 21 | #define EN_LOG_LEVEL_NONE 0b00000000 22 | #define EN_LOG_LEVEL_INFO 0b00000001 23 | #define EN_LOG_LEVEL_HINT 0b00000010 24 | #define EN_LOG_LEVEL_WARNING 0b00000100 25 | #define EN_LOG_LEVEL_ERROR 0b00001000 26 | 27 | 28 | namespace EN 29 | { 30 | /// Logging levels 31 | enum LogLevels 32 | { 33 | Info, ///< Info. A simple message with some debugging information 34 | Hint, ///< Hint. A message with hint for correcting errors. All hints are created manually 35 | Warning, ///< Warning. Warning message about possible errors 36 | Error ///< Error. Critical error messages 37 | }; 38 | 39 | /** 40 | \brief Enabled logging levels 41 | 42 | Using this variable, you can disable the output of unnecessary messages. 43 | Use EnableLogLevels functions to turn on only required log levels. 44 | */ 45 | extern int EnabledLogLevels; 46 | 47 | /// Pointer to logging function 48 | extern void (*LogFunc)(LogLevels, std::string); 49 | 50 | /// \brief Default logging function. Write all to cerr 51 | /// \param [in] logLevel The type of this message 52 | /// \param [in] logMessage The message itself 53 | void DefaultLogFunc(LogLevels logLevel, std::string logMessage); 54 | 55 | /** 56 | \brief Set the logging function and optionally max log level 57 | 58 | \param [in] logFunc A pointer to the logging function. 59 | The function must accept a parameter of type LogLevels and std::string. 60 | The message level will be passed to the LogLevels type parameter. 61 | The message itself is passed to the std::string type parameter 62 | \param [in] logLevelsToEnable Optional parametr for fine-tuning the required logging levels. 63 | Possible values: EN_LOG_LEVEL_INFO, EN_LOG_LEVEL_HINT, EN_LOG_LEVEL_WARNING, EN_LOG_LEVEL_ERROR. 64 | Using the logical operator |, you can combine the necessary logging levels. 65 | */ 66 | void SetLogFunc(void (*logFunc)(LogLevels, std::string), int logLevelsToEnable = (EN_LOG_LEVEL_INFO | EN_LOG_LEVEL_HINT | EN_LOG_LEVEL_WARNING | EN_LOG_LEVEL_ERROR)); 67 | 68 | /** 69 | \brief Enable log levels. 70 | 71 | \param [in] logLevelsToEnable Parametr for fine-tuning the required logging levels. 72 | Possible values: EN_LOG_LEVEL_INFO, EN_LOG_LEVEL_HINT, EN_LOG_LEVEL_WARNING, EN_LOG_LEVEL_ERROR. 73 | Using the logical operator |, you can combine the necessary logging levels. 74 | */ 75 | void EnableLogLevels(int logLevelsToEnable); 76 | 77 | /** 78 | \brief Enable file log levels. 79 | 80 | \param [in] logLevelsToEnable Parametr for fine-tuning the required logging levels. 81 | Possible values: EN_LOG_LEVEL_INFO, EN_LOG_LEVEL_HINT, EN_LOG_LEVEL_WARNING, EN_LOG_LEVEL_ERROR. 82 | Using the logical operator |, you can combine the necessary logging levels. 83 | \param [in] fileName The name of the file to record this logging level 84 | \param [in] openMode Optional parametr to open log files. Works like standart fstream open. By default set to std::ios_base::out 85 | */ 86 | void SetLogLevelsFile(int logLevelsToEnable, std::string fileName, std::ios_base::openmode openMode = std::ios_base::out); 87 | } 88 | -------------------------------------------------------------------------------- /Examples/TCP_BlockingFileTransmitter_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_TCP_Server.h" 2 | 3 | class MyServer : public EN::EN_TCP_Server 4 | { 5 | public: 6 | MyServer(std::string ip, int port) 7 | { 8 | IpAddress = ip; // Default set to localhost 9 | Port = port; // Default port is 1111 10 | } 11 | 12 | virtual void OnClientConnected(EN_SOCKET clientSocket) override 13 | { 14 | LOG(EN::LogLevels::Info, "Client connected! Socket descriptor: " + std::to_string(clientSocket)); 15 | } 16 | 17 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) override 18 | { 19 | // Important. This function is run in a separate thread. 20 | // If you want to write data to class variables, you should use mutexes or other algorithms for thread-safe code. 21 | LOG(EN::LogLevels::Info, message); 22 | std::atomic_bool ShouldShutdown(false); 23 | std::atomic_int transferingSpeed(0); 24 | EN::EN_FileTransmissionStatus transmissionStatus; 25 | transmissionStatus.SetProgressFunction(EN::DefaultDownloadStatusFunction); 26 | 27 | std::vector InterpretedMessage = EN::Split(message); 28 | 29 | if (InterpretedMessage[0] == "was") 30 | { 31 | if (EN::IsFileExist(InterpretedMessage[1] + ".tmp")) 32 | { 33 | LOG(EN::LogLevels::Info, "Continue the previous file receiving"); 34 | SendToClient(clientSocket, "ok " + std::to_string(EN::GetFileSize(InterpretedMessage[1] + ".tmp"))); 35 | } 36 | else 37 | { 38 | LOG(EN::LogLevels::Info, "Starting the file receiving"); 39 | SendToClient(clientSocket, "bad"); 40 | } 41 | 42 | EN::RecvFile(clientSocket, ShouldShutdown, transmissionStatus); 43 | return; 44 | } 45 | 46 | if (InterpretedMessage[0] == "get") 47 | { 48 | if (EN::IsFileExist(InterpretedMessage[1])) 49 | { 50 | LOG(EN::LogLevels::Info, "Starting to send the file"); 51 | SendToClient(clientSocket, "ok"); 52 | EN::SendFile(clientSocket, InterpretedMessage[1], ShouldShutdown, transferingSpeed, 0, transmissionStatus); 53 | } 54 | else 55 | { 56 | LOG(EN::LogLevels::Info, "The file is not on the server"); 57 | SendToClient(clientSocket, "bad"); 58 | } 59 | 60 | return; 61 | } 62 | 63 | if (InterpretedMessage[0] == "continue") 64 | { 65 | if (EN::IsFileExist(InterpretedMessage[1])) 66 | { 67 | LOG(EN::LogLevels::Info, "Continue the previous sending"); 68 | SendToClient(clientSocket, "ok"); 69 | std::uint64_t sendingSize; 70 | EN::StringToInt(InterpretedMessage[2], sendingSize); 71 | EN::SendFile(clientSocket, InterpretedMessage[1], ShouldShutdown, transferingSpeed, sendingSize, transmissionStatus); 72 | } 73 | else 74 | { 75 | LOG(EN::LogLevels::Info, "The file is not on the server"); 76 | SendToClient(clientSocket, "bad"); 77 | } 78 | return; 79 | } 80 | } 81 | 82 | virtual void OnClientDisconnect(EN_SOCKET clientSocket) override 83 | { 84 | LOG(EN::LogLevels::Info, "Client disconnected! Socket descriptor: " + std::to_string(clientSocket)); 85 | } 86 | }; 87 | 88 | 89 | int main() 90 | { 91 | LOG(EN::LogLevels::Info, "Write server ip or/and port. Format: ip:port. Example: 192.168.1.85:1234."); 92 | LOG(EN::LogLevels::Info, "If you dont write ip it will be all available pc ips from all networks"); 93 | LOG(EN::LogLevels::Info, "If you dont write port it will be default value: 1111"); 94 | 95 | std::string addr, ip; 96 | int port; 97 | 98 | getline(std::cin, addr); 99 | auto vec = EN::Split(addr, ":"); 100 | 101 | if (!addr.empty()) 102 | ip = vec[0]; 103 | 104 | if (vec.size() == 2) 105 | EN::StringToInt(vec[1], port); 106 | else port = 1111; // Default value 107 | 108 | // No incorrect port check 109 | MyServer A(ip, port); 110 | try 111 | { 112 | A.Run(); 113 | } 114 | catch (std::runtime_error& err) 115 | { 116 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 117 | } 118 | 119 | // Uncomment this code to make server standart console input. 120 | // Using this you can write logic to kick clients or shutdown server 121 | /* 122 | MyServer A; 123 | std::thread th([&A]() 124 | { 125 | try 126 | { 127 | A.Run(); 128 | } 129 | catch (std::runtime_error err) 130 | { 131 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 132 | } 133 | }); 134 | 135 | th.detach(); 136 | std::string message; 137 | while (true) 138 | { 139 | getline(std::cin, message); 140 | if (message == "f") 141 | { 142 | A.Shutdown(); 143 | break; 144 | } 145 | } 146 | */ 147 | } -------------------------------------------------------------------------------- /Examples/TCP_BlockingFileTransmitter_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_TCP_Client.h" 2 | 3 | class MyClient : public EN::EN_TCP_Client 4 | { 5 | public: 6 | MyClient() 7 | { 8 | IsRunMessageHadlerThread = false; 9 | } 10 | 11 | // A function to be defined by the user. It is used for logic after connection 12 | virtual void OnConnect() override {} 13 | 14 | // A function to be defined by the user. It is used to process incoming messages from the server 15 | virtual void ServerMessageHandler(std::string message) override {} 16 | 17 | // A function to be defined by the user. Performed after disconnected from the server 18 | virtual void OnDisconnect() override {} 19 | }; 20 | 21 | int main() 22 | { 23 | MyClient A; 24 | 25 | LOG(EN::LogLevels::Info, "Write server ip or/and port. Format: ip:port. Example: 192.168.1.85:1234."); 26 | LOG(EN::LogLevels::Info, "If you dont write ip it will be default value: 127.0.0.1"); 27 | LOG(EN::LogLevels::Info, "If you dont write port it will be default value: 1111"); 28 | 29 | std::string addr, ip; 30 | int port; 31 | 32 | getline(std::cin, addr); 33 | auto vec = EN::Split(addr, ":"); 34 | 35 | if (addr.empty()) 36 | ip = "127.0.0.1"; // Default value 37 | else 38 | ip = vec[0]; 39 | 40 | if (vec.size() == 2) 41 | EN::StringToInt(vec[1], port); 42 | else port = 1111; // Default value 43 | 44 | // No incorrect port check 45 | if (A.Connect(ip, port) == false) 46 | { 47 | LOG(EN::LogLevels::Warning, "Failed to connect"); 48 | return 0; 49 | } 50 | 51 | LOG(EN::LogLevels::Info, "Connected to server"); 52 | 53 | if (!A.SendToServer("FileSender client connected")) 54 | { 55 | LOG(EN::LogLevels::Info, "Server disconnected"); 56 | return 0; 57 | } 58 | 59 | std::string message; 60 | std::string responce; 61 | std::atomic_bool isStop(false); 62 | std::atomic_int transferingSpeed(0); 63 | EN::EN_FileTransmissionStatus transmissionStatus; 64 | transmissionStatus.SetProgressFunction(EN::DefaultDownloadStatusFunction); 65 | 66 | while (true) 67 | { 68 | getline(std::cin, message); 69 | 70 | if (A.IsConnected() == false) 71 | { 72 | LOG(EN::LogLevels::Warning, "Server disconnected"); 73 | break; 74 | } 75 | std::vector InterpretedMessage = EN::Split(message); 76 | 77 | if (InterpretedMessage[0] == "send") 78 | { 79 | if (EN::IsFileExist(InterpretedMessage[1])) 80 | { 81 | A.SendToServer("was " + InterpretedMessage[1]); 82 | A.WaitMessage(responce); 83 | 84 | std::vector parsedResponce = EN::Split(responce); 85 | 86 | if (parsedResponce[0] == "ok") 87 | { 88 | LOG(EN::LogLevels::Info, "Continue the previous sending"); 89 | std::uint64_t sendingSize; 90 | EN::StringToInt(parsedResponce[1], sendingSize); 91 | EN::SendFile(A.GetSocket(), InterpretedMessage[1], isStop, transferingSpeed, sendingSize, transmissionStatus); 92 | } 93 | else 94 | { 95 | LOG(EN::LogLevels::Info, "Starting to send the file"); 96 | EN::SendFile(A.GetSocket(), InterpretedMessage[1], isStop, transferingSpeed, 0, transmissionStatus); 97 | } 98 | } 99 | else 100 | LOG(EN::LogLevels::Info, "The file is not in the directory"); 101 | continue; 102 | } 103 | 104 | if (InterpretedMessage[0] == "get") 105 | { 106 | if (EN::IsFileExist(InterpretedMessage[1] + ".tmp")) 107 | { 108 | LOG(EN::LogLevels::Info, "Continue the previous file receiving"); 109 | A.SendToServer("continue " + InterpretedMessage[1] + " " + std::to_string(EN::GetFileSize(InterpretedMessage[1] + ".tmp"))); 110 | A.WaitMessage(responce); 111 | 112 | if (responce == "ok") 113 | { 114 | if (EN::RecvFile(A.GetSocket(), isStop, transmissionStatus)) 115 | { 116 | LOG(EN::LogLevels::Info, "The file was received"); 117 | } 118 | else 119 | LOG(EN::LogLevels::Info, "The file was not received"); 120 | } 121 | else 122 | { 123 | LOG(EN::LogLevels::Info, "The file is not on the server"); 124 | } 125 | } 126 | else 127 | { 128 | LOG(EN::LogLevels::Info, "Starting the file receiving"); 129 | 130 | A.SendToServer("get " + InterpretedMessage[1]); 131 | A.WaitMessage(responce); 132 | 133 | if (responce == "ok") 134 | { 135 | if (EN::RecvFile(A.GetSocket(), isStop, transmissionStatus)) 136 | { 137 | LOG(EN::LogLevels::Info, "The file was received"); 138 | } 139 | else 140 | LOG(EN::LogLevels::Info, "The file was not received"); 141 | } 142 | else 143 | LOG(EN::LogLevels::Info, "The file is not on the server"); 144 | } 145 | } 146 | } 147 | A.Disconnect(); 148 | 149 | return 0; 150 | } -------------------------------------------------------------------------------- /EN_RAU_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_RAU_Client.h" 2 | 3 | namespace EN 4 | { 5 | // TCP client 6 | EN_RAU_TCP_Client::EN_RAU_TCP_Client(EN_RAU_Client* rau_Client) 7 | { 8 | RAU_Client = rau_Client; 9 | } 10 | 11 | void EN_RAU_TCP_Client::OnConnect() {}; 12 | 13 | void EN_RAU_TCP_Client::ServerMessageHandler(std::string message) 14 | { 15 | if (RAU_Client->ClientId != -1) 16 | { 17 | if (RAU_Client->IsServerGetUDPAddress == false) 18 | { 19 | RAU_Client->IsServerGetUDPAddress = true; 20 | RAU_Client->OnConnect(); 21 | } 22 | else 23 | { 24 | RAU_Client->Mtx.lock(); 25 | RAU_Client->Messages.push(message); 26 | RAU_Client->Mtx.unlock(); 27 | RAU_Client->CondVar.notify_all(); 28 | } 29 | } 30 | else 31 | { 32 | int clientId; 33 | if (StringToInt(message, clientId)) 34 | RAU_Client->ClientId = clientId); 35 | } 36 | } 37 | 38 | void EN_RAU_TCP_Client::OnDisconnect() 39 | { 40 | RAU_Client->OnDisconnect(); 41 | RAU_Client->UDP_Client->Stop(); 42 | 43 | Delay(300); 44 | 45 | RAU_Client->IsServerGetUDPAddress = true; 46 | RAU_Client->IsShutdown = true; 47 | 48 | RAU_Client->CondVar.notify_all(); 49 | 50 | Delay(300); 51 | 52 | RAU_Client->IsServerGetUDPAddress = false; 53 | RAU_Client->IsShutdown = false; 54 | RAU_Client->ClientId = -1; 55 | } 56 | 57 | // UDP client 58 | EN_RAU_UDP_Client::EN_RAU_UDP_Client(EN_RAU_Client* rau_Client) 59 | { 60 | RAU_Client = rau_Client; 61 | } 62 | 63 | void EN_RAU_UDP_Client::ServerMessageHandler(std::string message) 64 | { 65 | RAU_Client->Mtx.lock(); 66 | RAU_Client->Messages.push(message); 67 | RAU_Client->Mtx.unlock(); 68 | RAU_Client->CondVar.notify_all(); 69 | } 70 | 71 | // RAU client 72 | EN_RAU_Client::EN_RAU_Client() 73 | { 74 | TCP_Client = new EN_RAU_TCP_Client(this); 75 | UDP_Client = new EN_RAU_UDP_Client(this); 76 | } 77 | 78 | int EN_RAU_Client::GetServerPort() { return ServerPort; } 79 | 80 | std::string EN_RAU_Client::GetServerIpAddress() { return ServerIpAddress; } 81 | 82 | bool EN_RAU_Client::IsConnected() 83 | { 84 | return TCP_Client->IsConnected(); 85 | } 86 | 87 | bool EN_RAU_Client::Connect() 88 | { 89 | return Connect("127.0.0.1", 1111); 90 | } 91 | 92 | bool EN_RAU_Client::Connect(int port) 93 | { 94 | return Connect("127.0.0.1", port); 95 | } 96 | 97 | bool EN_RAU_Client::Connect(std::string ipAddr, int port) 98 | { 99 | ServerIpAddress = ipAddr; 100 | ServerPort = port; 101 | 102 | UDP_Client->ServerIpAddress = ServerIpAddress; 103 | UDP_Client->ServerPort = ServerPort + 1; 104 | UDP_Client->Run(); 105 | 106 | std::thread QueueHandlerThread([this]() {this->QueueMessageHandler(); }); 107 | QueueHandlerThread.detach(); 108 | 109 | if (!TCP_Client->Connect(ipAddr, port)) 110 | { 111 | LOG(Warning, "Failed to connect to server"); 112 | return false; 113 | } 114 | 115 | while (IsServerGetUDPAddress != true) 116 | UDP_Client->SendToServer(std::to_string(ClientId)); 117 | 118 | return true; 119 | } 120 | 121 | void EN_RAU_Client::QueueMessageHandler() 122 | { 123 | std::mutex mtx; 124 | std::unique_lock unique_lock_mutex(mtx); 125 | 126 | while (true) 127 | { 128 | while (!Messages.empty()) 129 | { 130 | ServerMessageHandler(Messages.front()); 131 | Mtx.lock(); 132 | Messages.pop(); 133 | Mtx.unlock(); 134 | } 135 | 136 | if (IsShutdown) 137 | break; 138 | 139 | CondVar.wait(unique_lock_mutex); 140 | 141 | if (IsShutdown) 142 | break; 143 | } 144 | } 145 | 146 | void EN_RAU_Client::SendToServer(std::string message, bool IsReliable) 147 | { 148 | if (IsReliable) 149 | TCP_Client->SendToServer(message); 150 | else 151 | UDP_Client->SendToServer(std::to_string(ClientId) + " " + message); 152 | } 153 | 154 | void EN_RAU_Client::Disconnect() 155 | { 156 | TCP_Client->Disconnect(); 157 | UDP_Client->Stop(); 158 | } 159 | 160 | void EN_RAU_Client::SetTCPSocketOption(int level, int optionName, int optionValue) 161 | { 162 | TCP_Client->SetSocketOption(level, optionName, optionValue); 163 | } 164 | 165 | void EN_RAU_Client::SetTCPSocketOption(PredefinedSocketOptions socketOptions) 166 | { 167 | TCP_Client->SetSocketOption(socketOptions); 168 | } 169 | 170 | void EN_RAU_Client::SetUDPSocketOption(int level, int optionName, int optionValue) 171 | { 172 | UDP_Client->SetSocketOption(level, optionName, optionValue); 173 | } 174 | 175 | void EN_RAU_Client::SetUDPSocketOption(PredefinedSocketOptions socketOptions) 176 | { 177 | UDP_Client->SetSocketOption(socketOptions); 178 | } 179 | 180 | EN_RAU_Client::~EN_RAU_Client() 181 | { 182 | delete TCP_Client; 183 | delete UDP_Client; 184 | } 185 | } -------------------------------------------------------------------------------- /EN_UDP_Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined WIN32 || defined _WIN64 4 | 5 | #include 6 | #include 7 | typedef SOCKET EN_SOCKET; 8 | 9 | #else 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | typedef int EN_SOCKET; 16 | #define INVALID_SOCKET -1 17 | #define SOCKET_ERROR -1 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "EN_Functions.h" 25 | #include "EN_SocketOptions.h" 26 | 27 | namespace EN 28 | { 29 | /// Base udp client class 30 | class EN_UDP_Client 31 | { 32 | private: 33 | // Socket to connect to server 34 | EN_SOCKET ServerConnectionSocket = INVALID_SOCKET; 35 | 36 | // The server's internal method for processing incoming messages. 37 | // Passes the incoming string to method ServerMessageHandler to interpretate incoming message 38 | void ServerHandler(); 39 | 40 | // Vector with socket options 41 | std::vector SocketOptions; 42 | 43 | std::mutex ClientMtx; 44 | 45 | // A pointer to a function for sending messages. Allows you to use custom network protocols. Send message to socket 46 | void (*UDP_Send)(EN_SOCKET sock, std::string destinationAddress, const std::string& message) = EN::Default_UDP_Send; 47 | 48 | // A pointer to a function for recv messages. Allows you to use custom network protocols. Recv message from socket 49 | bool (*UDP_Recv)(EN_SOCKET sock, std::string& sourceAddress, std::string& message) = EN::Default_UDP_Recv; 50 | protected: 51 | 52 | /// Default port. Default set to 1111 53 | int ServerPort = 1111; 54 | 55 | /// Server ip address string. Default set to localhost 56 | std::string ServerIpAddress = "127.0.0.1"; 57 | 58 | /// It is used to process incoming messages from the server 59 | /// \warning Must be defined by the user 60 | virtual void ServerMessageHandler(std::string message) = 0; 61 | 62 | public: 63 | /// Server port getter 64 | int GetPort(); 65 | 66 | /// Ip getter 67 | std::string GetIpAddr(); 68 | 69 | /// Socket getter 70 | EN_SOCKET GetSocket(); 71 | 72 | /// Method to start server. Starts a thread to process server responses 73 | /// Will return the true in case of a successful launch otherwise false 74 | bool Run(); 75 | 76 | /** 77 | \brief Function for sending a message to a connected server 78 | 79 | \param[in] message string to send to server 80 | */ 81 | void SendToServer(std::string message); 82 | 83 | /// Function to stop client 84 | void Stop(); 85 | 86 | /** 87 | \brief The method sets options for client socket 88 | 89 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 90 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 91 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 92 | \param[in] optionValue The value for the requested option is specified. 93 | */ 94 | void SetSocketOption(int level, int optionName, int optionValue); 95 | 96 | /** 97 | \brief The method sets options for client socket 98 | 99 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 100 | The list of all predefined structures is in EN_SocketOptions.h. 101 | You can create your own sets of options using define or by creating structure objects 102 | */ 103 | void SetSocketOption(PredefinedSocketOptions socketOptions); 104 | 105 | /** 106 | \brief The method sets custom send function. Allow you use your own protocol 107 | 108 | \param[in] UDPSendFunction This parameter is a pointer to a function for sending messages to the socket. 109 | The function accepts the socket of the connected client, where you want to send 110 | the message and the message itself 111 | 112 | \warning If you want to use your protocol, use only one send call. 113 | This is necessary because the send function is thread-safe, 114 | but if you send one message to 2 send calls, then if 2 threads write to the same socket, 115 | then the data of two different messages may be mixed and you will receive errors 116 | */ 117 | void SetUDPSendFunction(void (*UDPSendFunction)(EN_SOCKET sock, std::string destinationAddress, const std::string& message)); 118 | 119 | /** 120 | \brief The method sets custom recv function. Allow you use your own protocol 121 | 122 | \param[in] UDPRecvFunction This parameter is a pointer to a function for receiving messages from the socket. 123 | The function accepts the socket of the connected client, where you want to recv from 124 | the message and the message itself 125 | */ 126 | void SetUDPRecvFunction(bool (*UDPRecvFunction)(EN_SOCKET sock, std::string& sourceAddress, std::string& message)); 127 | 128 | virtual ~EN_UDP_Client(); 129 | }; 130 | } 131 | 132 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 2 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakePredefinedTargets") 3 | 4 | 5 | if(WIN32) 6 | set(StandartIncludeLibraries ws2_32) 7 | else() 8 | set(StandartIncludeLibraries pthread) 9 | endif() 10 | 11 | # HTTP server binary 12 | project (HTTP_Server) 13 | 14 | add_executable(HTTP_Server Examples/http/HTTP_Server.cpp) 15 | 16 | target_include_directories(HTTP_Server PRIVATE ${CMAKE_SOURCE_DIR}/) 17 | 18 | target_link_libraries(HTTP_Server EasyNetwork ${StandartIncludeLibraries}) 19 | 20 | # TCP chat server binary 21 | project (TCP_Chat_Server) 22 | 23 | add_executable(TCP_Chat_Server Examples/TCP_Chat_Server.cpp) 24 | 25 | target_include_directories(TCP_Chat_Server PRIVATE ${CMAKE_SOURCE_DIR}/) 26 | 27 | target_link_libraries(TCP_Chat_Server EasyNetwork ${StandartIncludeLibraries}) 28 | 29 | # TCP chat server binary 30 | project (TCP_Chat_Client) 31 | 32 | add_executable(TCP_Chat_Client Examples/TCP_Chat_Client.cpp) 33 | 34 | target_include_directories(TCP_Chat_Client PRIVATE ${CMAKE_SOURCE_DIR}/) 35 | 36 | target_link_libraries(TCP_Chat_Client EasyNetwork ${StandartIncludeLibraries}) 37 | 38 | # TCP_SC chat server binary 39 | project (TCP_Chat_Server_SC) 40 | 41 | add_executable(TCP_Chat_Server_SC Examples/TCP_Chat_Server_SC.cpp) 42 | 43 | target_include_directories(TCP_Chat_Server_SC PRIVATE ${CMAKE_SOURCE_DIR}/) 44 | 45 | target_link_libraries(TCP_Chat_Server_SC EasyNetwork ${StandartIncludeLibraries}) 46 | 47 | # UDP chat server binary 48 | project (UDP_Chat_Server) 49 | 50 | add_executable(UDP_Chat_Server Examples/UDP_Chat_Server.cpp) 51 | 52 | target_include_directories(UDP_Chat_Server PRIVATE ${CMAKE_SOURCE_DIR}/) 53 | 54 | target_link_libraries(UDP_Chat_Server EasyNetwork ${StandartIncludeLibraries}) 55 | 56 | # UDP chat server binary 57 | project (UDP_Chat_Client) 58 | 59 | add_executable(UDP_Chat_Client Examples/UDP_Chat_Client.cpp) 60 | 61 | target_include_directories(UDP_Chat_Client PRIVATE ${CMAKE_SOURCE_DIR}/) 62 | 63 | target_link_libraries(UDP_Chat_Client EasyNetwork ${StandartIncludeLibraries}) 64 | 65 | # # RAU chat server binary 66 | # project (RAU_Chat_Server) 67 | 68 | # add_executable(RAU_Chat_Server Examples/RAU_Chat_Server.cpp) 69 | 70 | # target_include_directories(RAU_Chat_Server PRIVATE ${CMAKE_SOURCE_DIR}/) 71 | 72 | # target_link_libraries(RAU_Chat_Server EasyNetwork ${StandartIncludeLibraries}) 73 | 74 | # # RAU chat server binary 75 | # project (RAU_Chat_Client) 76 | 77 | # add_executable(RAU_Chat_Client Examples/RAU_Chat_Client.cpp) 78 | 79 | # target_include_directories(RAU_Chat_Client PRIVATE ${CMAKE_SOURCE_DIR}/) 80 | 81 | # target_link_libraries(RAU_Chat_Client EasyNetwork ${StandartIncludeLibraries}) 82 | 83 | # TCP FileSender server binary 84 | project (TCP_BlockingFileTransmitter_Server) 85 | 86 | add_executable(TCP_BlockingFileTransmitter_Server Examples/TCP_BlockingFileTransmitter_Server.cpp) 87 | 88 | target_include_directories(TCP_BlockingFileTransmitter_Server PRIVATE ${CMAKE_SOURCE_DIR}/) 89 | 90 | target_link_libraries(TCP_BlockingFileTransmitter_Server EasyNetwork ${StandartIncludeLibraries}) 91 | 92 | # TCP FileSender server binary 93 | project (TCP_BlockingFileTransmitter_Client) 94 | 95 | add_executable(TCP_BlockingFileTransmitter_Client Examples/TCP_BlockingFileTransmitter_Client.cpp) 96 | 97 | target_include_directories(TCP_BlockingFileTransmitter_Client PRIVATE ${CMAKE_SOURCE_DIR}/) 98 | 99 | target_link_libraries(TCP_BlockingFileTransmitter_Client EasyNetwork ${StandartIncludeLibraries}) 100 | 101 | # FT server binary 102 | project (FT_Chat_Server) 103 | 104 | add_executable(FT_Chat_Server Examples/FT_Chat_Server.cpp) 105 | 106 | target_include_directories(FT_Chat_Server PRIVATE ${CMAKE_SOURCE_DIR}/) 107 | 108 | target_link_libraries(FT_Chat_Server EasyNetwork ${StandartIncludeLibraries}) 109 | 110 | # FT client binary 111 | project (FT_Chat_Client) 112 | 113 | add_executable(FT_Chat_Client Examples/FT_Chat_Client.cpp) 114 | 115 | target_include_directories(FT_Chat_Client PRIVATE ${CMAKE_SOURCE_DIR}/) 116 | 117 | target_link_libraries(FT_Chat_Client EasyNetwork ${StandartIncludeLibraries}) 118 | 119 | # Parallel for binary 120 | project (ParallelFor) 121 | 122 | add_executable(ParallelFor Examples/ParallelFor.cpp) 123 | 124 | target_include_directories(ParallelFor PRIVATE ${CMAKE_SOURCE_DIR}/) 125 | 126 | target_link_libraries(ParallelFor EasyNetwork ${StandartIncludeLibraries}) 127 | 128 | # Library binary 129 | project(EasyNetwork) 130 | 131 | set(LibHeaders EN_Functions.h 132 | EN_TCP_Client.h EN_BackgroundTimer.h EN_FileTransmissionStatus.h 133 | EN_TCP_Server.h EN_UDP_Client.h EN_UDP_Server.h EN_ParallelFor.h EN_SocketOptions.h EN_ThreadGate.h EN_Logger.h 134 | EN_ThreadCrossWalk.h EN_FT_Server.h EN_FT_Client.h EN_ThreadBarrier.h EN_TCP_Server_SC.h) 135 | 136 | set(LibSources EN_Functions.cpp 137 | EN_TCP_Client.cpp EN_BackgroundTimer.cpp EN_FileTransmissionStatus.cpp 138 | EN_TCP_Server.cpp EN_UDP_Client.cpp EN_UDP_Server.cpp EN_SocketOptions.cpp EN_ThreadGate.cpp EN_Logger.cpp 139 | EN_ThreadCrossWalk.cpp EN_FT_Server.cpp EN_FT_Client.cpp EN_ThreadBarrier.cpp) 140 | 141 | add_library(EasyNetwork STATIC ${LibSources} ${LibHeaders}) -------------------------------------------------------------------------------- /EN_TCP_Server_SC.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EN_TCP_Server.h" 4 | 5 | namespace EN 6 | { 7 | /** 8 | \brief Template class for storing different information during a single client session 9 | 10 | An object of this class will be created after the client is connected, 11 | it will be created in the client processing thread and will be available 12 | in all three methods of working with the connected client, 13 | i.e. with OnClientConnected, ClientMessageHandler and OnClientDisconnect. 14 | As the type of this class, you need to specify the type or class that stores all 15 | the necessary variables to describe the session. Data is accessed via the "->" operator. 16 | Inside EN_TCP_Server_SC, each object of this class is used only in its own thread, 17 | so it is thread-safe, and you should not try to use it in different threads. 18 | */ 19 | template 20 | struct TCP_SessionContext_SC 21 | { 22 | /// Template variable to store session data 23 | T data; 24 | 25 | T* operator-> () 26 | { 27 | return &data; 28 | } 29 | }; 30 | 31 | /** 32 | \brief Tcp server with session context 33 | 34 | This class adds the ability to store data within a single client session. 35 | The type of data to store is specified in the class template specification. 36 | The user class will be wrapped in TCP_SessionContext_SC, data access inside it is implemented through the "->" operator. 37 | 38 | An object of the TCP_SessionContext_SC class will be created after the client is connected, 39 | it will be created in the client processing thread and will be available 40 | in all three methods of working with the connected client, 41 | i.e. with OnClientConnected, ClientMessageHandler and OnClientDisconnect. 42 | Each object of this class is used only in its own thread, 43 | so it is thread-safe, and you should not try to use it in different threads. 44 | */ 45 | template 46 | class EN_TCP_Server_SC : public EN::EN_TCP_Server 47 | { 48 | private: 49 | // Definition of a purely virtual parent function, for further redefinition of this function with the addition of new parameters 50 | virtual void OnClientConnected(EN_SOCKET clientSocket) {}; 51 | 52 | // Definition of a purely virtual parent function, for further redefinition of this function with the addition of new parameters 53 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) {}; 54 | 55 | // Definition of a purely virtual parent function, for further redefinition of this function with the addition of new parameters 56 | virtual void OnClientDisconnect(EN_SOCKET clientSocket) {}; 57 | 58 | // A method that processes messages from clients. Sends a message to the function ClientMessageHandler(). 59 | virtual void ClientHandler(EN_SOCKET clientSocket) override 60 | { 61 | OnClientConnected(clientSocket, SessionContext); 62 | 63 | std::string message; 64 | bool ConnectionStatus; 65 | 66 | while (true) 67 | { 68 | ConnectionStatus = TCP_Recv(clientSocket, message); 69 | 70 | if (ConnectionStatus == false) 71 | { 72 | OnClientDisconnect(clientSocket, SessionContext); 73 | break; 74 | } 75 | 76 | ClientMessageHandler(clientSocket, message, SessionContext); 77 | } 78 | 79 | CloseSocket(clientSocket); 80 | 81 | CrossWalk.PedestrianStartCrossRoad(); 82 | 83 | ClientSockets.erase(clientSocket); 84 | 85 | CrossWalk.PedestrianStopCrossRoad(); 86 | } 87 | 88 | protected: 89 | /// Variable to store session context data 90 | /// \todo Переменная одна для всех потоков, а это не правильно 91 | TCP_SessionContext_SC SessionContext; 92 | 93 | /** 94 | \brief The method that is executed when the client connects to the server 95 | 96 | \param[in] clientSocket Socket of the connected client 97 | \param[in] sessionContext A reference to a variable that stores the context of the connected client's session 98 | 99 | \warning Must be defined by the user 100 | */ 101 | virtual void OnClientConnected(EN_SOCKET clientSocket, TCP_SessionContext_SC& sessionContext) = 0; 102 | 103 | /** 104 | \brief Method that processes incoming messages 105 | 106 | \param[in] clientSocket The socket of the client from which the message came 107 | \param[in] message Message from the client 108 | \param[in] sessionContext A reference to a variable that stores the context of the connected client's session 109 | 110 | \warning Must be defined by the user 111 | */ 112 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message, TCP_SessionContext_SC& sessionContext) = 0; 113 | 114 | /** 115 | \brief Method that runs after the client is disconnected 116 | 117 | \param[in] clientSocket Socket of the disconnected client 118 | \param[in] sessionContext A reference to a variable that stores the context of the connected client's session 119 | 120 | \warning Must be defined by the user 121 | */ 122 | virtual void OnClientDisconnect(EN_SOCKET clientSocket, TCP_SessionContext_SC& sessionContext) = 0; 123 | }; 124 | } -------------------------------------------------------------------------------- /arduino/EN_TCP_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "TCP_Client.h" 2 | 3 | bool TCP_Client::ConnectToWIFI(String wifiName, String wifiPassword, int attemptsCount) 4 | { 5 | WiFi.begin(wifiName.c_str(), wifiPassword.c_str()); 6 | 7 | Serial.println("Connecting to Wifi:"); 8 | for (int i = 0; i < attemptsCount; ++i) 9 | { 10 | if (WiFi.status() == WL_CONNECTED) 11 | { 12 | Serial.println("\nConnected to WiFi."); 13 | return true; 14 | } 15 | delay(500); 16 | Serial.print("."); 17 | } 18 | return false; 19 | } 20 | 21 | bool TCP_Client::ConnectToServer(String serverIP, String serverPort, int attemptsCount) 22 | { 23 | Serial.println("Connecting to server:"); 24 | for (int i = 0; i < attemptsCount; ++i) 25 | { 26 | Client.connect(serverIP.c_str(), serverPort.toInt()); 27 | if (Client.connected()) 28 | { 29 | Serial.println("\nConnected to server."); 30 | return true; 31 | } 32 | delay(500); 33 | Serial.print("."); 34 | } 35 | return false; 36 | } 37 | 38 | bool TCP_Client::SendToServer(const std::string &msg) 39 | { 40 | // Sending data prefix with information about message length 41 | std::string messageLengthString; 42 | std::size_t messageLength = msg.length(); 43 | 44 | if (messageLength == 0) 45 | messageLengthString += '\0'; 46 | 47 | // Convert message length to 128 base system 48 | while (messageLength > 0) 49 | { 50 | // Get last seven bits. Equals to % 128 51 | messageLengthString += (unsigned char)(messageLength & 0b01111111); 52 | messageLength >>= 7; 53 | } 54 | 55 | messageLength = msg.length(); 56 | 57 | // Alloc memory to sending message 58 | unsigned char *msgBuf = new unsigned char[messageLengthString.length() + messageLength]; 59 | 60 | // Set first bit in length bytes except last length byte 61 | for (std::size_t i = 0; i < messageLengthString.length() - 1; ++i) 62 | msgBuf[i] = ((unsigned char)messageLengthString[i]) | 0b10000000; 63 | 64 | msgBuf[messageLengthString.length() - 1] = messageLengthString[messageLengthString.length() - 1]; 65 | 66 | // Fill sending buffer with data from string 67 | std::size_t counter = messageLengthString.length(); 68 | for (auto &it : msg) 69 | { 70 | msgBuf[counter] = (unsigned char)it; 71 | ++counter; 72 | } 73 | 74 | // Send all data in one send call 75 | int sendedBytes = Client.write((char *)msgBuf, messageLengthString.length() + messageLength); 76 | delete[] msgBuf; 77 | 78 | return ((size_t)sendedBytes == messageLengthString.length() + messageLength); 79 | } 80 | 81 | bool TCP_Client::WaitMessage(std::string &msg) 82 | { 83 | std::string messageSizeString; 84 | int receivedBytes; 85 | 86 | // We read the incoming data one byte at a time until we get the first byte with the first bit equal to 0, 87 | // which means that this is the last byte with the size. 88 | while (true) 89 | { 90 | while (Client.available() < 1 && Client.connected()) 91 | { 92 | volatile int i = 0; 93 | } 94 | 95 | uint8_t sizeByte; 96 | receivedBytes = Client.read(&sizeByte, 1); 97 | 98 | if (!Client.connected() || receivedBytes < 1) 99 | return false; 100 | 101 | messageSizeString += sizeByte; 102 | if ((sizeByte & 0b10000000) == 0) 103 | break; 104 | } 105 | 106 | std::size_t messageSize = 0; 107 | 108 | // Calculate message size 109 | for (auto it = messageSizeString.rbegin(); it != --messageSizeString.rend(); ++it) 110 | { 111 | messageSize += (unsigned char)*it & 0b01111111; 112 | messageSize *= 128; 113 | } 114 | 115 | messageSize += (unsigned char)messageSizeString[0] & 0b01111111; 116 | 117 | // Allocate memory to incoming message 118 | char *msgBuf = new char[messageSize]; 119 | 120 | #if defined(ESP8266) 121 | std::size_t counter = 0; 122 | while (counter < messageSize) 123 | { 124 | while (Client.available() < 1 && Client.connected()) { volatile int i = 0; } 125 | 126 | if (!Client.connected()) 127 | { 128 | delete[] msgBuf; 129 | return false; 130 | } 131 | 132 | msgBuf[counter] = Client.read(); 133 | ++counter; 134 | } 135 | #elif defined(ESP32) 136 | while (Client.available() < messageSize && Client.connected()) { volatile int i = 0; } 137 | 138 | if (!Client.connected()) 139 | return false; 140 | 141 | receivedBytes = Client.read((uint8_t*)msgBuf, messageSize); 142 | 143 | if ((std::size_t)receivedBytes != messageSize) 144 | { 145 | msg = ""; 146 | delete[] msgBuf; 147 | return false; 148 | } 149 | #endif 150 | 151 | 152 | msg.clear(); 153 | for (std::size_t i = 0; i < messageSize; ++i) 154 | msg += msgBuf[i]; 155 | 156 | delete[] msgBuf; 157 | return true; 158 | } 159 | 160 | bool TCP_Client::GetMessage(std::string &msg) 161 | { 162 | if (Client.available() != 0) 163 | { 164 | WaitMessage(msg); 165 | return true; 166 | } 167 | else 168 | return false; 169 | } 170 | 171 | void TCP_Client::DisconnectFromServer() 172 | { 173 | Client.stop(); 174 | } 175 | 176 | bool TCP_Client::IsConnectedToServer() 177 | { 178 | return Client.connected(); 179 | } 180 | -------------------------------------------------------------------------------- /EN_TCP_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_TCP_Client.h" 2 | 3 | namespace EN 4 | { 5 | int EN_TCP_Client::GetServerPort() 6 | { 7 | return ServerPort; 8 | } 9 | 10 | std::string EN_TCP_Client::GetServerIpAddress() 11 | { 12 | return ServerIpAddress; 13 | } 14 | 15 | EN_SOCKET EN_TCP_Client::GetSocket() 16 | { 17 | SocketMtx.lock(); 18 | EN_SOCKET tmpSock = ServerConnectionSocket; 19 | SocketMtx.unlock(); 20 | return tmpSock; 21 | } 22 | 23 | bool EN_TCP_Client::IsConnected() 24 | { 25 | SocketMtx.lock(); 26 | bool isConnected = (ServerConnectionSocket != INVALID_SOCKET); 27 | SocketMtx.unlock(); 28 | return isConnected; 29 | } 30 | 31 | 32 | bool EN_TCP_Client::Connect(std::string ipAddr, int port) 33 | { 34 | ServerIpAddress = GetIpByURL(ipAddr); 35 | if (ServerIpAddress == "") 36 | ServerIpAddress = ipAddr; 37 | 38 | ServerPort = port; 39 | 40 | sockaddr_in addr; 41 | addr.sin_family = AF_INET; 42 | addr.sin_port = htons(port); 43 | 44 | SocketMtx.lock(); 45 | 46 | if (ServerConnectionSocket != INVALID_SOCKET) 47 | { 48 | SocketMtx.unlock(); 49 | LOG(LogLevels::Warning, "You are trying to connect but you are alredy connected"); 50 | return false; 51 | } 52 | 53 | ServerConnectionSocket = socket(AF_INET, SOCK_STREAM, 0); 54 | 55 | if (ServerConnectionSocket == INVALID_SOCKET) 56 | { 57 | SocketMtx.unlock(); 58 | LOG(LogLevels::Error, "Error at socket: " + std::to_string(GetSocketErrorCode()) + " " + EN::GetSocketErrorString()); 59 | return false; 60 | } 61 | 62 | // Set ip address 63 | inet_pton(AF_INET, ServerIpAddress.c_str(), &addr.sin_addr); 64 | 65 | int operationRes = connect(ServerConnectionSocket, (sockaddr*)&addr, sizeof(addr)); 66 | 67 | if (operationRes != 0) 68 | { 69 | ServerConnectionSocket = INVALID_SOCKET; 70 | SocketMtx.unlock(); 71 | LOG(LogLevels::Warning, "Warning: failed connect to server"); 72 | return false; 73 | } 74 | 75 | SocketMtx.unlock(); 76 | 77 | // If reconnection we have to join last std::thread 78 | WaitForServerHandlerEnd(); 79 | 80 | for (SocketOption& opt : SocketOptions) 81 | EN::SetSocketOption(ServerConnectionSocket, opt.Level, opt.OptionName, opt.OptionValue); 82 | 83 | if (IsRunMessageHadlerThread) 84 | ServerHandlerThread = std::thread([this]() { this->ServerHandler(); }); 85 | 86 | return true; 87 | } 88 | 89 | void EN_TCP_Client::ServerHandler() 90 | { 91 | OnConnect(); 92 | 93 | bool isServerConnected = true; 94 | std::string message; 95 | 96 | while (true) 97 | { 98 | isServerConnected = TCP_Recv(ServerConnectionSocket, message); 99 | 100 | // Means what server was disconnected 101 | if (isServerConnected == false) 102 | { 103 | OnDisconnect(); 104 | 105 | SocketMtx.lock(); 106 | CloseSocket(ServerConnectionSocket); 107 | ServerConnectionSocket = INVALID_SOCKET; 108 | SocketMtx.unlock(); 109 | 110 | return; 111 | } 112 | 113 | ServerMessageHandler(message); 114 | } 115 | } 116 | 117 | void EN_TCP_Client::WaitForServerHandlerEnd() 118 | { 119 | ServerHandlerMtx.lock(); 120 | if (ServerHandlerThread.joinable() && IsRunMessageHadlerThread) 121 | ServerHandlerThread.join(); 122 | ServerHandlerMtx.unlock(); 123 | } 124 | 125 | bool EN_TCP_Client::SendToServer(std::string message) 126 | { 127 | SocketMtx.lock(); 128 | bool sendRes = TCP_Send(ServerConnectionSocket, message); 129 | SocketMtx.unlock(); 130 | return sendRes; 131 | } 132 | 133 | bool EN_TCP_Client::WaitMessage(std::string& message) 134 | { 135 | // Thread safety because this method should be called only inside handler thread or handler thread even not started 136 | return TCP_Recv(ServerConnectionSocket, message); 137 | } 138 | 139 | void EN_TCP_Client::Disconnect(bool isBlocking) 140 | { 141 | SocketMtx.lock(); 142 | CloseSocket(ServerConnectionSocket); 143 | SocketMtx.unlock(); 144 | 145 | ServerHandlerMtx.lock(); 146 | 147 | if (ServerHandlerThread.get_id() != std::this_thread::get_id() && ServerHandlerThread.joinable() && isBlocking) 148 | ServerHandlerThread.join(); 149 | 150 | ServerConnectionSocket = INVALID_SOCKET; 151 | 152 | ServerHandlerMtx.unlock(); 153 | } 154 | 155 | void EN_TCP_Client::SetSocketOption(int level, int optionName, int optionValue) 156 | { 157 | SocketOptions.push_back(SocketOption(level, optionName, optionValue)); 158 | } 159 | 160 | void EN_TCP_Client::SetSocketOption(PredefinedSocketOptions socketOptions) 161 | { 162 | for (size_t i = 0; i < socketOptions.Levels.size(); ++i) 163 | SocketOptions.push_back(SocketOption(socketOptions.Levels[i], socketOptions.OptionNames[i], socketOptions.OptionValues[i])); 164 | } 165 | 166 | void EN_TCP_Client::SetTCPSendFunction(bool (*TCPSendFunction)(EN_SOCKET, const std::string&)) 167 | { 168 | TCP_Send = TCPSendFunction; 169 | } 170 | 171 | void EN_TCP_Client::SetTCPRecvFunction(bool (*TCPRecvFunction)(EN_SOCKET, std::string&)) 172 | { 173 | TCP_Recv = TCPRecvFunction; 174 | } 175 | 176 | EN_TCP_Client::~EN_TCP_Client() 177 | { 178 | SocketMtx.lock(); 179 | if (ServerConnectionSocket != INVALID_SOCKET) 180 | { 181 | LOG(LogLevels::Error, "Error: You forgot to disconnect from the server. Use method Disconnect() to do this"); 182 | } 183 | 184 | CloseSocket(ServerConnectionSocket); 185 | SocketMtx.unlock(); 186 | 187 | WaitForServerHandlerEnd(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /EN_ThreadGate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace EN 9 | { 10 | // This class implements a gate for a thread. It works as condition variable, 11 | // but if the Open method was called in another thread before the Close method, 12 | // then the Close method will not block the thread. Doesn't make sense when working in more than two threads 13 | class EN_ThreadGate 14 | { 15 | private: 16 | std::mutex mtx, condVarMutex, lockMutex; 17 | std::condition_variable cv; 18 | bool IsActivated = true; 19 | public: 20 | // Blocks the execution of the thread until the Open method is called. 21 | // If the Open method was called before this method, then the thread is not blocked. 22 | // The Close and Open methods are synchronized with each other using a mutex 23 | void Close(); 24 | 25 | // Causes the thread to continue executing after the Close method. 26 | // If called before the Close method, then the Close method will not block the thread. 27 | // The Close and Open methods are synchronized with each other using a mutex 28 | void Open(); 29 | }; 30 | 31 | // This class implements a gate for a thread. It works as condition variable, 32 | // but if the Open method was called in another thread before the Close method, 33 | // then the Close method will not block the thread. Doesn't make sense when working in more than two threads. 34 | // Supports opening and closing counter. You can call the Open method N times and then the 35 | // Close method will block the execution of the thread only for N call 36 | class EN_RecursiveThreadGate 37 | { 38 | private: 39 | std::mutex mtx, condVarMutex, lockMutex; 40 | std::condition_variable cv; 41 | int ClosingAmount = 0; 42 | public: 43 | // Blocks the execution of the thread until the Open method is called. 44 | // If the Open method was called before this method, then the thread is not blocked. 45 | // The Close and Open methods are synchronized with each other using a mutex 46 | void Close(); 47 | 48 | // Causes the thread to continue executing after the Close method. 49 | // If called before the Close method, then the Close method will not block the thread. 50 | // The Close and Open methods are synchronized with each other using a mutex 51 | void Open(); 52 | }; 53 | 54 | // This class implements a gate for a thread. It works as condition variable, 55 | // but if the Open method was called in another thread before the Close method, 56 | // then the Close method will not block the thread. Doesn't make sense when working in more than two threads 57 | class EN_TimeThreadGate 58 | { 59 | private: 60 | std::mutex mtx, condVarMutex, lockMutex; 61 | std::condition_variable cv; 62 | bool IsActivated = true; 63 | public: 64 | // Blocks the execution of the thread until the Open method is called. 65 | // If the Open method was called before this method, then the thread is not blocked. 66 | // The Close and Open methods are synchronized with each other using a mutex 67 | void Close(); 68 | 69 | // Blocks the execution of the thread until the Open method is called or time's not up. 70 | // If the Open method was called before this method, then the thread is not blocked. 71 | // The Close and Open methods are synchronized with each other using a mutex. 72 | // After the time expires, the gate remains active, which means that the following Close method will block the thread. 73 | template 74 | void CloseFor(std::chrono::duration duration) 75 | { 76 | std::unique_lock lk(lockMutex); 77 | mtx.lock(); 78 | if (IsActivated) 79 | { 80 | condVarMutex.lock(); 81 | mtx.unlock(); 82 | cv.wait_for(lk, duration); 83 | condVarMutex.unlock(); 84 | mtx.lock(); 85 | } 86 | else 87 | { 88 | IsActivated = true; 89 | } 90 | mtx.unlock(); 91 | } 92 | 93 | // Blocks the execution of the thread until the Open method is called or the time has come for. 94 | // If the Open method was called before this method, then the thread is not blocked. 95 | // The Close and Open methods are synchronized with each other using a mutex. 96 | // After the time expires, the gate remains active, which means that the following Close method will block the thread. 97 | template 98 | void CloseUntil(std::chrono::time_point timePoint) 99 | { 100 | std::unique_lock lk(lockMutex); 101 | mtx.lock(); 102 | if (IsActivated) 103 | { 104 | condVarMutex.lock(); 105 | mtx.unlock(); 106 | cv.wait_until(lk, timePoint); 107 | condVarMutex.unlock(); 108 | mtx.lock(); 109 | } 110 | else 111 | { 112 | IsActivated = true; 113 | } 114 | mtx.unlock(); 115 | } 116 | 117 | // Causes the thread to continue executing after the Close method. 118 | // If called before the Close method or after CloseFor time expires, then the Close method will not block the thread. 119 | // The Close and Open methods are synchronized with each other using a mutex 120 | void Open(); 121 | 122 | // Causes the thread to continue executing after the CloseFor method. If the time is over before this method is called, then it will not do anything 123 | // If called before the Close method, then the Close method will block the thread. 124 | // The Close and Open methods are synchronized with each other using a mutex 125 | void OpenIfClosed(); 126 | }; 127 | } -------------------------------------------------------------------------------- /Examples/FT_Chat_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "../EN_TCP_Client.h" 2 | #include "../EN_ThreadBarrier.h" 3 | 4 | namespace EN 5 | { 6 | class EN_FT_Client; 7 | 8 | class EN_FT_Client_Internal_FileTransmitter : public EN::EN_TCP_Client 9 | { 10 | private: 11 | EN_FT_Client* FT_Client; 12 | protected: 13 | void OnConnect(); 14 | 15 | void ServerMessageHandler(std::string message); 16 | 17 | void OnDisconnect(); 18 | public: 19 | EN_FT_Client_Internal_FileTransmitter(EN_FT_Client* fT_Client); 20 | }; 21 | 22 | class EN_FT_Client_Internal_MessageTransmitter : public EN::EN_TCP_Client 23 | { 24 | private: 25 | EN_FT_Client* FT_Client; 26 | protected: 27 | void OnConnect(); 28 | 29 | void ServerMessageHandler(std::string message); 30 | 31 | void OnDisconnect(); 32 | public: 33 | EN_FT_Client_Internal_MessageTransmitter(EN_FT_Client* fT_Client); 34 | }; 35 | 36 | class EN_FT_Client 37 | { 38 | private: 39 | friend EN_FT_Client_Internal_MessageTransmitter; 40 | friend EN_FT_Client_Internal_FileTransmitter; 41 | 42 | EN_ThreadBarrier StartEndBarrier; 43 | bool IsConnectedToFTServer = true; 44 | protected: 45 | EN_FT_Client_Internal_MessageTransmitter MessageTransmitter; 46 | EN_FT_Client_Internal_FileTransmitter FileTransmitter; 47 | 48 | void OnConnect() 49 | { 50 | LOG(LogLevels::Info, "FT_Client connected!"); 51 | } 52 | 53 | void ServerMessageHandler(std::string message) 54 | { 55 | std::cout << "Message from server: " << message << std::endl; 56 | } 57 | 58 | void OnDisconnect() 59 | { 60 | LOG(LogLevels::Info, "FT_Client disconnected!"); 61 | } 62 | public: 63 | EN_FT_Client() : MessageTransmitter(this), FileTransmitter(this) {} 64 | 65 | bool IsConnected() 66 | { 67 | return (MessageTransmitter.IsConnected() || FileTransmitter.IsConnected()); 68 | } 69 | 70 | bool Connect(std::string serverIpAddress = "127.0.0.1", int messageTransmitterPort = 1111, int fileTransmitterPort = 1112) 71 | { 72 | if (!MessageTransmitter.Connect(serverIpAddress, messageTransmitterPort)) 73 | return false; 74 | 75 | if (!FileTransmitter.Connect(serverIpAddress, fileTransmitterPort)) 76 | { 77 | MessageTransmitter.Disconnect(); 78 | return false; 79 | } 80 | 81 | return true; 82 | } 83 | 84 | bool SendToServer(std::string message) 85 | { 86 | return MessageTransmitter.SendToServer(message); 87 | } 88 | 89 | bool WaitMessage(std::string& message) 90 | { 91 | return MessageTransmitter.WaitMessage(message); 92 | } 93 | 94 | void Disconnect() 95 | { 96 | FileTransmitter.Disconnect(false); 97 | MessageTransmitter.Disconnect(false); 98 | } 99 | }; 100 | 101 | EN_FT_Client_Internal_FileTransmitter::EN_FT_Client_Internal_FileTransmitter(EN_FT_Client* fT_Client) 102 | { 103 | FT_Client = fT_Client; 104 | } 105 | 106 | void EN_FT_Client_Internal_FileTransmitter::OnConnect() 107 | { 108 | LOG(LogLevels::Info, "Internal file transmitter connected!"); 109 | std::string ftSockDesc; 110 | // Wait first message with socket descriptor from file server 111 | WaitMessage(ftSockDesc); 112 | 113 | auto vec = Split(ftSockDesc); 114 | 115 | // Valid first message 116 | if (vec[0] != "FtSockDesc") 117 | FT_Client->IsConnectedToFTServer = false; // Set correct server var to false 118 | else 119 | FT_Client->MessageTransmitter.SendToServer(ftSockDesc); // Send file server socket descriptor to message server 120 | 121 | // Wait while message client connected 122 | FT_Client->StartEndBarrier.Wait(2); // Wait A 123 | // Wait while message client call FT_Client OnConnect function 124 | FT_Client->StartEndBarrier.Wait(2); // Wait B 125 | 126 | if (!FT_Client->IsConnectedToFTServer) 127 | { 128 | LOG(LogLevels::Info, "The wrong beginning of communication. The FT client did not connect to FT server"); 129 | FT_Client->Disconnect(); 130 | } 131 | } 132 | 133 | void EN_FT_Client_Internal_FileTransmitter::ServerMessageHandler(std::string message) 134 | { 135 | 136 | } 137 | 138 | void EN_FT_Client_Internal_FileTransmitter::OnDisconnect() 139 | { 140 | FT_Client->MessageTransmitter.Disconnect(false); 141 | 142 | LOG(LogLevels::Info, "Internal file transmitter disconnected!"); 143 | 144 | FT_Client->StartEndBarrier.Wait(2); 145 | } 146 | 147 | EN_FT_Client_Internal_MessageTransmitter::EN_FT_Client_Internal_MessageTransmitter(EN_FT_Client* fT_Client) 148 | { 149 | FT_Client = fT_Client; 150 | } 151 | 152 | void EN_FT_Client_Internal_MessageTransmitter::OnConnect() 153 | { 154 | LOG(LogLevels::Info, "Internal message transmitter connected!"); 155 | 156 | FT_Client->StartEndBarrier.Wait(2); // Wait A 157 | if (FT_Client->IsConnectedToFTServer) 158 | FT_Client->OnConnect(); 159 | FT_Client->StartEndBarrier.Wait(2); // Wait B 160 | } 161 | 162 | void EN_FT_Client_Internal_MessageTransmitter::ServerMessageHandler(std::string message) 163 | { 164 | FT_Client->ServerMessageHandler(message); 165 | } 166 | 167 | void EN_FT_Client_Internal_MessageTransmitter::OnDisconnect() 168 | { 169 | FT_Client->FileTransmitter.Disconnect(false); 170 | 171 | LOG(LogLevels::Info, "Internal message transmitter disconnected!"); 172 | 173 | FT_Client->StartEndBarrier.Wait(2); 174 | FT_Client->OnDisconnect(); 175 | } 176 | } 177 | 178 | 179 | int main() 180 | { 181 | EN::EN_FT_Client A; 182 | 183 | // Check if connection success 184 | if (A.Connect() == false) 185 | { 186 | LOG(EN::LogLevels::Info, "Failed to connect"); 187 | return 0; 188 | } 189 | 190 | std::string message; 191 | 192 | while (true) 193 | { 194 | // Get line from standart input 195 | getline(std::cin, message); 196 | 197 | // Stop while loop 198 | if (message == "f") 199 | break; 200 | 201 | // Disconnect to client 202 | if (message == "d") 203 | { 204 | A.Disconnect(); 205 | continue; 206 | } 207 | 208 | // Reconnect to client 209 | if (message == "r") 210 | A.Connect(); 211 | 212 | A.SendToServer(message); 213 | } 214 | 215 | // Disconnect client from server if still connected 216 | if (A.IsConnected()) 217 | A.Disconnect(); 218 | 219 | return 0; 220 | } -------------------------------------------------------------------------------- /EN_RAU_Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EN_Functions.h" 4 | #include "EN_TCP_Client.h" 5 | #include "EN_UDP_Client.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace EN 11 | { 12 | // EN_RAU_Client class definition 13 | class EN_RAU_Client; 14 | 15 | /// \cond HIDDEN_SYMBOLS 16 | class EN_RAU_TCP_Client : public EN_TCP_Client 17 | { 18 | public: 19 | friend EN_RAU_Client; 20 | EN_RAU_Client* RAU_Client; 21 | 22 | EN_RAU_TCP_Client(EN_RAU_Client* rau_Client); 23 | 24 | // A function to be defined by the user. It is used for logic after connection 25 | void OnConnect(); 26 | 27 | // A function to be defined by the user. It is used to process incoming messages from the server 28 | void ServerMessageHandler(std::string message); 29 | 30 | // A function to be defined by the user. Performed after disconnected from the server 31 | void OnDisconnect(); 32 | }; 33 | 34 | class EN_RAU_UDP_Client : public EN_UDP_Client 35 | { 36 | public: 37 | friend EN_RAU_Client; 38 | EN_RAU_Client* RAU_Client; 39 | 40 | EN_RAU_UDP_Client(EN_RAU_Client* rau_Client); 41 | 42 | void ServerMessageHandler(std::string message); 43 | }; 44 | /// \endcond 45 | 46 | 47 | /// Client class with connection and the possibility of reliable and unreliable sending and receiveng 48 | class EN_RAU_Client 49 | { 50 | private: 51 | // RAU_TCP_Client and RAU_UDP_Client pointers 52 | friend EN_RAU_TCP_Client; 53 | friend EN_RAU_UDP_Client; 54 | EN_RAU_TCP_Client* TCP_Client; 55 | EN_RAU_UDP_Client* UDP_Client; 56 | 57 | // Client number on the server 58 | int ClientId = -1; 59 | 60 | // Boolean variable for tracking the connection status. 61 | // When the server receives the client's IP address, 62 | // It will send a message via tcp and this variable will become true 63 | bool IsServerGetUDPAddress = false; 64 | 65 | // Variable to shutdown server 66 | bool IsShutdown = false; 67 | 68 | // Incoming messages buffer 69 | std::queue Messages; 70 | // Condition variable to wake up server message handler thread 71 | std::condition_variable CondVar; 72 | // Mutex to make code thread-safety 73 | std::mutex Mtx; 74 | 75 | // Incoming messages handler 76 | void QueueMessageHandler(); 77 | 78 | // Default port 79 | int ServerPort = 1111; 80 | 81 | // Server ip address string. Default set to localhost 82 | std::string ServerIpAddress = "127.0.0.1"; 83 | 84 | protected: 85 | /** 86 | \brief This function is called after connecting to the server 87 | 88 | \warning Must be defined by the user 89 | */ 90 | virtual void OnConnect() = 0; 91 | 92 | /** 93 | \brief The function processes all incoming messages 94 | 95 | 96 | \warning Must be defined by the user 97 | */ 98 | virtual void ServerMessageHandler(std::string message) = 0; 99 | 100 | /** 101 | \brief The function is called after disconnecting from the server. 102 | 103 | Important! If disconnection occurs from the server side or connection lost, the IsConnected() function returns false 104 | \warning Must be defined by the user 105 | */ 106 | virtual void OnDisconnect() = 0; 107 | 108 | public: 109 | EN_RAU_Client(); 110 | 111 | /// Server port getter 112 | int GetServerPort(); 113 | 114 | /// Server ip getter 115 | std::string GetServerIpAddress(); 116 | 117 | /// Function return true if client connected to server 118 | bool IsConnected(); 119 | 120 | /** 121 | \brief Connect to localhost and default port. 122 | \return Returns true in case of success, false otherwise 123 | */ 124 | bool Connect(); 125 | 126 | /** 127 | \brief Connect to localhost and current port. 128 | \param[in] port port to connect 129 | \return Returns true in case of success, false otherwise 130 | */ 131 | bool Connect(int port); 132 | 133 | /** 134 | \brief Connect to current ip and current port. 135 | \param[in] ip server ip address 136 | \param[in] port port to connect 137 | \return Returns true in case of success, false otherwise 138 | */ 139 | bool Connect(std::string ipAddr, int port); 140 | 141 | /** 142 | \brief Function for sending a message to a connected server. 143 | \param[in] message the message string to send to the server 144 | */ 145 | void SendToServer(std::string message, bool IsReliable = true); 146 | 147 | /// This function disconnect client from server 148 | void Disconnect(); 149 | 150 | /** 151 | \brief The method sets options for tcp(reliable) client socket 152 | 153 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 154 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 155 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 156 | \param[in] optionValue The value for the requested option is specified. 157 | */ 158 | void SetTCPSocketOption(int level, int optionName, int optionValue); 159 | 160 | /** 161 | \brief The method sets options for tcp(reliable) client socket 162 | 163 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 164 | The list of all predefined structures is in EN_SocketOptions.h. 165 | You can create your own sets of options using define or by creating structure objects 166 | */ 167 | void SetTCPSocketOption(PredefinedSocketOptions socketOptions); 168 | 169 | /** 170 | \brief The method sets options for udp(unreliable) client socket 171 | 172 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 173 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 174 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 175 | \param[in] optionValue The value for the requested option is specified. 176 | */ 177 | void SetUDPSocketOption(int level, int optionName, int optionValue); 178 | 179 | /** 180 | \brief The method sets options for udp(unreliable) client socket 181 | 182 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 183 | The list of all predefined structures is in EN_SocketOptions.h. 184 | You can create your own sets of options using define or by creating structure objects 185 | */ 186 | void SetUDPSocketOption(PredefinedSocketOptions socketOptions); 187 | 188 | virtual ~EN_RAU_Client(); 189 | }; 190 | } -------------------------------------------------------------------------------- /EN_UDP_Server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined WIN32 || defined _WIN64 4 | 5 | #include 6 | #include 7 | typedef SOCKET EN_SOCKET; 8 | 9 | #else 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | typedef int EN_SOCKET; 16 | #define INVALID_SOCKET -1 17 | #define SOCKET_ERROR -1 18 | 19 | #endif 20 | 21 | #include "EN_Functions.h" 22 | #include "EN_ThreadGate.h" 23 | #include "EN_SocketOptions.h" 24 | 25 | namespace EN 26 | { 27 | /// UDP server buffer types 28 | enum EN_UDP_ServerBuferType 29 | { 30 | Queue, ///< Queue. First in last out 31 | Stack ///< Stack. First in first out 32 | }; 33 | 34 | /// Base udp server class 35 | class EN_UDP_Server 36 | { 37 | private: 38 | // Variable to shutdown server 39 | std::atomic_bool IsShutdown; 40 | 41 | // Server socket 42 | EN_SOCKET UDP_ServerSocket = INVALID_SOCKET; 43 | 44 | // Vector with socket options 45 | std::vector SocketOptions; 46 | 47 | // Array of pointers 48 | std::list** QueueMessageVec; 49 | std::list** QueueAddrVec; 50 | std::list** QueueTimeVec; 51 | EN::EN_ThreadGate** GateVec; 52 | std::mutex** Mutexes; 53 | std::thread* ThreadVec; 54 | 55 | // Mutex to prevent errors while shutdown before run 56 | std::mutex ShutdownMutex; 57 | 58 | // Functions to handle incoming message buffer 59 | void ThreadListHandler(int ThreadID); 60 | 61 | // A pointer to a function for sending messages. Allows you to use custom network protocols. Send message to socket 62 | void (*UDP_Send)(EN_SOCKET sock, std::string destinationAddress, const std::string& message) = EN::Default_UDP_Send; 63 | 64 | // A pointer to a function for recv messages. Allows you to use custom network protocols. Recv message from socket 65 | bool (*UDP_Recv)(EN_SOCKET sock, std::string& sourceAddress, std::string& message) = EN::Default_UDP_Recv; 66 | protected: 67 | /// Server port. Default set to 1111 68 | int Port = 1111; 69 | 70 | /// Server ip address string. Default set to localhost 71 | std::string IpAddress = "127.0.0.1"; 72 | 73 | /// The number of threads in which incoming messages will be processed 74 | int ThreadAmount = 2; 75 | 76 | /** 77 | \brief The maximum size of the incoming message stack. 78 | 79 | Used only if ServerBuferType set to EN::Stack 80 | */ 81 | size_t MaxStackBuffSize = 16; 82 | 83 | /// Incoming message buffer type 84 | EN_UDP_ServerBuferType ServerBuferType = Queue; 85 | 86 | /** 87 | \brief Method that processes incoming messages 88 | 89 | This method processes a message from the buffer 90 | Get message, UDP client address and time in milliseconds since message come to server 91 | \warning Must be defined by the user 92 | */ 93 | virtual void ClientMessageHandler(std::string message, std::string ClientIpAddress, long long TimeWhenPackageArrived) = 0; 94 | 95 | /** 96 | \brief Method that processes incoming messages 97 | 98 | This method processes messages before they are placed in the buffer 99 | Get message, UDP client address and time in milliseconds since message come to server 100 | Return true if you want to put message into bufer, false otherwise 101 | \warning Must be defined by the user 102 | */ 103 | virtual bool InstantClientMessageHandler(std::string message, std::string ClientIpAddress, long long TimeWhenPackageArrived) = 0; 104 | 105 | public: 106 | EN_UDP_Server(); 107 | 108 | /// Port getter 109 | int GetPort(); 110 | 111 | /// Ip getter 112 | std::string GetIpAddr(); 113 | 114 | /** 115 | \brief Method to start server. Blocking call. 116 | 117 | Place this method in try block to catch errors. 118 | Throws socket errors. To get information about the error, use the documentation of your operating system. 119 | For Windows, errors go through WSAGetLastError, for Linux, errors go through errno. 120 | All errors with the description are duplicated in the log system. 121 | */ 122 | void Run(); 123 | 124 | /// Method that stops the server 125 | void Shutdown(); 126 | 127 | /** 128 | \brief function for sending a message to the client 129 | 130 | \param[in] message string to send to server 131 | \param[in] ClientSocketAddr string with server address. Format: 127.0.0.1:1111 132 | */ 133 | void SendToClient(std::string ClientIpAddress, std::string message); 134 | 135 | /** 136 | \brief The method sets options for client socket 137 | 138 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 139 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 140 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 141 | \param[in] optionValue The value for the requested option is specified. 142 | */ 143 | void SetSocketOption(int level, int optionName, int optionValue); 144 | 145 | /** 146 | \brief The method sets options for client socket 147 | 148 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 149 | The list of all predefined structures is in EN_SocketOptions.h. 150 | You can create your own sets of options using define or by creating structure objects 151 | */ 152 | void SetSocketOption(PredefinedSocketOptions socketOptions); 153 | 154 | /** 155 | \brief The method sets custom send function. Allow you use your own protocol 156 | 157 | \param[in] UDPSendFunction This parameter is a pointer to a function for sending messages to the socket. 158 | The function accepts the socket of the connected client, where you want to send 159 | the message and the message itself 160 | 161 | \warning If you want to use your protocol, use only one send call. 162 | This is necessary because the send function is thread-safe, 163 | but if you send one message to 2 send calls, then if 2 threads write to the same socket, 164 | then the data of two different messages may be mixed and you will receive errors 165 | */ 166 | void SetUDPSendFunction(void (*UDPSendFunction)(EN_SOCKET sock, std::string destinationAddress, const std::string& message)); 167 | 168 | /** 169 | \brief The method sets custom recv function. Allow you use your own protocol 170 | 171 | \param[in] UDPRecvFunction This parameter is a pointer to a function for receiving messages from the socket. 172 | The function accepts the socket of the connected client, where you want to recv from 173 | the message and the message itself 174 | */ 175 | void SetUDPRecvFunction(bool (*UDPRecvFunction)(EN_SOCKET sock, std::string& sourceAddress, std::string& message)); 176 | 177 | virtual ~EN_UDP_Server(); 178 | }; 179 | } 180 | -------------------------------------------------------------------------------- /EN_RAU_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_RAU_Server.h" 2 | 3 | namespace EN 4 | { 5 | // TCP server 6 | EN_RAU_TCP_Server::EN_RAU_TCP_Server(EN_RAU_Server* rau_Server) 7 | { 8 | RAU_Server = rau_Server; 9 | } 10 | 11 | void EN_RAU_TCP_Server::OnClientConnected(size_t ClientID) 12 | { 13 | SendToClient(ClientID, std::to_string(ClientID)); 14 | 15 | auto f1 = new std::queue; 16 | auto f2 = new std::condition_variable; 17 | 18 | if (ClientID >= RAU_Server->UDPIpAddresses.size()) 19 | { 20 | RAU_Server->VectorQueuesMessages.push_back(f1); 21 | 22 | RAU_Server->UDPIpAddresses.push_back("none"); 23 | 24 | RAU_Server->VectorCondVars.push_back(f2); 25 | 26 | RAU_Server->KillThreads.push_back(false); 27 | } 28 | else 29 | { 30 | RAU_Server->VectorQueuesMessages[ClientID] = f1; 31 | 32 | RAU_Server->UDPIpAddresses[ClientID] = "none"; 33 | 34 | RAU_Server->VectorCondVars[ClientID] = f2; 35 | 36 | RAU_Server->KillThreads[ClientID] = false; 37 | } 38 | 39 | std::thread QueueThreadHandler([this, ClientID]() {this->RAU_Server->ThreadQueueHandler(ClientID); }); 40 | QueueThreadHandler.detach(); 41 | } 42 | 43 | void EN_RAU_TCP_Server::ClientMessageHandler(std::string message, size_t ClientID) 44 | { 45 | RAU_Server->VectorQueuesMessages[ClientID]->push(message); 46 | RAU_Server->VectorCondVars[ClientID]->notify_all(); 47 | } 48 | 49 | void EN_RAU_TCP_Server::OnClientDisconnect(size_t ClientID) 50 | { 51 | RAU_Server->OnClientDisconnect(ClientID); 52 | RAU_Server->KillThreads[ClientID] = true; 53 | RAU_Server->VectorCondVars[ClientID]->notify_all(); 54 | 55 | Delay(300); 56 | 57 | delete RAU_Server->VectorQueuesMessages[ClientID]; 58 | delete RAU_Server->VectorCondVars[ClientID]; 59 | 60 | 61 | if (ClientID == RAU_Server->UDPIpAddresses.size() - 1) 62 | { 63 | RAU_Server->KillThreads.pop_back(); 64 | 65 | RAU_Server->VectorQueuesMessages.pop_back(); 66 | 67 | RAU_Server->UDPIpAddresses.pop_back(); 68 | 69 | RAU_Server->VectorCondVars.pop_back(); 70 | } 71 | else 72 | { 73 | RAU_Server->KillThreads[ClientID] = false; 74 | 75 | RAU_Server->VectorQueuesMessages[ClientID] = nullptr; 76 | 77 | RAU_Server->UDPIpAddresses[ClientID] = "none"; 78 | 79 | RAU_Server->VectorCondVars[ClientID] = nullptr; 80 | } 81 | } 82 | 83 | // UDP server 84 | EN_RAU_UDP_Server::EN_RAU_UDP_Server(EN_RAU_Server* rau_Server) 85 | { 86 | RAU_Server = rau_Server; 87 | } 88 | 89 | void EN_RAU_UDP_Server::ClientMessageHandler(std::string message, std::string ClientSocketAddr, long long TimeSincePackageArrived) {}; 90 | 91 | bool EN_RAU_UDP_Server::InstantClientMessageHandler(std::string message, std::string ClientSocketAddr, long long TimeWhenPackageArrived) 92 | { 93 | int threadId; 94 | if (!StringToInt(message.substr(0, message.find(" ")), threadId) || threadId < 0) 95 | return false; 96 | 97 | if (RAU_Server->UDPIpAddresses[threadId] == "none") 98 | { 99 | RAU_Server->UDPIpAddresses[threadId] = ClientSocketAddr; 100 | RAU_Server->TCP_Server->SendToClient(threadId, " "); 101 | RAU_Server->OnClientConnected(threadId); 102 | } 103 | else 104 | { 105 | RAU_Server->VectorQueuesMessages[threadId]->push(message.substr(message.find(" ") + 1)); 106 | RAU_Server->VectorCondVars[threadId]->notify_all(); 107 | } 108 | return false; 109 | } 110 | 111 | // RAU server 112 | EN_RAU_Server::EN_RAU_Server() 113 | { 114 | TCP_Server = new EN_RAU_TCP_Server(this); 115 | UDP_Server = new EN_RAU_UDP_Server(this); 116 | } 117 | 118 | int EN_RAU_Server::GetPort() { return Port; } 119 | 120 | std::string EN_RAU_Server::GetIpAddr() { return IpAddress; } 121 | 122 | size_t EN_RAU_Server::GetConnectionsCount() { return TCP_Server->GetConnectionsCount(); } 123 | 124 | void EN_RAU_Server::ThreadQueueHandler(int ClientID) 125 | { 126 | std::mutex mtx; 127 | std::unique_lock unique_lock_mutex(mtx); 128 | 129 | while (true) 130 | { 131 | while (!VectorQueuesMessages[ClientID]->empty()) 132 | { 133 | ClientMessageHandler(VectorQueuesMessages[ClientID]->front(), ClientID); 134 | VectorQueuesMessages[ClientID]->pop(); 135 | } 136 | 137 | if (IsShutdown || KillThreads[ClientID] == true) 138 | break; 139 | 140 | VectorCondVars[ClientID]->wait(unique_lock_mutex); 141 | 142 | if (IsShutdown || KillThreads[ClientID] == true) 143 | break; 144 | } 145 | } 146 | 147 | void EN_RAU_Server::Run() 148 | { 149 | // Setup ip and port on tcp server 150 | TCP_Server->IpAddress = IpAddress; 151 | TCP_Server->Port = Port; 152 | 153 | // Setup ip and port on udp server 154 | UDP_Server->IpAddress = IpAddress; 155 | UDP_Server->Port = Port + 1; 156 | UDP_Server->ThreadAmount = 1; 157 | UDP_Server->ServerBuferType = EN::Queue; 158 | 159 | std::thread UDP_Thread([this]() {this->UDP_Server->Run(); }); 160 | UDP_Thread.detach(); 161 | TCP_Server->Run(); 162 | } 163 | 164 | void EN_RAU_Server::DisconnectClient(size_t ClientID) 165 | { 166 | TCP_Server->DisconnectClient(ClientID); 167 | } 168 | 169 | void EN_RAU_Server::Shutdown() 170 | { 171 | TCP_Server->Shutdown(); 172 | UDP_Server->Shutdown(); 173 | IsShutdown = true; 174 | for (size_t i = 0; i < VectorCondVars.size(); i++) 175 | { 176 | VectorCondVars[i]->notify_all(); 177 | } 178 | } 179 | 180 | void EN_RAU_Server::SendToClient(size_t ClientId, std::string message, bool IsReliable) 181 | { 182 | if (UDPIpAddresses.size() > ClientId && UDPIpAddresses[ClientId] != "none") 183 | { 184 | if (IsReliable) 185 | TCP_Server->SendToClient(ClientId, message); 186 | else UDP_Server->SendToClient(UDPIpAddresses[ClientId], message); 187 | } 188 | else LOG(Warning, "The client is not connected"); 189 | } 190 | 191 | void EN_RAU_Server::SetTCPAcceptSocketOption(int level, int optionName, int optionValue) 192 | { 193 | TCP_Server->AddAcceptSocketOption(level, optionName, optionValue); 194 | } 195 | 196 | void EN_RAU_Server::SetTCPAcceptSocketOption(PredefinedSocketOptions socketOptions) 197 | { 198 | TCP_Server->AddAcceptSocketOption(socketOptions); 199 | } 200 | 201 | void EN_RAU_Server::AddOnTCPSocketCreateOption(int level, int optionName, int optionValue) 202 | { 203 | TCP_Server->AddOnSocketCreateOption(level, optionName, optionValue); 204 | } 205 | 206 | void EN_RAU_Server::AddOnTCPSocketCreateOption(PredefinedSocketOptions socketOptions) 207 | { 208 | TCP_Server->AddOnSocketCreateOption(socketOptions); 209 | } 210 | 211 | void EN_RAU_Server::SetTCPSocketOption(size_t ClientID, int level, int optionName, int optionValue) 212 | { 213 | TCP_Server->SetSocketOption(ClientID, level, optionName, optionValue); 214 | } 215 | 216 | void EN_RAU_Server::SetTCPSocketOption(size_t ClientID, PredefinedSocketOptions socketOptions) 217 | { 218 | TCP_Server->SetSocketOption(ClientID, socketOptions); 219 | } 220 | 221 | void EN_RAU_Server::SetUDPSocketOption(int level, int optionName, int optionValue) 222 | { 223 | UDP_Server->SetSocketOption(level, optionName, optionValue); 224 | } 225 | 226 | void EN_RAU_Server::SetUDPSocketOption(PredefinedSocketOptions socketOptions) 227 | { 228 | UDP_Server->SetSocketOption(socketOptions); 229 | } 230 | 231 | EN_RAU_Server::~EN_RAU_Server() 232 | { 233 | for (size_t i = 0; i < VectorQueuesMessages.size(); i++) 234 | { 235 | delete VectorQueuesMessages[i]; 236 | delete VectorCondVars[i]; 237 | } 238 | 239 | delete TCP_Server; 240 | delete UDP_Server; 241 | } 242 | } -------------------------------------------------------------------------------- /EN_Subprocess_Lin.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_Subprocess_Lin.h" 2 | 3 | namespace EN 4 | { 5 | void Subprocess::Launch(std::string programmName) 6 | { 7 | DataCounter.store(0); 8 | IsProcessEnded.store(false); 9 | 10 | if (pipe(PipeToChild)) 11 | { 12 | std::cerr << "Failed to create pipe!" << std::endl; 13 | return; 14 | } 15 | 16 | if (pipe(PipeFromChild)) 17 | { 18 | std::cerr << "Failed to create pipe!" << std::endl; 19 | return; 20 | } 21 | 22 | // Create fork of current process 23 | Pid = fork(); 24 | 25 | // Parent process 26 | if (Pid > (pid_t)0) 27 | { 28 | // Close unrequired pipes ends 29 | close(PipeToChild[0]); 30 | close(PipeFromChild[1]); 31 | 32 | Th = std::thread([&]() 33 | { 34 | // Array of descriptors to poll 35 | fd_set requiredPipeEndsToPoll; 36 | 37 | while (true) 38 | { 39 | // Zeroed set 40 | FD_ZERO(&requiredPipeEndsToPoll); 41 | // Add fdout[0] to poll set 42 | FD_SET(PipeFromChild[0], &requiredPipeEndsToPoll); 43 | 44 | // Wait data in PipeFromChild 45 | if (select(PipeFromChild[1] + 1, &requiredPipeEndsToPoll, NULL, NULL, NULL) == -1) 46 | { 47 | std::cerr << "Failed to select data from pipe! Errno: " << errno << std::endl; 48 | return; 49 | } 50 | 51 | // Check if it is data from child inside pipe 52 | if(FD_ISSET(PipeFromChild[0], &requiredPipeEndsToPoll)) 53 | { 54 | // Get available data 55 | int availableBytes; 56 | ioctl(PipeFromChild[0], FIONREAD, &availableBytes); 57 | 58 | // Allocate array 59 | char* bytes = new char [availableBytes]; 60 | 61 | // Read data from pipe 62 | int readed = read(PipeFromChild[0], bytes, availableBytes); 63 | 64 | // EOF and process finishing 65 | if (readed == 0) 66 | { 67 | // Thread safety append data to queue and trigger wait function 68 | while (!Mtx.try_lock()) 69 | Cv.notify_all(); 70 | 71 | IsProcessEnded.store(true); 72 | Mtx.unlock(); 73 | 74 | delete[] bytes; 75 | break; 76 | } 77 | else 78 | { 79 | if (readed != availableBytes) 80 | { 81 | std::cerr << "Failed to read data from pipe!" << std::endl; 82 | delete[] bytes; 83 | return; 84 | } 85 | else 86 | { 87 | // Thread safety append data to queue and trigger wait function 88 | while (!Mtx.try_lock()) 89 | Cv.notify_all(); 90 | 91 | MessagesQueue.push(std::string(bytes, readed)); 92 | DataCounter.fetch_add(1); 93 | Mtx.unlock(); 94 | } 95 | } 96 | 97 | delete[] bytes; 98 | } 99 | } 100 | }); 101 | return; 102 | } 103 | 104 | // Child process 105 | if (Pid == (pid_t)0) 106 | { 107 | close(STDOUT_FILENO); 108 | close(STDIN_FILENO); 109 | 110 | close(PipeToChild[1]); 111 | close(PipeFromChild[0]); 112 | 113 | dup2(PipeToChild[0], STDIN_FILENO); 114 | dup2(PipeFromChild[1], STDOUT_FILENO); 115 | 116 | close(PipeToChild[0]); 117 | close(PipeFromChild[1]); 118 | 119 | std::string command = programmName + " 2>&1"; 120 | 121 | system(command.c_str()); 122 | 123 | exit(EXIT_SUCCESS); 124 | } 125 | 126 | // Error 127 | if (Pid < (pid_t)0) 128 | { 129 | std::cerr << "Failed to create fork!" << std::endl; 130 | return; 131 | } 132 | } 133 | 134 | void Subprocess::SendData(std::string data, bool isRequiredNewLineSymbol) 135 | { 136 | Mtx.lock(); 137 | 138 | if (IsProcessEnded.load()) 139 | { 140 | Mtx.unlock(); 141 | return; 142 | } 143 | 144 | if (isRequiredNewLineSymbol) 145 | data += '\n'; 146 | 147 | write(PipeToChild[1], data.c_str(), data.length()); 148 | Mtx.unlock(); 149 | } 150 | 151 | std::string Subprocess::GetData(bool isRemoveNewLineSymbols) 152 | { 153 | std::string res = ""; 154 | Mtx.lock(); 155 | if (!MessagesQueue.empty()) 156 | { 157 | res = MessagesQueue.front(); 158 | MessagesQueue.pop(); 159 | DataCounter.fetch_sub(1); 160 | } 161 | Mtx.unlock(); 162 | 163 | if (isRemoveNewLineSymbols) 164 | { 165 | // Remove LF from res 166 | if(!res.empty()) 167 | res.pop_back(); 168 | } 169 | 170 | return res; 171 | } 172 | 173 | std::string Subprocess::WaitData(bool isRemoveNewLineSymbols) 174 | { 175 | std::string res = ""; 176 | std::mutex cvMtx; 177 | std::unique_lock lk(cvMtx); 178 | 179 | StartWaiting: 180 | Mtx.lock(); 181 | 182 | if (IsProcessEnded.load()) 183 | { 184 | Mtx.unlock(); 185 | return ""; 186 | } 187 | 188 | if (!MessagesQueue.empty()) 189 | { 190 | res = MessagesQueue.front(); 191 | MessagesQueue.pop(); 192 | DataCounter.fetch_sub(1); 193 | Mtx.unlock(); 194 | } 195 | else 196 | { 197 | Cv.wait(lk); 198 | Mtx.unlock(); 199 | goto StartWaiting; 200 | } 201 | 202 | if (isRemoveNewLineSymbols) 203 | { 204 | // Remove LF from res 205 | if(!res.empty()) 206 | res.pop_back(); 207 | } 208 | 209 | return res; 210 | } 211 | 212 | void Subprocess::StopProcess(std::string commandToSendToProcessToStop, bool isRequiredNewLineSymbol) 213 | { 214 | SendData(commandToSendToProcessToStop, isRequiredNewLineSymbol); 215 | Th.join(); 216 | 217 | int a; 218 | waitpid(Pid, &a, 0); 219 | close(PipeToChild[1]); 220 | close(PipeFromChild[0]); 221 | } 222 | 223 | bool Subprocess::IsData() 224 | { 225 | return DataCounter.load() > 1; 226 | } 227 | 228 | bool Subprocess::GetIsProcessEnded() 229 | { 230 | return IsProcessEnded.load(); 231 | } 232 | 233 | Subprocess::~Subprocess() 234 | { 235 | if (Th.joinable()) 236 | { 237 | std::cerr << "Thread was not joined! Higly possible thar you dont stop process" << std::endl; 238 | } 239 | } 240 | }; -------------------------------------------------------------------------------- /EN_Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_Logger.h" 2 | 3 | std::ofstream LogLevelInfoFile; 4 | std::ofstream LogLevelHintFile; 5 | std::ofstream LogLevelWarningFile; 6 | std::ofstream LogLevelErrorFile; 7 | 8 | std::mutex LoggerMtx; 9 | 10 | #if defined WIN32 || defined _WIN64 11 | HANDLE console_color = GetStdHandle(STD_OUTPUT_HANDLE); 12 | #endif 13 | 14 | class FileLogger 15 | { 16 | public: 17 | ~FileLogger() 18 | { 19 | LoggerMtx.lock(); 20 | 21 | LogLevelInfoFile.close(); 22 | LogLevelHintFile.close(); 23 | LogLevelWarningFile.close(); 24 | LogLevelErrorFile.close(); 25 | 26 | LoggerMtx.unlock(); 27 | } 28 | }; 29 | 30 | std::string LogLevelInfoFileName; 31 | std::string LogLevelHintFileName; 32 | std::string LogLevelWarningFileName; 33 | std::string LogLevelErrorFileName; 34 | 35 | FileLogger FL; 36 | 37 | namespace EN 38 | { 39 | int EnabledLogLevels = EN_LOG_LEVEL_INFO | EN_LOG_LEVEL_HINT | EN_LOG_LEVEL_WARNING | EN_LOG_LEVEL_ERROR; 40 | void (*LogFunc)(LogLevels, std::string) = DefaultLogFunc; 41 | 42 | void DefaultLogFunc(LogLevels logLevel, std::string logMessage) 43 | { 44 | switch (logLevel) 45 | { 46 | case Info: 47 | if (EnabledLogLevels & EN_LOG_LEVEL_INFO) 48 | { 49 | LoggerMtx.lock(); 50 | std::string msg = GetCurrentDate() + " " + GetCurrentDayTimeWithSecondFraction() + " [info] " + logMessage; 51 | std::cerr << msg << std::endl; 52 | if (!LogLevelInfoFileName.empty()) 53 | LogLevelInfoFile << msg << std::endl; 54 | } 55 | break; 56 | 57 | case Hint: 58 | if (EnabledLogLevels & EN_LOG_LEVEL_HINT) 59 | { 60 | LoggerMtx.lock(); 61 | std::string msg = GetCurrentDate() + " " + GetCurrentDayTimeWithSecondFraction() + " [hint] " + logMessage; 62 | 63 | #if defined WIN32 || defined _WIN64 64 | SetConsoleTextAttribute(console_color, 11); 65 | std::cerr << msg << std::endl; 66 | SetConsoleTextAttribute(console_color, 7); 67 | #else 68 | std::cerr << "\x1B[94m" << msg << "\033[0m" << std::endl; 69 | #endif 70 | 71 | if (!LogLevelHintFileName.empty()) 72 | LogLevelHintFile << msg << std::endl; 73 | } 74 | break; 75 | 76 | case Warning: 77 | if (EnabledLogLevels & EN_LOG_LEVEL_WARNING) 78 | { 79 | LoggerMtx.lock(); 80 | std::string msg = GetCurrentDate() + " " + GetCurrentDayTimeWithSecondFraction() + " [warning] " + logMessage; 81 | 82 | #if defined WIN32 || defined _WIN64 83 | SetConsoleTextAttribute(console_color, 14); 84 | std::cerr << msg << std::endl; 85 | SetConsoleTextAttribute(console_color, 7); 86 | #else 87 | std::cerr << "\x1B[33m" << msg << "\033[0m" << std::endl; 88 | #endif 89 | 90 | if (!LogLevelWarningFileName.empty()) 91 | LogLevelWarningFile << msg << std::endl; 92 | } 93 | break; 94 | 95 | case Error: 96 | if (EnabledLogLevels & EN_LOG_LEVEL_ERROR) 97 | { 98 | LoggerMtx.lock(); 99 | std::string msg = GetCurrentDate() + " " + GetCurrentDayTimeWithSecondFraction() + " [error] " + logMessage; 100 | 101 | #if defined WIN32 || defined _WIN64 102 | SetConsoleTextAttribute(console_color, 12); 103 | std::cerr << msg << std::endl; 104 | SetConsoleTextAttribute(console_color, 7); 105 | #else 106 | std::cerr << "\x1B[31m" << msg << "\033[0m" << std::endl; 107 | #endif 108 | 109 | if (!LogLevelErrorFileName.empty()) 110 | LogLevelErrorFile << msg << std::endl; 111 | } 112 | break; 113 | } 114 | LoggerMtx.unlock(); 115 | } 116 | 117 | void SetLogFunc(void (*logFunc)(LogLevels, std::string), int logLevelsToEnable) 118 | { 119 | LogFunc = logFunc; 120 | EnabledLogLevels = logLevelsToEnable; 121 | } 122 | 123 | void EnableLogLevels(int logLevelsToEnable) 124 | { 125 | EnabledLogLevels = logLevelsToEnable; 126 | } 127 | 128 | void SetLogLevelsFile(int logLevelsToEnable, std::string fileName, std::ios_base::openmode openMode) 129 | { 130 | if (logLevelsToEnable & EN_LOG_LEVEL_INFO) 131 | { 132 | LogLevelInfoFileName = fileName; 133 | LogLevelInfoFile.close(); 134 | 135 | if (!fileName.empty()) 136 | { 137 | LogLevelInfoFile.open(fileName, openMode); 138 | if (!LogLevelInfoFile.is_open()) 139 | { 140 | LOG(EN::LogLevels::Error, "Failed to create log level info log file with name: " + fileName); 141 | LogLevelInfoFileName.clear(); 142 | } 143 | } 144 | else LOG(EN::LogLevels::Error, "Can not create log file with empty name"); 145 | } 146 | 147 | if (logLevelsToEnable & EN_LOG_LEVEL_HINT) 148 | { 149 | LogLevelHintFileName = fileName; 150 | LogLevelHintFile.close(); 151 | 152 | if (!fileName.empty()) 153 | { 154 | LogLevelHintFile.open(fileName, openMode); 155 | if (!LogLevelHintFile.is_open()) 156 | { 157 | LOG(EN::LogLevels::Error, "Failed to create log level hint log file with name: " + fileName); 158 | LogLevelHintFileName.clear(); 159 | } 160 | } 161 | else LOG(EN::LogLevels::Error, "Can not create log file with empty name"); 162 | } 163 | 164 | if (logLevelsToEnable & EN_LOG_LEVEL_WARNING) 165 | { 166 | LogLevelWarningFileName = fileName; 167 | LogLevelWarningFile.close(); 168 | 169 | if (!fileName.empty()) 170 | { 171 | LogLevelWarningFile.open(fileName, openMode); 172 | if (!LogLevelWarningFile.is_open()) 173 | { 174 | LOG(EN::LogLevels::Error, "Failed to create log level warning log file with name: " + fileName); 175 | LogLevelWarningFileName.clear(); 176 | } 177 | } 178 | else LOG(EN::LogLevels::Error, "Can not create log file with empty name"); 179 | } 180 | 181 | if (logLevelsToEnable & EN_LOG_LEVEL_ERROR) 182 | { 183 | LogLevelErrorFileName = fileName; 184 | LogLevelErrorFile.close(); 185 | 186 | if (!fileName.empty()) 187 | { 188 | LogLevelErrorFile.open(fileName, openMode); 189 | if (!LogLevelErrorFile.is_open()) 190 | { 191 | LOG(EN::LogLevels::Error, "Failed to create log level error log file with name: " + fileName); 192 | LogLevelErrorFileName.clear(); 193 | } 194 | } 195 | else LOG(EN::LogLevels::Error, "Can not create log file with empty name"); 196 | } 197 | 198 | if (!(logLevelsToEnable & EN_LOG_LEVEL_INFO) && !(logLevelsToEnable & EN_LOG_LEVEL_HINT) && 199 | !(logLevelsToEnable & EN_LOG_LEVEL_WARNING) && !(logLevelsToEnable & EN_LOG_LEVEL_ERROR)) 200 | { 201 | LOG(EN::LogLevels::Warning, "Wrong log levels to log files"); 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /EN_TCP_Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined WIN32 || defined _WIN64 4 | 5 | #include 6 | #include 7 | typedef SOCKET EN_SOCKET; 8 | 9 | #else 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | typedef int EN_SOCKET; 16 | #define INVALID_SOCKET -1 17 | 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | #include "EN_Functions.h" 26 | #include "EN_SocketOptions.h" 27 | 28 | namespace EN 29 | { 30 | /// Base tcp client class 31 | class EN_TCP_Client 32 | { 33 | private: 34 | // Default port 35 | int ServerPort = 1111; 36 | 37 | // Server ip address string. Default set to localhost 38 | std::string ServerIpAddress = "127.0.0.1"; 39 | 40 | // Socket to connect to server. Library wrapper for your operating system socket 41 | EN_SOCKET ServerConnectionSocket = INVALID_SOCKET; 42 | 43 | // Thread to handle incoming from server messages 44 | std::thread ServerHandlerThread; 45 | 46 | // Mutex to synchronize server handler logic 47 | std::mutex ServerHandlerMtx; 48 | 49 | // Mutex to synchronize socket logic 50 | std::mutex SocketMtx; 51 | 52 | // Vector with options to be set on client socket 53 | std::vector SocketOptions; 54 | 55 | // The server's internal method for processing incoming messages. 56 | // Passes the incoming string to method ServerMessageHandler to interpretate incoming message 57 | void ServerHandler(); 58 | 59 | // This method will wait until thread handler return 60 | void WaitForServerHandlerEnd(); 61 | 62 | // A pointer to a function for sending messages. Allows you to use custom network protocols. Send message to socket 63 | bool (*TCP_Send)(EN_SOCKET sock, const std::string& message) = EN::Default_TCP_Send; 64 | 65 | // A pointer to a function for recv messages. Allows you to use custom network protocols. Recv message from socket 66 | bool (*TCP_Recv)(EN_SOCKET sock, std::string& message) = EN::Default_TCP_Recv; 67 | protected: 68 | /** 69 | Variable for disabling the incoming message handler. 70 | If it is true, then all incoming messages will be processed inside the ServerMessageHandler function, 71 | after connecting to the server, the OnConnect method will be called, 72 | and after disconnecting from the server, the onDisconnect method will be called. 73 | If this variable is false, then the OnConnect, ServerMessageHandler, 74 | onDisconnect methods will be called in another thread. 75 | Use it if you don't need to process messages in another thread. 76 | In this mode, the WaitMessage method can be used in the main thread. 77 | */ 78 | bool IsRunMessageHadlerThread = true; 79 | 80 | /** 81 | \brief This function is called after connecting to the server 82 | 83 | \warning Must be defined by the user 84 | */ 85 | virtual void OnConnect() = 0; 86 | 87 | /** 88 | \brief The function processes all incoming messages 89 | 90 | 91 | \warning Must be defined by the user 92 | */ 93 | virtual void ServerMessageHandler(std::string message) = 0; 94 | 95 | /** 96 | \brief The function is called after disconnecting from the server. 97 | 98 | \warning Must be defined by the user 99 | */ 100 | virtual void OnDisconnect() = 0; 101 | 102 | public: 103 | /// Server port getter 104 | int GetServerPort(); 105 | 106 | /// Server ip getter 107 | std::string GetServerIpAddress(); 108 | 109 | /// Socket getter 110 | EN_SOCKET GetSocket(); 111 | 112 | /// Function return true if client connected to server 113 | bool IsConnected(); 114 | 115 | /** 116 | \brief Connect to current ip and current port. 117 | \param[in] ip server ip address. Default is 127.0.0.1 118 | \param[in] port port to connect. Default is 1111 119 | \return Returns true in case of success, false otherwise 120 | */ 121 | bool Connect(std::string ipAddr = "127.0.0.1", int port = 1111); 122 | 123 | /** 124 | \brief Function for sending a message to a connected server. 125 | \param[in] message the message string to send to the server 126 | */ 127 | bool SendToServer(std::string message); 128 | 129 | /** 130 | \brief Method that wait new incoming message from client 131 | 132 | \warning Since the ServerMessageHandler runs in a separate thread, the call to the WaitMessage method must be in the same thread. 133 | This is necessary so that there is no waiting for a new message in different threads, which leads to undefined behavior. 134 | Note that you still can use this function on OnConnect and OnDisconnect methods. 135 | If the variable IsRunMessageHadlerThread is false, then this method can also be used in the main thread 136 | 137 | \param[in] message The string to store incoming message 138 | \return Returns true in case of success, false if it was disconnection 139 | */ 140 | bool WaitMessage(std::string& message); 141 | 142 | /** 143 | \brief This function disconnect client from server. 144 | 145 | \param[in] isBlocking An optional parameter required to define the function. 146 | This function can be called in the message processing thread or in any other thread. 147 | If the function is called in a message processing thread, then it will not block it. 148 | If the function is called in any other thread, 149 | it will wait for the execution of the incoming message processing thread to complete. 150 | In this case, the input parameter allows you to determine the behavior of the function if necessary 151 | */ 152 | void Disconnect(bool isBlocking = true); 153 | 154 | /** 155 | \brief The method sets options for client socket 156 | 157 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 158 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 159 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 160 | \param[in] optionValue The value for the requested option is specified. 161 | */ 162 | void SetSocketOption(int level, int optionName, int optionValue); 163 | 164 | /** 165 | \brief The method sets options for client socket 166 | 167 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 168 | The list of all predefined structures is in EN_SocketOptions.h. 169 | You can create your own sets of options using define or by creating structure objects 170 | */ 171 | void SetSocketOption(PredefinedSocketOptions socketOptions); 172 | 173 | /** 174 | \brief The method sets custom send function. Allow you use your own protocol 175 | 176 | \param[in] TCPSendFunction This parameter is a pointer to a function for sending messages to the socket. 177 | The function accepts the socket of the connected client, where you want to send 178 | the message and the message itself 179 | 180 | \warning If you want to use your protocol, use only one send call. 181 | This is necessary because the send function is thread-safe, 182 | but if you send one message to 2 send calls, then if 2 threads write to the same socket, 183 | then the data of two different messages may be mixed and you will receive errors 184 | */ 185 | void SetTCPSendFunction(bool (*TCPSendFunction)(EN_SOCKET, const std::string&)); 186 | 187 | /** 188 | \brief The method sets custom recv function. Allow you use your own protocol 189 | 190 | \param[in] TCPRecvFunction This parameter is a pointer to a function for receiving messages from the socket. 191 | The function accepts the socket of the connected client, where you want to recv from 192 | the message and the message itself 193 | */ 194 | void SetTCPRecvFunction(bool (*TCPRecvFunction)(EN_SOCKET, std::string&)); 195 | 196 | // Default destructor 197 | virtual ~EN_TCP_Client(); 198 | }; 199 | } 200 | 201 | -------------------------------------------------------------------------------- /EN_UDP_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_UDP_Server.h" 2 | 3 | 4 | namespace EN 5 | { 6 | EN_UDP_Server::EN_UDP_Server() 7 | { 8 | IsShutdown.store(false); 9 | } 10 | 11 | int EN_UDP_Server::GetPort() 12 | { 13 | return Port; 14 | } 15 | 16 | std::string EN_UDP_Server::GetIpAddr() 17 | { 18 | return IpAddress; 19 | } 20 | 21 | void EN_UDP_Server::ThreadListHandler(int ThreadID) 22 | { 23 | while (true) 24 | { 25 | if (IsShutdown.load()) 26 | return; 27 | 28 | while (!QueueMessageVec[ThreadID]->empty()) 29 | { 30 | std::chrono::milliseconds elapsed_seconds = std::chrono::duration_cast(std::chrono::system_clock::now() - QueueTimeVec[ThreadID]->front()); 31 | 32 | std::string TopMessage = QueueMessageVec[ThreadID]->front(); 33 | std::string sourceAddress = QueueAddrVec[ThreadID]->front(); 34 | 35 | switch (ServerBuferType) 36 | { 37 | case (Stack): 38 | 39 | Mutexes[ThreadID]->lock(); 40 | QueueMessageVec[ThreadID]->pop_front(); 41 | QueueAddrVec[ThreadID]->pop_front(); 42 | QueueTimeVec[ThreadID]->pop_front(); 43 | Mutexes[ThreadID]->unlock(); 44 | 45 | ClientMessageHandler(TopMessage, sourceAddress, elapsed_seconds.count()); 46 | break; 47 | 48 | case (Queue): 49 | ClientMessageHandler(TopMessage, sourceAddress, elapsed_seconds.count()); 50 | 51 | Mutexes[ThreadID]->lock(); 52 | QueueMessageVec[ThreadID]->pop_front(); 53 | QueueAddrVec[ThreadID]->pop_front(); 54 | QueueTimeVec[ThreadID]->pop_front(); 55 | Mutexes[ThreadID]->unlock(); 56 | break; 57 | } 58 | } 59 | 60 | GateVec[ThreadID]->Close(); 61 | } 62 | } 63 | 64 | void EN_UDP_Server::Run() 65 | { 66 | ShutdownMutex.lock(); 67 | //Create a socket 68 | if ((UDP_ServerSocket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) 69 | { 70 | LOG(Error, "Could not create socket"); 71 | throw (std::runtime_error(std::to_string(GetSocketErrorCode()))); 72 | } 73 | 74 | // Server address 75 | sockaddr_in ServerAddress; 76 | 77 | //Prepare the sockaddr_in structure 78 | ServerAddress.sin_family = AF_INET; 79 | ServerAddress.sin_port = htons(Port); 80 | 81 | // Set ip address 82 | inet_pton(AF_INET, IpAddress.c_str(), &ServerAddress.sin_addr); 83 | 84 | // Bind 85 | if (bind(UDP_ServerSocket, (sockaddr*)&ServerAddress, sizeof(ServerAddress)) == SOCKET_ERROR) 86 | { 87 | LOG(Error, "Bind failed"); 88 | throw (std::runtime_error(std::to_string(GetSocketErrorCode()))); 89 | } 90 | 91 | for (SocketOption& opt : SocketOptions) 92 | EN::SetSocketOption(UDP_ServerSocket, opt.Level, opt.OptionName, opt.OptionValue); 93 | 94 | QueueMessageVec = new std::list*[ThreadAmount]; 95 | QueueAddrVec = new std::list*[ThreadAmount]; 96 | QueueTimeVec = new std::list*[ThreadAmount]; 97 | GateVec = new EN::EN_ThreadGate*[ThreadAmount]; 98 | ThreadVec = new std::thread[ThreadAmount]; 99 | Mutexes = new std::mutex*[ThreadAmount]; 100 | 101 | for (int i = 0; i < ThreadAmount; i++) 102 | { 103 | QueueMessageVec[i] = new std::list; 104 | QueueAddrVec[i] = new std::list; 105 | QueueTimeVec[i] = new std::list; 106 | GateVec[i] = new EN::EN_ThreadGate; 107 | Mutexes[i] = new std::mutex; 108 | 109 | ThreadVec[i] = std::thread([this, i]() {this->ThreadListHandler(i); }); 110 | } 111 | 112 | ShutdownMutex.unlock(); 113 | 114 | std::string message, clientAddress; 115 | bool recvRes; 116 | 117 | // Keep listening for data 118 | while (true) 119 | { 120 | recvRes = UDP_Recv(UDP_ServerSocket, clientAddress, message); 121 | 122 | if (IsShutdown.load() || !recvRes) 123 | break; 124 | 125 | if (InstantClientMessageHandler(message, clientAddress, 0) == false) 126 | continue; 127 | 128 | int IndexMinQueue = 0; 129 | for (int i = 1; i < ThreadAmount; i++) 130 | if (QueueMessageVec[i]->size() < QueueMessageVec[IndexMinQueue]->size()) 131 | IndexMinQueue = i; 132 | 133 | switch (ServerBuferType) 134 | { 135 | case Queue: 136 | Mutexes[IndexMinQueue]->lock(); 137 | QueueMessageVec[IndexMinQueue]->push_back(message); 138 | QueueAddrVec[IndexMinQueue]->push_back(clientAddress); 139 | QueueTimeVec[IndexMinQueue]->push_back(std::chrono::system_clock::now()); 140 | Mutexes[IndexMinQueue]->unlock(); 141 | 142 | GateVec[IndexMinQueue]->Open(); 143 | break; 144 | 145 | case Stack: 146 | Mutexes[IndexMinQueue]->lock(); 147 | QueueMessageVec[IndexMinQueue]->push_front(message); 148 | QueueAddrVec[IndexMinQueue]->push_front(clientAddress); 149 | QueueTimeVec[IndexMinQueue]->push_front(std::chrono::system_clock::now()); 150 | 151 | if (QueueMessageVec[IndexMinQueue]->size() > MaxStackBuffSize) 152 | { 153 | QueueMessageVec[IndexMinQueue]->pop_back(); 154 | QueueAddrVec[IndexMinQueue]->pop_back(); 155 | QueueTimeVec[IndexMinQueue]->pop_back(); 156 | } 157 | 158 | Mutexes[IndexMinQueue]->unlock(); 159 | GateVec[IndexMinQueue]->Open(); 160 | break; 161 | } 162 | } 163 | 164 | for (int i = 0; i < ThreadAmount; i++) 165 | ThreadVec[i].join(); 166 | 167 | for (int i = 0; i < ThreadAmount; i++) 168 | { 169 | delete QueueMessageVec[i]; 170 | delete QueueAddrVec[i]; 171 | delete GateVec[i]; 172 | delete QueueTimeVec[i]; 173 | delete Mutexes[i]; 174 | } 175 | 176 | delete[] QueueMessageVec; 177 | delete[] QueueAddrVec; 178 | delete[] GateVec; 179 | delete[] ThreadVec; 180 | delete[] QueueTimeVec; 181 | delete[] Mutexes; 182 | 183 | CloseSocket(UDP_ServerSocket); 184 | 185 | if (!IsShutdown) 186 | { 187 | int lastErrorCode = GetSocketErrorCode(); 188 | LOG(Error, "Error: Error on socket: " + std::to_string(lastErrorCode) + " " + EN::GetSocketErrorString(lastErrorCode)); 189 | throw (std::runtime_error(std::to_string(lastErrorCode))); 190 | } 191 | } 192 | 193 | void EN_UDP_Server::Shutdown() 194 | { 195 | IsShutdown.store(true); 196 | 197 | // Check what server successfully started 198 | while (true) 199 | { 200 | ShutdownMutex.lock(); 201 | if (UDP_ServerSocket != INVALID_SOCKET) 202 | { 203 | ShutdownMutex.unlock(); 204 | break; 205 | } 206 | ShutdownMutex.unlock(); 207 | } 208 | 209 | CloseSocket(UDP_ServerSocket); 210 | 211 | for (int i = 0; i < ThreadAmount; i++) 212 | GateVec[i]->Open(); 213 | } 214 | 215 | void EN_UDP_Server::SendToClient(std::string ClientIpAddress, std::string message) 216 | { 217 | UDP_Send(UDP_ServerSocket, ClientIpAddress, message); 218 | } 219 | 220 | void EN_UDP_Server::SetSocketOption(int level, int optionName, int optionValue) 221 | { 222 | ShutdownMutex.lock(); 223 | if (UDP_ServerSocket == INVALID_SOCKET) 224 | EN::SetSocketOption(UDP_ServerSocket, level, optionName, optionValue); 225 | else 226 | SocketOptions.push_back(SocketOption(level, optionName, optionValue)); 227 | ShutdownMutex.unlock(); 228 | } 229 | 230 | void EN_UDP_Server::SetSocketOption(PredefinedSocketOptions socketOptions) 231 | { 232 | ShutdownMutex.lock(); 233 | if (UDP_ServerSocket == INVALID_SOCKET) 234 | for (size_t i = 0; i < socketOptions.Levels.size(); ++i) 235 | SocketOptions.push_back(SocketOption(socketOptions.Levels[i], socketOptions.OptionNames[i], socketOptions.OptionValues[i])); 236 | else 237 | for (size_t i = 0; i < socketOptions.Levels.size(); ++i) 238 | EN::SetSocketOption(UDP_ServerSocket, socketOptions.Levels[i], socketOptions.OptionNames[i], socketOptions.OptionValues[i]); 239 | ShutdownMutex.unlock(); 240 | } 241 | 242 | void EN_UDP_Server::SetUDPSendFunction(void (*UDPSendFunction)(EN_SOCKET sock, std::string destinationAddress, const std::string& message)) 243 | { 244 | UDP_Send = UDPSendFunction; 245 | } 246 | 247 | void EN_UDP_Server::SetUDPRecvFunction(bool (*UDPRecvFunction)(EN_SOCKET sock, std::string& sourceAddress, std::string& message)) 248 | { 249 | UDP_Recv = UDPRecvFunction; 250 | } 251 | 252 | EN_UDP_Server::~EN_UDP_Server() {} 253 | } 254 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | \mainpage 2 | 3 | # Refactoring and restructuring of the project will begin in H2 2024 4 | 5 | # Easy_Network 6 | Simple C++ network library 7 | # Library documentation: 8 | https://mrognor.github.io/Easy_Network_Documentation/doc/html/index.html 9 | 10 | # Library roadmap 11 | ![VeSsGO69PFQ](https://github.com/mrognor/Easy_Network/assets/47296449/6ae43c96-ed24-4081-a87c-24d012821de5) 12 | 13 | # Library usage 14 | Before you start, I advise you to download the code examples. For linux, you can save files to one folder, and for visual studio, you will need to add these files to different projects 15 | Examples available here: https://github.com/mrognor/Easy_Network/tree/master/Examples 16 | 17 | ## Visual Studio library usage guide 18 | 1. Download the latest release from github 19 | 2. Create a new project in visual studio 20 | 3. Click project -> properties 21 | 22 | ![VisualStudioProjectProperties](https://user-images.githubusercontent.com/47296449/165805905-60992708-92ed-4107-a858-e94e13581369.png) 23 | 24 | 4. Make sure that the configuration is selected to "All Configurations" 25 | 26 | Open menu on C/C++ -> General -> Additional Include Directories 27 | and add the path to the include library folder there 28 | 29 | ![VisualStudioProjectInclude](https://user-images.githubusercontent.com/47296449/165806651-ee06a01e-4438-4b5b-a2e9-bf17fd1a938d.png) 30 | 31 | 5. Open menu on Linker -> General -> Additional Library Directories 32 | and add the path to the lib library folder there 33 | 34 | ![image](https://user-images.githubusercontent.com/47296449/165807739-68487caa-84ea-4caf-8630-63198174dea2.png) 35 | 36 | 6. Open menu on Linker -> Input -> Additional Dependencies and add "EasyNetwork.lib" here. Note that you must specify the correct file name for each build configuration. For example, to compile the debug x86 version, you must specify "EasyNetwork-x86d.lib", to release x86 - "EasyNetwork-x86.lib", to debug x64 - "EasyNetwork-x64d.lib", to release x64 - "EasyNetwork-x64.lib". Also add "ws2_32.lib" 37 | image 38 | 39 | 7. The configuration of the project for working with the library is completed. Now you can use the code samples and try to make own programm 40 | 41 | ## MinGW library usage guide 42 | 1. Download the latest release from github 43 | 2. You can extract library release archive to any folder 44 | 3. Now all ready to compile your project 45 | You can compile server using command: 46 | `g++ -pthread -std=c++11 -lws2_32 PathToServer/Server.cpp -IEasyNetworkDir/ -LEasyNetworkDir/bin -lEasyNetwork -o server` 47 | You can compile client using command: 48 | `g++ -pthread -std=c++11 -lws2_32 PathToClient/Client.cpp -IEasyNetworkDir/ -LEasyNetworkDir/bin -lEasyNetwork -o client` 49 | 4. Run your programs using command: 50 | `server.exe` and `client.exe` 51 | 52 | ## Linux library usage 53 | 1. Download the latest release from github 54 | 2. You can extract library release archive to any folder 55 | 3. Now all ready to compile your project 56 | You can compile server using command: 57 | `g++ -pthread -std=c++11 PathToServer/Server.cpp -IEasyNetworkDir/ -LEasyNetworkDir/bin -lEasyNetwork -o server` 58 | You can compile client using command: 59 | `g++ -pthread -std=c++11 PathToClient/Client.cpp -IEasyNetworkDir/ -LEasyNetworkDir/bin -lEasyNetwork -o client` 60 | 4. Run your programs using command: 61 | `./server` and `./client` 62 | 63 | # Important 64 | I know that no one reads further than the readme, so I want to bring some important things here 65 | 1. When working with linux, you may notice that std::cout does not 66 | output characters to the console. Don't worry, 67 | your code works as it should, you can check it yourself with cerr. 68 | 2. On linux, after disconnecting clients from the server, 69 | the port may be blocked for a small amount of time. 70 | Due to when trying to start the server again, 71 | errors will come out. We just have to wait a little 72 | 3. The UDP server cannot send a message to the client if it has not received a 73 | message from this client before. This does not apply to unreliable sending of 74 | messages in rau classes 75 | 4. Note that the maximum size of a udp packet that can be guaranteed to be sent and received is 508 bytes. You can send more, but then the message may not go away 76 | 77 | # Free white ip 78 | White ip allow you to use your programs outside your local network. 79 | You can use free utility named [ngrok](https://ngrok.com/). This program let you free white ip. 80 | You can launch ngrok using command `ngrok tcp 1111`. You can use any protocol and port you want. 81 | Note that when using this program, URLs will be provided to you, but ip is used to connect. To solve this problem, use the `GetIpByURL` function 82 | 83 | # How to choose a class 84 | This is a small overview of the library classes to simplify class selection 85 | 86 | ## TCP classes 87 | These classes support connection and have got only reliable message sending. 88 | ### EN_TCP_Server 89 | You have to define 3 methods 90 | 1. void OnClientConnected(EN_SOCKET clientSocket) 91 | 2. void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) 92 | 3. void OnClientDisconnect(EN_SOCKET clientSocket) 93 | 94 | The server ip address and port are set in the constructor. 95 | To start the server, you need to call the Run method 96 | An example of using the class can be found here: https://github.com/mrognor/Easy_Network/blob/master/Examples/TCP_Chat_Server.cpp 97 | 98 | ### EN_TCP_Client 99 | You have to define 3 methods 100 | 1. void OnConnect() 101 | 2. void ServerMessageHandler(std::string message) 102 | 3. void OnDisconnect() 103 | 104 | To connect to the server, you need to call the function Connect. 105 | Before shutting down the program, you need to disconnect from the server using the method Disconnect 106 | An example of using the class can be found here: https://github.com/mrognor/Easy_Network/blob/master/Examples/TCP_Chat_Client.cpp 107 | 108 | ## UDP classes 109 | These classes do not support connection and have got only unreliable message sending. 110 | ### EN_UDP_Server 111 | You have to define 2 methods 112 | 1. void ClientMessageHandler(std::string message, std::string ClientIpAddress, long long TimeSincePackageArrived) 113 | 2. bool InstantClientMessageHandler(std::string message, std::string ClientIpAddress, long long TimeWhenPackageArrived) 114 | 115 | The entire configuration of the server takes place in the constructor. 116 | To start the server, you must call the method Run. 117 | To stop the server, you need to call the method Shutdown. 118 | An example of using the class can be found here: https://github.com/mrognor/Easy_Network/blob/master/Examples/UDP_Chat_Server.cpp 119 | 120 | ### EN_UDP_Client 121 | You have to define 1 method 122 | 1. void ServerMessageHandler(std::string message) 123 | 124 | To start the client's work, you must call the method Run. 125 | To complete the work, you must call the method Close. 126 | An example of using the class can be found here: https://github.com/mrognor/Easy_Network/blob/master/Examples/UDP_Chat_Client.cpp 127 | 128 | ## RAU classes 129 | This class supports connection. There is both reliable sending of messages and unreliable. 130 | The RAU differs from the TCP only in the ability to unreliable message send. 131 | ### EN_RAU_Server 132 | You have to define 3 methods 133 | 1. void OnClientConnected(int ClientID) 134 | 2. void ClientMessageHandler(std::string message, int ClientID) 135 | 3. void OnClientDisconnect(int ClientID) 136 | 137 | The entire configuration of the server takes place in the constructor 138 | To start the server, you need to call the Run method 139 | An example of using the class can be found here: https://github.com/mrognor/Easy_Network/blob/master/Examples/RAU_Chat_Server.cpp 140 | 141 | ### EN_RAU_Client 142 | You have to define 3 methods 143 | 1. void OnConnect() 144 | 2. void ServerMessageHandler(std::string message) 145 | 3. void OnDisconnect() 146 | 147 | To connect to the server, you need to call the function Connect. 148 | Before shutting down the program, you need to disconnect from the server using the method Disconnect 149 | An example of using the class can be found here: https://github.com/mrognor/Easy_Network/blob/master/Examples/RAU_Chat_Client.cpp 150 | 151 | # Functions 152 | [Info about all library functions:](namespace_e_n.html#func-members) 153 | -------------------------------------------------------------------------------- /EN_TCP_Server_SCUP.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EN_TCP_Server_SC.h" 4 | 5 | namespace EN 6 | { 7 | /** 8 | \brief Template class for storing different information during a single client session 9 | 10 | An object of this class will be created after the client is connected, 11 | it will be created in the client processing thread and will be available 12 | in all three methods of working with the connected client, 13 | i.e. with OnClientConnected, ClientMessageHandler and OnClientDisconnect. 14 | As the type of this class, you need to specify the type or class that stores all 15 | the necessary variables to describe the session. Data is accessed via the "->" operator. 16 | Also, this class allows you to change the communication protocol for each individual client while working with him, 17 | for this you need to set new values for the parameters TCP_Send and TCP_Recv. 18 | Inside EN_TCP_Server_SC, each object of this class is used only in its own thread, 19 | so it is thread-safe, and you should not try to use it in different threads. 20 | 21 | \warning Note that if TCP_Send and TCP_Recv have a default value not equal to nullptr, 22 | then when creating EN_TCP_Server_SC, the methods EN_TCP_Server_SC::TCP_Send and EN_TCP_Server_SC::TCP_Recv 23 | will be set the same as TCP_SessionContext_SC_CUP, regardless of which methods were setted in the childs of 24 | the EN_TCP_Server_SC class 25 | */ 26 | template 27 | struct TCP_SessionContext_SCUP 28 | { 29 | /// Template variable to store session data 30 | T data; 31 | 32 | // A pointer to a function for sending messages. Allows you to use custom network protocols. Send message to socket 33 | bool (*TCP_Send)(EN_SOCKET sock, const std::string& message) = nullptr; 34 | 35 | // A pointer to a function for recv messages. Allows you to use custom network protocols. Recv message from socket 36 | bool (*TCP_Recv)(EN_SOCKET sock, std::string& message) = nullptr; 37 | 38 | T* operator-> () 39 | { 40 | return &data; 41 | } 42 | }; 43 | 44 | /** 45 | TCP server with dynamic protocol changes during the session and session context. 46 | Полезен только в определенных ситуациях, например для http сервера, так как в нем 47 | нет пересылки между клиентами, но достаточно часто происходит изменение протоколов. 48 | SCUP - session context with custom user protocol 49 | */ 50 | template 51 | class EN_TCP_Server_SCUP : public EN::EN_TCP_Server_SC 52 | { 53 | private: 54 | // Definition of a purely virtual parent function, for further redefinition of this function with the addition of new parameters 55 | virtual void OnClientConnected(EN_SOCKET clientSocket, TCP_SessionContext_SC& sessionContext) {}; 56 | 57 | // Definition of a purely virtual parent function, for further redefinition of this function with the addition of new parameters 58 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message, TCP_SessionContext_SC& sessionContext) {}; 59 | 60 | // Definition of a purely virtual parent function, for further redefinition of this function with the addition of new parameters 61 | virtual void OnClientDisconnect(EN_SOCKET clientSocket, TCP_SessionContext_SC& sessionContext) {}; 62 | 63 | // Transfer the function to the private section so that it cannot be accessed in child classes 64 | virtual void SetTCPSendFunction(bool (*TCPSendFunction)(EN_SOCKET, const std::string&)) {}; 65 | 66 | // Transfer the function to the private section so that it cannot be accessed in child classes 67 | virtual void SetTCPRecvFunction(bool (*TCPRecvFunction)(EN_SOCKET, std::string&)) {}; 68 | protected: 69 | /// Variable to store session context data 70 | TCP_SessionContext_SCUP SessionContext; 71 | 72 | /** 73 | \brief The method that is executed when the client connects to the server 74 | 75 | \param[in] clientSocket Socket of the connected client 76 | \param[in] sessionContext A reference to a variable that stores the context of the connected client's session 77 | 78 | \warning Must be defined by the user 79 | */ 80 | virtual void OnClientConnected(EN_SOCKET clientSocket, TCP_SessionContext_SCUP& sessionContext) = 0; 81 | 82 | /** 83 | \brief Method that processes incoming messages 84 | 85 | \param[in] clientSocket The socket of the client from which the message came 86 | \param[in] message Message from the client 87 | \param[in] sessionContext A reference to a variable that stores the context of the connected client's session 88 | 89 | \warning Must be defined by the user 90 | */ 91 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message, TCP_SessionContext_SCUP& sessionContext) = 0; 92 | 93 | /** 94 | \brief Method that runs after the client is disconnected 95 | 96 | \param[in] clientSocket Socket of the disconnected client 97 | \param[in] sessionContext A reference to a variable that stores the context of the connected client's session 98 | 99 | \warning Must be defined by the user 100 | */ 101 | virtual void OnClientDisconnect(EN_SOCKET clientSocket, TCP_SessionContext_SCUP& sessionContext) = 0; 102 | 103 | public: 104 | EN_TCP_Server_SCUP() 105 | { 106 | if (SessionContext.TCP_Send != nullptr) 107 | this->SetTCPSendFunction(SessionContext.TCP_Send); 108 | 109 | if (SessionContext.TCP_Recv != nullptr) 110 | this->SetTCPRecvFunction(SessionContext.TCP_Recv); 111 | } 112 | 113 | /** 114 | \brief Method that send message to all connected clients 115 | 116 | \param[in] message The message to be sent to the client 117 | 118 | \warning This method uses the function for sending, which is set in the class. 119 | Do not use this function if clients with a modified protocol cannot receive messages from the default function. 120 | */ 121 | virtual void MulticastSend(std::string message) 122 | { 123 | EN_TCP_Server::MulticastSend(message); 124 | } 125 | 126 | virtual void ClientHandler(EN_SOCKET clientSocket) 127 | { 128 | OnClientConnected(clientSocket, SessionContext); 129 | 130 | std::string message; 131 | bool ConnectionStatus; 132 | 133 | while (true) 134 | { 135 | ConnectionStatus = SessionContext.TCP_Recv(clientSocket, message); 136 | 137 | if (ConnectionStatus == false) 138 | { 139 | OnClientDisconnect(clientSocket, SessionContext); 140 | break; 141 | } 142 | 143 | ClientMessageHandler(clientSocket, message, SessionContext); 144 | } 145 | 146 | CloseSocket(clientSocket); 147 | 148 | this->CrossWalk.PedestrianStartCrossRoad(); 149 | 150 | this->ClientSockets.erase(clientSocket); 151 | 152 | this->CrossWalk.PedestrianStopCrossRoad(); 153 | } 154 | 155 | virtual bool SendToClient(EN_SOCKET clientSocket, std::string message, TCP_SessionContext_SCUP& sessionContext) 156 | { 157 | this->CrossWalk.CarStartCrossRoad(); 158 | 159 | // Variable to store sending result 160 | // -2 - no client with this socket id on server 161 | // -1 - failed to send to client. For example if socket closed at sending time 162 | // 0 - sending succeeded 163 | int resCode = -2; 164 | 165 | if (this->ClientSockets.find(clientSocket) != this->ClientSockets.end()) 166 | { 167 | if(!sessionContext.TCP_Send(clientSocket, message)) 168 | resCode = -1; 169 | else 170 | resCode = 0; 171 | } 172 | 173 | this->CrossWalk.CarStopCrossRoad(); 174 | 175 | if (resCode == -2) 176 | { 177 | LOG(LogLevels::Warning, "You are trying to send to non client socket. Socket descriptor: " + std::to_string(clientSocket)); 178 | LOG(LogLevels::Hint, "Check that you are dont forget to lock sockets. This means that no one is already connected to the called socket" + std::to_string(clientSocket)); 179 | return false; 180 | } 181 | 182 | if (resCode == -1) 183 | { 184 | LOG(LogLevels::Warning, "Failed to send data to socket. Socket descriptor: " + std::to_string(clientSocket)); 185 | return false; 186 | } 187 | 188 | return true; 189 | } 190 | 191 | virtual bool WaitMessage(EN_SOCKET clientSocket, std::string& message, TCP_SessionContext_SCUP& sessionContext) 192 | { 193 | // Thread safety because this method should be called only inside handler thread 194 | return sessionContext.TCP_Recv(clientSocket, message); 195 | } 196 | }; 197 | } -------------------------------------------------------------------------------- /EN_Subprocess_Win.cpp: -------------------------------------------------------------------------------- 1 | #include "EN_Subprocess_Win.h" 2 | 3 | namespace EN 4 | { 5 | void Subprocess::Launch(std::string programmName) 6 | { 7 | DataCounter.store(0); 8 | IsProcessEnded.store(false); 9 | 10 | SECURITY_ATTRIBUTES saAttr; 11 | 12 | // Set the bInheritHandle flag so pipe handles are inherited. 13 | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 14 | saAttr.bInheritHandle = TRUE; 15 | saAttr.lpSecurityDescriptor = NULL; 16 | 17 | // Create a pipe for the child process's STDOUT. Read from 0, write to 1 18 | if (!CreatePipe(&pipeFromChild[0], &pipeFromChild[1], &saAttr, 0)) 19 | { 20 | std::cerr << "Failed to create pipe!" << std::endl; 21 | return; 22 | } 23 | 24 | // Ensure the read handle to the pipe for STDOUT is not inherited. 25 | if (!SetHandleInformation(pipeFromChild[0], HANDLE_FLAG_INHERIT, 0)) 26 | { 27 | std::cerr << "Failed to create pipe!" << std::endl; 28 | return; 29 | } 30 | 31 | // Create a pipe for the child process's STDIN. 32 | if (!CreatePipe(&pipeToChild[0], &pipeToChild[1], &saAttr, 0)) 33 | { 34 | std::cerr << "Failed to create pipe!" << std::endl; 35 | return; 36 | } 37 | 38 | // Ensure the write handle to the pipe for STDIN is not inherited. 39 | if (!SetHandleInformation(pipeToChild[1], HANDLE_FLAG_INHERIT, 0)) 40 | { 41 | std::cerr << "Failed to create pipe!" << std::endl; 42 | return; 43 | } 44 | 45 | STARTUPINFOA siStartInfo; 46 | BOOL bSuccess = FALSE; 47 | 48 | // Set up members of the PROCESS_INFORMATION structure. 49 | 50 | ZeroMemory(&ProcInfo, sizeof(PROCESS_INFORMATION)); 51 | 52 | // Set up members of the STARTUPINFO structure. 53 | // This structure specifies the STDIN and STDOUT handles for redirection. 54 | 55 | ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); 56 | siStartInfo.cb = sizeof(STARTUPINFO); 57 | siStartInfo.hStdError = pipeFromChild[1]; 58 | siStartInfo.hStdOutput = pipeFromChild[1]; 59 | siStartInfo.hStdInput = pipeToChild[0]; 60 | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 61 | 62 | // Create the child process. 63 | bSuccess = CreateProcessA(NULL, 64 | (LPSTR)programmName.c_str(), // command line 65 | NULL, // process security attributes 66 | NULL, // primary thread security attributes 67 | TRUE, // handles are inherited 68 | 0, // creation flags 69 | NULL, // use parent's environment 70 | NULL, // use parent's current directory 71 | &siStartInfo, // STARTUPINFO pointer 72 | &ProcInfo); // receives PROCESS_INFORMATIONx 73 | 74 | if (!bSuccess) 75 | { 76 | std::cerr << "Failed to create child process!" << std::endl; 77 | return; 78 | } 79 | 80 | // Close handles to the stdin and stdout pipes no longer needed by the child process. 81 | // If they are not explicitly closed, there is no way to recognize that the child process has ended. 82 | CloseHandle(pipeFromChild[1]); 83 | CloseHandle(pipeToChild[0]); 84 | 85 | Th = std::thread([&]() 86 | { 87 | while (true) 88 | { 89 | // Wait while some bytes write 90 | ReadFile(pipeFromChild[0], NULL, 0, NULL, NULL); 91 | 92 | // Get amount of available bytes 93 | DWORD availableBytes; 94 | PeekNamedPipe(pipeFromChild[0], NULL, 0, NULL, &availableBytes, NULL); 95 | 96 | char* bytes = new char[availableBytes]; 97 | DWORD readed; 98 | 99 | bSuccess = ReadFile(pipeFromChild[0], bytes, availableBytes, &readed, NULL); 100 | 101 | // EOF and process finishing 102 | if (readed == 0) 103 | { 104 | // Thread safety append data to queue and trigger wait function 105 | while (!Mtx.try_lock()) 106 | Cv.notify_all(); 107 | 108 | IsProcessEnded.store(true); 109 | Mtx.unlock(); 110 | 111 | delete[] bytes; 112 | break; 113 | } 114 | else 115 | { 116 | if (readed != availableBytes) 117 | { 118 | std::cerr << "Failed to read data from pipe!" << std::endl; 119 | delete[] bytes; 120 | return; 121 | } 122 | else 123 | { 124 | // Thread safety append data to queue and trigger wait function 125 | while (!Mtx.try_lock()) 126 | { 127 | Cv.notify_all(); 128 | } 129 | 130 | MessagesQueue.push(std::string(bytes, readed)); 131 | DataCounter.fetch_add(1); 132 | Mtx.unlock(); 133 | } 134 | 135 | } 136 | delete[] bytes; 137 | } 138 | }); 139 | } 140 | 141 | void Subprocess::SendData(std::string data, bool isRequiredNewLineSymbol) 142 | { 143 | Mtx.lock(); 144 | 145 | if (IsProcessEnded.load()) 146 | { 147 | Mtx.unlock(); 148 | return; 149 | } 150 | 151 | if (isRequiredNewLineSymbol) 152 | data += '\n'; 153 | 154 | DWORD dwWritten; 155 | WriteFile(pipeToChild[1], data.c_str(), data.length(), &dwWritten, NULL); 156 | 157 | Mtx.unlock(); 158 | } 159 | 160 | std::string Subprocess::GetData(bool isRemoveNewLineSymbols) 161 | { 162 | std::string res = ""; 163 | Mtx.lock(); 164 | if (!MessagesQueue.empty()) 165 | { 166 | res = MessagesQueue.front(); 167 | MessagesQueue.pop(); 168 | DataCounter.fetch_sub(1); 169 | } 170 | Mtx.unlock(); 171 | 172 | if (isRemoveNewLineSymbols) 173 | { 174 | // Remove CR LF from res 175 | if(!res.empty()) 176 | res.pop_back(); 177 | 178 | if(!res.empty()) 179 | res.pop_back(); 180 | } 181 | 182 | return res; 183 | } 184 | 185 | std::string Subprocess::WaitData(bool isRemoveNewLineSymbols) 186 | { 187 | std::string res = ""; 188 | std::mutex cvMtx; 189 | std::unique_lock lk(cvMtx); 190 | 191 | StartWaiting: 192 | Mtx.lock(); 193 | 194 | if (IsProcessEnded.load()) 195 | { 196 | Mtx.unlock(); 197 | return ""; 198 | } 199 | 200 | if (!MessagesQueue.empty()) 201 | { 202 | res = MessagesQueue.front(); 203 | MessagesQueue.pop(); 204 | DataCounter.fetch_sub(1); 205 | Mtx.unlock(); 206 | } 207 | else 208 | { 209 | Cv.wait(lk); 210 | Mtx.unlock(); 211 | goto StartWaiting; 212 | } 213 | 214 | if (isRemoveNewLineSymbols) 215 | { 216 | // Remove CR LF from res 217 | if(!res.empty()) 218 | res.pop_back(); 219 | 220 | if(!res.empty()) 221 | res.pop_back(); 222 | } 223 | 224 | return res; 225 | } 226 | 227 | void Subprocess::StopProcess(std::string commandToSendToProcessToStop, bool isRequiredNewLineSymbol) 228 | { 229 | SendData(commandToSendToProcessToStop, isRequiredNewLineSymbol); 230 | Th.join(); 231 | 232 | DWORD result = WaitForSingleObject(ProcInfo.hProcess, INFINITE); 233 | 234 | // Close handles to the child process and its primary thread. 235 | // Some applications might keep these handles to monitor the status 236 | // of the child process, for example. 237 | CloseHandle(ProcInfo.hProcess); 238 | CloseHandle(ProcInfo.hThread); 239 | 240 | if (result != WAIT_OBJECT_0 && result != WAIT_TIMEOUT) 241 | std::cerr << "Process finished with errors!" << std::endl; 242 | } 243 | 244 | bool Subprocess::IsData() 245 | { 246 | return DataCounter.load() > 1; 247 | } 248 | 249 | bool Subprocess::GetIsProcessEnded() 250 | { 251 | return IsProcessEnded.load(); 252 | } 253 | 254 | Subprocess::~Subprocess() 255 | { 256 | if (Th.joinable()) 257 | { 258 | std::cerr << "Thread was not joined! Higly possible thar you dont stop process" << std::endl; 259 | } 260 | } 261 | }; -------------------------------------------------------------------------------- /EN_RAU_Server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EN_Functions.h" 4 | #include "EN_TCP_Server.h" 5 | #include "EN_UDP_Server.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace EN 11 | { 12 | // EN_RAU_Server class definition 13 | class EN_RAU_Server; 14 | 15 | /// \cond HIDDEN_SYMBOLS 16 | class EN_RAU_TCP_Server : public EN_TCP_Server 17 | { 18 | public: 19 | friend EN_RAU_Server; 20 | EN_RAU_Server* RAU_Server; 21 | 22 | EN_RAU_TCP_Server(EN_RAU_Server* rau_Server); 23 | 24 | void OnClientConnected(size_t ClientID); 25 | 26 | void ClientMessageHandler(std::string message, size_t ClientID); 27 | 28 | void OnClientDisconnect(size_t ClientID); 29 | }; 30 | 31 | class EN_RAU_UDP_Server : public EN_UDP_Server 32 | { 33 | public: 34 | friend EN_RAU_Server; 35 | EN_RAU_Server* RAU_Server; 36 | 37 | EN_RAU_UDP_Server(EN_RAU_Server* rau_Server); 38 | 39 | // Third parametr in milliseconds 40 | void ClientMessageHandler(std::string message, std::string ClientSocketAddr, long long TimeSincePackageArrived); 41 | 42 | // Function work between putting message in buffer. Return true if you want to put message in buffer 43 | bool InstantClientMessageHandler(std::string message, std::string ClientSocketAddr, long long TimeWhenPackageArrived); 44 | }; 45 | /// \endcond 46 | 47 | 48 | /// Server class with connection and the possibility of reliable and unreliable sending 49 | class EN_RAU_Server 50 | { 51 | private: 52 | // RAU_TCP_Server and RAU_UDP_Server pointers 53 | friend EN_RAU_TCP_Server; 54 | friend EN_RAU_UDP_Server; 55 | EN_RAU_TCP_Server* TCP_Server; 56 | EN_RAU_UDP_Server* UDP_Server; 57 | 58 | // Vector of queues for storing incoming messages 59 | std::vector*> VectorQueuesMessages; 60 | // Vector of clients udp addresses 61 | std::vector UDPIpAddresses; 62 | // Vector of condition variavles 63 | std::vector VectorCondVars; 64 | // Vector of bools to stop client handler threads 65 | std::vector KillThreads; 66 | 67 | // Varibale to shutdown server 68 | bool IsShutdown = false; 69 | 70 | // Incoming message handler 71 | void ThreadQueueHandler(int ClientID); 72 | 73 | protected: 74 | /// Server port. By default set to 1111 75 | int Port = 1111; 76 | 77 | /// Server ip address. By default set to localhost 78 | std::string IpAddress = "127.0.0.1"; 79 | 80 | /** 81 | \brief The method that is executed when the client connects to the server 82 | 83 | \warning Must be defined by the user 84 | */ 85 | virtual void OnClientConnected(size_t ClientID) = 0; 86 | 87 | /** 88 | \brief Method that processes incoming messages 89 | 90 | \warning Must be defined by the user 91 | */ 92 | virtual void ClientMessageHandler(std::string message, size_t ClientID) = 0; 93 | 94 | /** 95 | \brief Method that runs after the client is disconnected 96 | 97 | \warning Must be defined by the user 98 | */ 99 | virtual void OnClientDisconnect(size_t ClientID) = 0; 100 | 101 | public: 102 | EN_RAU_Server(); 103 | 104 | /// Port getter 105 | int GetPort(); 106 | 107 | /// Ip getter 108 | std::string GetIpAddr(); 109 | 110 | /// The function returns the number of connected devices 111 | size_t GetConnectionsCount(); 112 | 113 | /// Method to start server. 114 | void Run(); 115 | 116 | /** 117 | \brief Method that disconnects the client from the server 118 | 119 | \param[in] ClientID The number of the client to be disconnect 120 | */ 121 | void DisconnectClient(size_t ClientID); 122 | 123 | /// Method that stops the server 124 | void Shutdown(); 125 | 126 | /** 127 | \brief Method that send message to client 128 | 129 | \param[in] ClientID The number of the client 130 | \param[in] message The message to be sent to the client 131 | \param[in] IsReliable if the parameter is set to true, the message is guaranteed to reach. If the parameter is set to false the message delivery is not guaranteed 132 | */ 133 | void SendToClient(size_t ClientId, std::string message, bool IsReliable = true); 134 | 135 | /** 136 | \brief The method sets options for accept socket. Works with tcp(reliable) 137 | Accept socket accepts incoming connections from clients. 138 | 139 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 140 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 141 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 142 | \param[in] optionValue The value for the requested option is specified. 143 | */ 144 | void SetTCPAcceptSocketOption(int level, int optionName, int optionValue); 145 | 146 | /** 147 | \brief The method sets options for accept socket. Works with tcp(reliable) 148 | Accept socket accepts incoming connections from clients. 149 | 150 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 151 | The list of all predefined structures is in EN_SocketOptions.h. 152 | You can create your own sets of options using define or by creating structure objects 153 | */ 154 | void SetTCPAcceptSocketOption(PredefinedSocketOptions socketOptions); 155 | 156 | /** 157 | \brief The method sets options for all sockets that will connect after its call. Works with tcp(reliable) 158 | 159 | It makes sense to call this method before starting connecting clients, 160 | since it does not affect previously created sockets. 161 | 162 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 163 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 164 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 165 | \param[in] optionValue The value for the requested option is specified. 166 | */ 167 | void AddOnTCPSocketCreateOption(int level, int optionName, int optionValue); 168 | 169 | /** 170 | \brief The method sets options for all sockets that will connect after its call. Works with tcp(reliable) 171 | 172 | It makes sense to call this method before starting connecting clients, 173 | since it does not affect previously created sockets 174 | 175 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 176 | The list of all predefined structures is in EN_SocketOptions.h. 177 | You can create your own sets of options using define or by creating structure objects 178 | */ 179 | void AddOnTCPSocketCreateOption(PredefinedSocketOptions socketOptions); 180 | 181 | /** 182 | \brief The method sets options for client socket. Works with tcp(reliable) 183 | 184 | \param[in] ClientID The number of the client 185 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 186 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 187 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 188 | \param[in] optionValue The value for the requested option is specified. 189 | */ 190 | void SetTCPSocketOption(size_t ClientID, int level, int optionName, int optionValue); 191 | 192 | /** 193 | \brief The method sets options for client socket. Works with tcp(reliable) 194 | 195 | \param[in] ClientID The number of the client 196 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 197 | The list of all predefined structures is in EN_SocketOptions.h. 198 | You can create your own sets of options using define or by creating structure objects 199 | */ 200 | void SetTCPSocketOption(size_t ClientID, PredefinedSocketOptions socketOptions); 201 | 202 | /** 203 | \brief The method sets options for client socket. Works with udp(unreliable) 204 | 205 | \param[in] level The level at which the option is defined (for example, SOL_SOCKET). 206 | \param[in] optionName The socket option for which the value is to be set (for example, SO_BROADCAST). 207 | The optionName parameter must be a socket option defined within the specified level, or behavior is undefined. 208 | \param[in] optionValue The value for the requested option is specified. 209 | */ 210 | void SetUDPSocketOption(int level, int optionName, int optionValue); 211 | 212 | /** 213 | \brief The method sets options for client socket. Works with udp(unreliable) 214 | 215 | \param[in] socketOptions This parameter takes a predefined structure to specify a package of socket options at once. 216 | The list of all predefined structures is in EN_SocketOptions.h. 217 | You can create your own sets of options using define or by creating structure objects 218 | */ 219 | void SetUDPSocketOption(PredefinedSocketOptions socketOptions); 220 | 221 | virtual ~EN_RAU_Server(); 222 | }; 223 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS = -pthread 2 | CXXFLAGS = -std=c++11 -Wall 3 | FILEEXT = 4 | 5 | EN_SUBPROCESS = EN_Subprocess_Lin 6 | 7 | # Check os 8 | ifeq ($(OS), Windows_NT) 9 | LDFLAGS += -lws2_32 10 | FILEEXT = .exe 11 | EN_SUBPROCESS = EN_Subprocess_Win 12 | endif 13 | 14 | TARGETS = bin/EN_Functions.o \ 15 | bin/EN_TCP_Client.o \ 16 | bin/EN_TCP_Server.o \ 17 | bin/EN_UDP_Client.o \ 18 | bin/EN_UDP_Server.o \ 19 | bin/EN_Logger.o \ 20 | bin/EN_ThreadGate.o \ 21 | bin/EN_ThreadCrossWalk.o \ 22 | bin/EN_SocketOptions.o \ 23 | bin/EN_BackgroundTimer.o \ 24 | bin/EN_FileTransmissionStatus.o \ 25 | bin/EN_FT_Server.o \ 26 | bin/EN_FT_Client.o \ 27 | bin/EN_ThreadBarrier.o \ 28 | bin/$(EN_SUBPROCESS).o \ 29 | bin/EN_EXPERIMENTAL_HTTP_Server.o \ 30 | EN_TCP_Server_SC.h \ 31 | EN_TCP_Server_SCUP.h 32 | 33 | # bin/EN_RAU_Server.o bin/EN_RAU_Client.o 34 | 35 | # Check native atomic_int64 support 36 | ifneq ($(shell test -e ./scripts/CheckAtomicInt64Support.test && echo -n yes), yes) 37 | CXXFLAGS += -D NATIVE_ATOMIC_INT64_NOT_SUPPORTED 38 | TARGETS += bin/EN_Atomic_Int64.o 39 | $(warning Your system does not have native atomic_int64 support. A software implementation will be used) 40 | endif 41 | 42 | #use -D DISABLE_LOGGER to disable logger 43 | 44 | debug: CXXFLAGS += -g 45 | debug: all 46 | 47 | release: CXXFLAGS += -D NDEBUG -O3 48 | release: all 49 | 50 | all: bin/TCP_Chat_Server$(FILEEXT) \ 51 | bin/TCP_Chat_Server_SC$(FILEEXT) \ 52 | bin/TCP_Chat_Server_SCUP$(FILEEXT) \ 53 | bin/TCP_Chat_Client$(FILEEXT) \ 54 | bin/UDP_Chat_Server$(FILEEXT) \ 55 | bin/UDP_Chat_Client$(FILEEXT) \ 56 | bin/TCP_BlockingFileTransmitter_Server$(FILEEXT) \ 57 | bin/TCP_BlockingFileTransmitter_Client$(FILEEXT) \ 58 | bin/ParallelFor$(FILEEXT) \ 59 | bin/HTTP_Server$(FILEEXT) \ 60 | bin/FT_Chat_Server$(FILEEXT) \ 61 | bin/FT_Chat_Client$(FILEEXT) \ 62 | bin/HTTP_Server$(FILEEXT) \ 63 | # bin/RAU_Chat_Server$(FILEEXT) bin/RAU_Chat_Client$(FILEEXT) 64 | 65 | # HTTP_Server 66 | bin/HTTP_Server$(FILEEXT): bin/libEasyNetwork.a Examples/http/HTTP_Server.cpp 67 | g++ $(CXXFLAGS) Examples/http/HTTP_Server.cpp -I. -Lbin -lEasyNetwork -o bin/HTTP_Server$(FILEEXT) $(LDFLAGS) 68 | cp -n Examples/http/index.html bin 69 | cp -n Examples/http/page.html bin 70 | cp -n Examples/http/404.html bin 71 | cp -n Examples/http/Picture.png bin 72 | cp -n Examples/http/Picture.jpg bin 73 | cp -n Examples/http/Picture.svg bin 74 | 75 | # TCP_Chat 76 | bin/TCP_Chat_Server$(FILEEXT): bin/libEasyNetwork.a Examples/TCP_Chat_Server.cpp 77 | g++ $(CXXFLAGS) Examples/TCP_Chat_Server.cpp -I. -Lbin -lEasyNetwork -o bin/TCP_Chat_Server$(FILEEXT) $(LDFLAGS) 78 | 79 | bin/TCP_Chat_Client$(FILEEXT): bin/libEasyNetwork.a Examples/TCP_Chat_Client.cpp 80 | g++ $(CXXFLAGS) Examples/TCP_Chat_Client.cpp -I. -Lbin -lEasyNetwork -o bin/TCP_Chat_Client$(FILEEXT) $(LDFLAGS) 81 | 82 | # TCP_Chat_SC server 83 | bin/TCP_Chat_Server_SC$(FILEEXT): bin/libEasyNetwork.a Examples/TCP_Chat_Server_SC.cpp 84 | g++ $(CXXFLAGS) Examples/TCP_Chat_Server_SC.cpp -I. -Lbin -lEasyNetwork -o bin/TCP_Chat_Server_SC$(FILEEXT) $(LDFLAGS) 85 | 86 | # TCP_Chat_Server_SCUP server 87 | bin/TCP_Chat_Server_SCUP$(FILEEXT): bin/libEasyNetwork.a Examples/TCP_Chat_Server_SCUP.cpp 88 | g++ $(CXXFLAGS) Examples/TCP_Chat_Server_SCUP.cpp -I. -Lbin -lEasyNetwork -o bin/TCP_Chat_Server_SC_CUP$(FILEEXT) $(LDFLAGS) 89 | 90 | # UDP_Chat 91 | bin/UDP_Chat_Server$(FILEEXT): bin/libEasyNetwork.a Examples/UDP_Chat_Server.cpp 92 | g++ $(CXXFLAGS) Examples/UDP_Chat_Server.cpp -I. -Lbin -lEasyNetwork -o bin/UDP_Chat_Server$(FILEEXT) $(LDFLAGS) 93 | 94 | bin/UDP_Chat_Client$(FILEEXT): bin/libEasyNetwork.a Examples/UDP_Chat_Client.cpp 95 | g++ $(CXXFLAGS) Examples/UDP_Chat_Client.cpp -I. -Lbin -lEasyNetwork -o bin/UDP_Chat_Client$(FILEEXT) $(LDFLAGS) 96 | 97 | # RAU_Chat 98 | # bin/RAU_Chat_Server$(FILEEXT): bin/libEasyNetwork.a Examples/RAU_Chat_Server.cpp 99 | # g++ $(CXXFLAGS) Examples/RAU_Chat_Server.cpp -I. -Lbin -lEasyNetwork -o bin/RAU_Chat_Server$(FILEEXT) $(LDFLAGS) 100 | 101 | # bin/RAU_Chat_Client$(FILEEXT): bin/libEasyNetwork.a Examples/RAU_Chat_Client.cpp 102 | # g++ $(CXXFLAGS) Examples/RAU_Chat_Client.cpp -I. -Lbin -lEasyNetwork -o bin/RAU_Chat_Client$(FILEEXT) $(LDFLAGS) 103 | 104 | # TCP_BlockingFileTransmitter 105 | bin/TCP_BlockingFileTransmitter_Server$(FILEEXT): bin/libEasyNetwork.a Examples/TCP_BlockingFileTransmitter_Server.cpp 106 | g++ $(CXXFLAGS) Examples/TCP_BlockingFileTransmitter_Server.cpp -I. -Lbin -lEasyNetwork -o bin/TCP_BlockingFileTransmitter_Server$(FILEEXT) $(LDFLAGS) 107 | 108 | bin/TCP_BlockingFileTransmitter_Client$(FILEEXT): bin/libEasyNetwork.a Examples/TCP_BlockingFileTransmitter_Client.cpp 109 | g++ $(CXXFLAGS) Examples/TCP_BlockingFileTransmitter_Client.cpp -I. -Lbin -lEasyNetwork -o bin/TCP_BlockingFileTransmitter_Client$(FILEEXT) $(LDFLAGS) 110 | 111 | # FileTransmitter_Chat 112 | bin/FT_Chat_Server$(FILEEXT): bin/libEasyNetwork.a Examples/FT_Chat_Server.cpp 113 | g++ $(CXXFLAGS) Examples/FT_Chat_Server.cpp -I. -Lbin -lEasyNetwork -o bin/FT_Chat_Server$(FILEEXT) $(LDFLAGS) 114 | 115 | bin/FT_Chat_Client$(FILEEXT): bin/libEasyNetwork.a Examples/FT_Chat_Client.cpp 116 | g++ $(CXXFLAGS) Examples/FT_Chat_Client.cpp -I. -Lbin -lEasyNetwork -o bin/FT_Chat_Client$(FILEEXT) $(LDFLAGS) 117 | 118 | # ParallelFor 119 | bin/ParallelFor$(FILEEXT): bin/libEasyNetwork.a Examples/ParallelFor.cpp 120 | g++ $(CXXFLAGS) Examples/ParallelFor.cpp -I. -Lbin -lEasyNetwork -o bin/ParallelFor$(FILEEXT) $(LDFLAGS) 121 | 122 | # Library binary 123 | bin/libEasyNetwork.a: $(TARGETS) 124 | ar rc bin/libEasyNetwork.a $(TARGETS) 125 | ranlib bin/libEasyNetwork.a 126 | 127 | # Build all object files 128 | # bin/EN_RAU_Client.o: EN_RAU_Client.cpp EN_RAU_Client.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h 129 | # mkdir -p bin 130 | # g++ $(CXXFLAGS) -c EN_RAU_Client.cpp -o bin/EN_RAU_Client.o 131 | 132 | # bin/EN_RAU_Server.o: EN_RAU_Server.cpp EN_RAU_Server.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h 133 | # mkdir -p bin 134 | # g++ $(CXXFLAGS) -c EN_RAU_Server.cpp -o bin/EN_RAU_Server.o 135 | 136 | bin/EN_FT_Client.o: EN_FT_Client.cpp EN_FT_Client.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 137 | mkdir -p bin 138 | g++ $(CXXFLAGS) -c EN_FT_Client.cpp -o bin/EN_FT_Client.o 139 | 140 | bin/EN_FT_Server.o: EN_FT_Server.cpp EN_FT_Server.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 141 | mkdir -p bin 142 | g++ $(CXXFLAGS) -c EN_FT_Server.cpp -o bin/EN_FT_Server.o 143 | 144 | bin/EN_UDP_Client.o: EN_UDP_Client.cpp EN_UDP_Client.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 145 | mkdir -p bin 146 | g++ $(CXXFLAGS) -c EN_UDP_Client.cpp -o bin/EN_UDP_Client.o 147 | 148 | bin/EN_UDP_Server.o: EN_UDP_Server.cpp EN_UDP_Server.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 149 | mkdir -p bin 150 | g++ $(CXXFLAGS) -c EN_UDP_Server.cpp -o bin/EN_UDP_Server.o 151 | 152 | bin/EN_TCP_Server.o: EN_TCP_Server.cpp EN_TCP_Server.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 153 | mkdir -p bin 154 | g++ $(CXXFLAGS) -c EN_TCP_Server.cpp -o bin/EN_TCP_Server.o 155 | 156 | bin/EN_TCP_Client.o: EN_TCP_Client.cpp EN_TCP_Client.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 157 | mkdir -p bin 158 | g++ $(CXXFLAGS) -c EN_TCP_Client.cpp -o bin/EN_TCP_Client.o 159 | 160 | bin/EN_Functions.o: EN_Functions.cpp EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 161 | mkdir -p bin 162 | g++ $(CXXFLAGS) -c EN_Functions.cpp -o bin/EN_Functions.o 163 | 164 | bin/EN_Logger.o: EN_Logger.cpp EN_Logger.h EN_Functions.h EN_SocketOptions.h EN_ThreadGate.h EN_ThreadCrossWalk.h EN_ThreadBarrier.h $(EN_SUBPROCESS).h 165 | mkdir -p bin 166 | g++ $(CXXFLAGS) -c EN_Logger.cpp -o bin/EN_Logger.o 167 | 168 | bin/EN_SocketOptions.o: EN_SocketOptions.h EN_SocketOptions.cpp EN_Functions.h 169 | mkdir -p bin 170 | g++ $(CXXFLAGS) -c EN_SocketOptions.cpp -o bin/EN_SocketOptions.o 171 | 172 | bin/EN_ThreadGate.o: EN_ThreadGate.cpp EN_ThreadGate.h 173 | mkdir -p bin 174 | g++ $(CXXFLAGS) -c EN_ThreadGate.cpp -o bin/EN_ThreadGate.o 175 | 176 | bin/EN_ThreadCrossWalk.o: EN_ThreadCrossWalk.cpp EN_ThreadCrossWalk.h 177 | mkdir -p bin 178 | g++ $(CXXFLAGS) -c EN_ThreadCrossWalk.cpp -o bin/EN_ThreadCrossWalk.o 179 | 180 | bin/EN_BackgroundTimer.o: EN_BackgroundTimer.cpp EN_BackgroundTimer.h 181 | mkdir -p bin 182 | g++ $(CXXFLAGS) -c EN_BackgroundTimer.cpp -o bin/EN_BackgroundTimer.o 183 | 184 | bin/EN_FileTransmissionStatus.o: EN_FileTransmissionStatus.cpp EN_FileTransmissionStatus.h 185 | mkdir -p bin 186 | g++ $(CXXFLAGS) -c EN_FileTransmissionStatus.cpp -o bin/EN_FileTransmissionStatus.o 187 | 188 | bin/EN_Atomic_Int64.o: EN_Atomic_Int64.cpp EN_Atomic_Int64.h 189 | mkdir -p bin 190 | g++ $(CXXFLAGS) -c EN_Atomic_Int64.cpp -o bin/EN_Atomic_Int64.o 191 | 192 | bin/EN_ThreadBarrier.o: EN_ThreadBarrier.cpp EN_ThreadBarrier.h 193 | mkdir -p bin 194 | g++ $(CXXFLAGS) -c EN_ThreadBarrier.cpp -o bin/EN_ThreadBarrier.o 195 | 196 | bin/$(EN_SUBPROCESS).o: $(EN_SUBPROCESS).cpp $(EN_SUBPROCESS).h 197 | mkdir -p bin 198 | g++ $(CXXFLAGS) -c $(EN_SUBPROCESS).cpp -o bin/$(EN_SUBPROCESS).o 199 | 200 | bin/EN_EXPERIMENTAL_HTTP_Server.o: experimental/EN_HTTP_Server.h experimental/EN_HTTP_Server.cpp 201 | mkdir -p bin 202 | g++ $(CXXFLAGS) -c experimental/EN_HTTP_Server.cpp -o bin/EN_EXPERIMENTAL_HTTP_Server.o 203 | 204 | # Clean all 205 | clean: 206 | rm -rf bin 207 | -------------------------------------------------------------------------------- /Examples/FT_Chat_Server.cpp: -------------------------------------------------------------------------------- 1 | #include "../EN_TCP_Server.h" 2 | #include "../EN_ThreadBarrier.h" 3 | #include "map" 4 | 5 | namespace EN 6 | { 7 | class EN_FT_Server; 8 | 9 | class EN_FT_Server_Internal_FileTransmitter : public EN::EN_TCP_Server 10 | { 11 | private: 12 | EN_FT_Server* FT_Server; 13 | protected: 14 | void OnClientConnected(EN_SOCKET clientSocket); 15 | 16 | virtual void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) override {} 17 | 18 | void OnClientDisconnect(EN_SOCKET clientSocket); 19 | public: 20 | EN_FT_Server_Internal_FileTransmitter(EN_FT_Server* fT_Server); 21 | }; 22 | 23 | class EN_FT_Server_Internal_MessageTransmitter : public EN::EN_TCP_Server 24 | { 25 | private: 26 | EN_FT_Server* FT_Server; 27 | protected: 28 | void OnClientConnected(EN_SOCKET clientSocket); 29 | 30 | void ClientMessageHandler(EN_SOCKET clientSocket, std::string message); 31 | 32 | void OnClientDisconnect(EN_SOCKET clientSocket); 33 | public: 34 | EN_FT_Server_Internal_MessageTransmitter(EN_FT_Server* fT_Server); 35 | }; 36 | 37 | class EN_FT_Server 38 | { 39 | private: 40 | friend EN_FT_Server_Internal_FileTransmitter; 41 | friend EN_FT_Server_Internal_MessageTransmitter; 42 | 43 | std::string IpAddress; 44 | int MessageTransmitterPort = 1111; 45 | int FileTransmitterPort = 1112; 46 | 47 | std::thread FileTransmitterRunThread; 48 | 49 | // Thread crosswalk to work with MtToFtSocketsMap 50 | EN::EN_ThreadCrossWalk MtToFtSocketsCW; 51 | // Map with message transmitter socket as key and file transmitter socket as value 52 | std::map MtToFtSocketsMap; 53 | 54 | // Thread crosswalk to work with FtSocketToBarrierMap 55 | EN::EN_ThreadCrossWalk FtSocketToBarrierCW; 56 | // Map with file transmitter socket as key and thread barrier as value 57 | std::map FtSocketToBarrierMap; 58 | 59 | // Thread crosswalk to work with MtSocketToBarrierMap 60 | EN::EN_ThreadCrossWalk MtSocketToBarrierCW; 61 | // Map with message transmitter socket as key and thread barrier as value 62 | std::map MtSocketToBarrierMap; 63 | 64 | protected: 65 | EN_FT_Server_Internal_FileTransmitter FileTransmitter; 66 | EN_FT_Server_Internal_MessageTransmitter MessageTransmitter; 67 | 68 | void OnClientConnected(EN_SOCKET clientSocket) 69 | { 70 | EN_SOCKET ftSock; 71 | MtToFtSocketsCW.CarStartCrossRoad(); 72 | auto f = MtToFtSocketsMap.find(clientSocket); 73 | if (f != MtToFtSocketsMap.end()) 74 | ftSock = f->second; 75 | MtToFtSocketsCW.CarStopCrossRoad(); 76 | 77 | std::cout << "Connected! Sock map: MT=" << std::to_string(clientSocket) + " FT=" + std::to_string(ftSock) << std::endl; 78 | SendToClient(clientSocket, "Hello from FT server"); 79 | } 80 | 81 | void ClientMessageHandler(EN_SOCKET clientSocket, std::string message) 82 | { 83 | std::cout << "Message from client: " << std::to_string(clientSocket) << " Message: " << message << std::endl; 84 | } 85 | 86 | void OnClientDisconnect(EN_SOCKET clientSocket) 87 | { 88 | EN_SOCKET ftSock; 89 | MtToFtSocketsCW.CarStartCrossRoad(); 90 | auto f = MtToFtSocketsMap.find(clientSocket); 91 | if (f != MtToFtSocketsMap.end()) 92 | ftSock = f->second; 93 | MtToFtSocketsCW.CarStopCrossRoad(); 94 | 95 | std::cout << "Disconnected! Sock map: MT=" << std::to_string(clientSocket) + " FT=" + std::to_string(ftSock) << std::endl; 96 | } 97 | 98 | public: 99 | EN_FT_Server() : FileTransmitter(this), MessageTransmitter(this) {} 100 | 101 | size_t GetConnectionsCount() 102 | { 103 | size_t res; 104 | MtToFtSocketsCW.CarStartCrossRoad(); 105 | res = MtToFtSocketsMap.size(); 106 | MtToFtSocketsCW.CarStopCrossRoad(); 107 | return res; 108 | } 109 | 110 | void Run() 111 | { 112 | FileTransmitterRunThread = std::thread ([&]() 113 | { 114 | FileTransmitter.Run(); 115 | }); 116 | 117 | MessageTransmitter.Run(); 118 | } 119 | 120 | void DisconnectClient(EN_SOCKET socketToDisconnect) 121 | { 122 | 123 | } 124 | 125 | void Shutdown() 126 | { 127 | FileTransmitter.Shutdown(); 128 | MessageTransmitter.Shutdown(); 129 | FileTransmitterRunThread.join(); 130 | } 131 | 132 | void DisconnectAllConnectedClients() 133 | { 134 | 135 | } 136 | 137 | bool SendToClient(EN_SOCKET clientSocket, std::string message) 138 | { 139 | return MessageTransmitter.SendToClient(clientSocket, message); 140 | } 141 | 142 | void MulticastSend(std::string message) 143 | { 144 | MessageTransmitter.MulticastSend(message); 145 | } 146 | 147 | void WaitWhileAllClientsDisconnect() 148 | { 149 | 150 | } 151 | 152 | bool WaitMessage(EN_SOCKET clientSocket, std::string& message) 153 | { 154 | 155 | } 156 | 157 | void LockClientSockets(); 158 | 159 | void UnlockClientSockets(); 160 | }; 161 | 162 | EN_FT_Server_Internal_FileTransmitter::EN_FT_Server_Internal_FileTransmitter(EN_FT_Server* fT_Server) 163 | { 164 | FT_Server = fT_Server; 165 | IpAddress = FT_Server->IpAddress; 166 | Port = FT_Server->FileTransmitterPort; 167 | } 168 | 169 | void EN_FT_Server_Internal_FileTransmitter::OnClientConnected(EN_SOCKET clientSocket) 170 | { 171 | // Add pair with client socket and barrier to map 172 | EN_ThreadBarrier* socketsBarrier = new EN_ThreadBarrier; 173 | FT_Server->FtSocketToBarrierCW.PedestrianStartCrossRoad(); 174 | FT_Server->FtSocketToBarrierMap.emplace(clientSocket, socketsBarrier); 175 | FT_Server->FtSocketToBarrierCW.PedestrianStopCrossRoad(); 176 | 177 | SendToClient(clientSocket, "FtSockDesc " + std::to_string(clientSocket)); 178 | 179 | LOG(LogLevels::Info, "Internal file transmitter connected! Socket descriptor: " + std::to_string(clientSocket)); 180 | 181 | // Wait barrrier or 300 seconds. 182 | socketsBarrier->WaitFor(2, std::chrono::seconds(300)); // Wait A 183 | } 184 | 185 | void EN_FT_Server_Internal_FileTransmitter::OnClientDisconnect(EN_SOCKET clientSocket) 186 | { 187 | EN_ThreadBarrier* barr; 188 | // Remove pair with file transmitter socket and barrier from map 189 | FT_Server->FtSocketToBarrierCW.PedestrianStartCrossRoad(); 190 | auto f = FT_Server->FtSocketToBarrierMap.find(clientSocket); 191 | if (f != FT_Server->FtSocketToBarrierMap.end()) 192 | { 193 | barr = f->second; 194 | FT_Server->FtSocketToBarrierMap.erase(f); 195 | FT_Server->FtSocketToBarrierCW.PedestrianStopCrossRoad(); 196 | barr->Wait(2); // Wait B 197 | delete barr; 198 | } 199 | else 200 | { 201 | LOG(LogLevels::Warning, "No thread barrier on FT server. Possibly memmory leak!"); 202 | FT_Server->FtSocketToBarrierCW.PedestrianStopCrossRoad(); 203 | } 204 | } 205 | 206 | EN_FT_Server_Internal_MessageTransmitter::EN_FT_Server_Internal_MessageTransmitter(EN_FT_Server* fT_Server) 207 | { 208 | FT_Server = fT_Server; 209 | IpAddress = FT_Server->IpAddress; 210 | Port = FT_Server->MessageTransmitterPort; 211 | } 212 | 213 | void EN_FT_Server_Internal_MessageTransmitter::OnClientConnected(EN_SOCKET clientSocket) 214 | { 215 | LOG(LogLevels::Info, "Internal message transmitter connected! Socket descriptor: " + std::to_string(clientSocket)); 216 | std::string ftSockDesc; 217 | 218 | WaitMessage(clientSocket, ftSockDesc); 219 | 220 | auto vec = Split(ftSockDesc); 221 | 222 | int ftSock; 223 | if (vec[0] != "FtSockDesc" || !StringToInt(ftSockDesc, ftSock)) 224 | { 225 | LOG(LogLevels::Warning, "Not FT client connected! Force disconnected it! Socket descriptor: " + std::to_string(clientSocket)); 226 | FT_Server->DisconnectClient(clientSocket); 227 | return; 228 | } 229 | 230 | // Add pair message socket to file transmitter socket to map 231 | FT_Server->MtToFtSocketsCW.PedestrianStartCrossRoad(); 232 | FT_Server->MtToFtSocketsMap.emplace(clientSocket, (EN_SOCKET)ftSock); 233 | FT_Server->MtToFtSocketsCW.PedestrianStopCrossRoad(); 234 | 235 | EN_ThreadBarrier* barr; 236 | // Find required thread barrier 237 | FT_Server->FtSocketToBarrierCW.CarStartCrossRoad(); 238 | auto f = FT_Server->FtSocketToBarrierMap.find((EN_SOCKET)ftSock); 239 | 240 | // Check if it is thread barrier on map 241 | if (f != FT_Server->FtSocketToBarrierMap.end()) 242 | { 243 | // Save ptr to thread barrier 244 | barr = f->second; 245 | FT_Server->FtSocketToBarrierCW.CarStopCrossRoad(); 246 | 247 | // Add thread barrier with message transmitter key to map 248 | FT_Server->MtSocketToBarrierCW.PedestrianStartCrossRoad(); 249 | FT_Server->MtSocketToBarrierMap.emplace(clientSocket, barr); 250 | FT_Server->MtSocketToBarrierCW.PedestrianStopCrossRoad(); 251 | } 252 | else 253 | { 254 | LOG(LogLevels::Error, "Not find client barrier!"); 255 | FT_Server->FtSocketToBarrierCW.CarStopCrossRoad(); 256 | FT_Server->DisconnectClient(clientSocket); 257 | return; 258 | } 259 | 260 | barr->Wait(2); // Wait A 261 | FT_Server->OnClientConnected(clientSocket); 262 | } 263 | 264 | void EN_FT_Server_Internal_MessageTransmitter::ClientMessageHandler(EN_SOCKET clientSocket, std::string message) 265 | { 266 | FT_Server->ClientMessageHandler(clientSocket, message); 267 | } 268 | 269 | void EN_FT_Server_Internal_MessageTransmitter::OnClientDisconnect(EN_SOCKET clientSocket) 270 | { 271 | EN_ThreadBarrier* barr; 272 | // Remove pair with message transmitter socket and barrier from map 273 | FT_Server->MtSocketToBarrierCW.PedestrianStartCrossRoad(); 274 | auto f = FT_Server->MtSocketToBarrierMap.find(clientSocket); 275 | if (f != FT_Server->MtSocketToBarrierMap.end()) 276 | { 277 | barr = f->second; 278 | FT_Server->MtSocketToBarrierMap.erase(f); 279 | FT_Server->MtSocketToBarrierCW.PedestrianStopCrossRoad(); 280 | FT_Server->OnClientDisconnect(clientSocket); 281 | barr->Wait(2); // Wait B 282 | } 283 | 284 | // Remove message transmitter to file transmitter pair from map 285 | FT_Server->MtToFtSocketsCW.PedestrianStartCrossRoad(); 286 | FT_Server->MtToFtSocketsMap.erase(clientSocket); 287 | FT_Server->MtToFtSocketsCW.PedestrianStopCrossRoad(); 288 | } 289 | } 290 | 291 | int main() 292 | { 293 | EN::EN_FT_Server A; 294 | 295 | std::thread th([&A]() 296 | { 297 | try 298 | { 299 | A.Run(); 300 | } 301 | catch (std::runtime_error& err) 302 | { 303 | LOG(EN::LogLevels::Error, "Run throw error with error code: " + std::string(err.what())); 304 | } 305 | }); 306 | 307 | std::string message; 308 | 309 | while (true) 310 | { 311 | getline(std::cin, message); 312 | 313 | if (message == "f") 314 | { 315 | A.Shutdown(); 316 | break; 317 | } 318 | 319 | A.MulticastSend(message); 320 | } 321 | 322 | th.join(); 323 | } --------------------------------------------------------------------------------