├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── Todo.md ├── include ├── ArgParser.hpp ├── CustomSink.hpp ├── DPServer.hpp └── inet │ ├── IPConnection.hpp │ ├── MasterConnection.hpp │ ├── ServiceAddress.hpp │ ├── Socket.hpp │ ├── TCPAcceptor.hpp │ ├── TCPConnection.hpp │ ├── UDPConnection.hpp │ └── config.hpp.in ├── scripts ├── local │ ├── winbuild.bat │ └── winrun.sh ├── osx │ ├── build.sh │ └── install-dependencies.sh └── windows │ ├── build.bat │ └── install-dependencies.bat ├── src ├── ArgParser.cpp ├── DPServer.cpp ├── inet │ ├── CMakeLists.txt │ ├── IPConnection.cpp │ ├── MasterConnection.cpp │ ├── ServiceAddress.cpp │ ├── Socket.cpp │ ├── TCPAcceptor.cpp │ ├── TCPConnection.cpp │ └── UDPConnection.cpp └── main.cpp └── tests ├── CMakeLists.txt ├── CMakeLists.txt.in ├── include └── inet │ └── TCPAcceptor_test.hpp └── src ├── ArgParser_test.cpp ├── DPServer_test.cpp ├── inet ├── MasterConnection_test.cpp ├── ServiceAddress_test.cpp ├── Socket_test.cpp ├── TCPAcceptor_test.cpp ├── TCPConnection_test.cpp └── UDPConnection_test.cpp └── main_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | bin/ 3 | obj/ 4 | *.gch 5 | build/ 6 | gtest 7 | .lvimrc 8 | *.tmp 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | os: 4 | - osx 5 | - windows 6 | 7 | before_install: 8 | - if [ "$TRAVIS_OS_NAME" = "osx" ] ; then sh ./scripts/osx/install-dependencies.sh ; fi 9 | - if [ "$TRAVIS_OS_NAME" = "windows" ] ; then ./scripts/windows/install-dependencies.bat ; fi 10 | 11 | install: 12 | - if [ "$TRAVIS_OS_NAME" = "osx" ] ; then sh ./scripts/osx/build.sh ; fi 13 | - if [ "$TRAVIS_OS_NAME" = "windows" ] ; then ./scripts/windows/build.bat ; fi 14 | 15 | script: 16 | - if [ "$TRAVIS_OS_NAME" = "osx" ] ; then ./build/tests/DirectPlayHelperTest ; fi 17 | - if [ "$TRAVIS_OS_NAME" = "windows" ] ; then ./build/tests/Release/DirectPlayHelperTest.exe ; fi 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15 FATAL_ERROR) 2 | set(PROJECT_NAME DirectPlayHelper) 3 | include(ExternalProject) 4 | 5 | project("${PROJECT_NAME}" VERSION 0.2 LANGUAGES CXX) 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | set(CMAKE_CXX_EXTENSIONS ON) 10 | 11 | ##################################################################### 12 | # # 13 | # DEPENDENCIES # 14 | # # 15 | ##################################################################### 16 | 17 | ExternalProject_Add(g3log_project 18 | GIT_REPOSITORY https://github.com/KjellKod/g3log 19 | GIT_TAG master 20 | PREFIX "${CMAKE_CURRENT_BINARY_DIR}/g3log" 21 | CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/g3log -DG3_SHARED_LIB=OFF -DUSE_DYNAMIC_LOGGING_LEVELS=ON 22 | ) 23 | ExternalProject_Get_Property(g3log_project install_dir) 24 | message(STATUS "g3log installation directory: ${install_dir}") 25 | set(g3log_LOC "${install_dir}/lib/libg3logger.a") 26 | if(MSVC) 27 | set(g3log_LOC "${install_dir}/lib/g3logger.lib") 28 | endif() 29 | add_library(g3log STATIC IMPORTED) 30 | set_property(TARGET g3log PROPERTY IMPORTED_LOCATION 31 | "${g3log_LOC}") 32 | add_dependencies(g3log g3log_project) 33 | 34 | ##################################################################### 35 | # # 36 | # PROGRAM # 37 | # # 38 | ##################################################################### 39 | 40 | # build the app 41 | add_executable("${PROJECT_NAME}" 42 | src/main.cpp 43 | src/DPServer.cpp 44 | src/ArgParser.cpp 45 | ) 46 | 47 | # Includes 48 | target_include_directories("${PROJECT_NAME}" BEFORE 49 | PUBLIC 50 | include 51 | "${install_dir}/include" 52 | ) 53 | 54 | # build the inet library 55 | add_subdirectory(src/inet) 56 | 57 | # Link dependencies 58 | if(WIN32) 59 | set(ws2_32 ws2_32.lib) 60 | endif() 61 | target_link_libraries("${PROJECT_NAME}" 62 | PUBLIC 63 | g3log 64 | "${ws2_32}" 65 | inet 66 | ) 67 | 68 | # Oh yeah, and add the tests 69 | add_subdirectory(tests) 70 | 71 | 72 | ############################# 73 | # INSTALL # 74 | ############################# 75 | 76 | #install(TARGETS "${PROJECT_NAME}" DESTINATION bin) 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/kevincar/DPServer.svg?branch=master)](https://travis-ci.org/kevincar/DPServer) 2 | 3 | # Direct Play Server 4 | 5 | DirectPlay is a deprecated API that was part of Microsoft's DirectX API. 6 | DirectPlay is a network communication library inteded for computer game 7 | development, although its general nature allows it to be used for other 8 | purposes (From Wikipedia). 9 | 10 | DirectPlay communication requires a peer-to-peer connection, subsequently 11 | requiring users to open internet protocol (IP) ports for direct connection and 12 | communication. Users needed to know the public IP addresses of the hosting 13 | party to establsih a connection. While DirectPlay has a NAT helper which 14 | includes a component that takes advantage of Universal Plug-N-Play (UPnP), 15 | common on routers, this was not always reliable to establish communcation. 16 | 17 | The purpose of this project is to create a server to which which clients can 18 | connect. Once connected, the server then acts as a network holepunch and relay 19 | service to enable connected clients to discover eachother with out the 20 | requirement to forward IP ports nor to know public IP address of other 21 | clients. The hope is that this project will facilitate the ease to connect 22 | across DirectPlay communcations. 23 | -------------------------------------------------------------------------------- /Todo.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | Below is the current todo list for the project. The legend is as follows: 4 | 5 | ---------------------------- 6 | 7 | 8 | - [x] Hello World 9 | - [x] Test out the following [Networking Libraries](https://stackoverflow.com/questions/118945/best-c-c-network-library) before continuing to make 10 | your own. Nope. ASIO requres boost headers, I don't want that. ASE is too 11 | complex. QT is proprietary. 12 | - [ ] INET Library and namespace 13 | - [x] Socket 14 | - [x] ServiceAddress 15 | - [x] IPConnection 16 | - [x] Send 17 | - [x] Interface 18 | - [x] Implementation 19 | - [x] Test 20 | - [x] Recv 21 | - [x] TCPAcceptor Class 22 | - [x] Instance 23 | - [x] childConnections vector of childConnections 24 | - [x] connectionHandler function - the function to run when data is 25 | available on the childConnection 26 | - [x] acceptHandler function - the function ran when 27 | data is available on the parent TCPAcceptor to check if the 28 | incoming connection should be accepted 29 | - [x] constructor - should take two arguments of shared_ptrs to the 30 | process and accept handler functions that will define the behavior 31 | or the TCPService 32 | - [ ] Remove inheritance from shared_with_this. Instead we should 33 | consider the MasterConnection to be an implementation of the fdset 34 | type, that then passes itself to functions that the TCPAcceptor and 35 | UDPConnections manage on their own. Alternatively, we can send 36 | requests to remove connections, but this might be more costly in 37 | time/resource. 38 | - [x] Implementation 39 | - [x] Testing 40 | - [ ] MasterConnection 41 | - [x] Rename MasterTCPConnection to MasterConnection 42 | - [x] Remove the TCPConnection inheritance and make the class it's own 43 | - [x] Rename and redefine TCPConnections to Connections as IPConnection 44 | to support both TCP and UDP 45 | - [x] MasterConnection::MasterConnection - should automatically start 46 | it's connection checking loop even if there are no connections to 47 | check. 48 | - [ ] MasterConnection::checkAllConnectionsForData - get this up and 49 | running even if no connections are present 50 | - [ ] MasterConnection::getAllConnections - remove this function. We 51 | shouldn't make the code ambiguous. Just manulate the UDPConnection 52 | vector and the TCPAcceptor vector directly 53 | - [ ] TCP Support 54 | - [x] MasterConnection::TCPAcceptors - a private vector of TCPAccptors 55 | where new TCPAcceptors are pushed to by calling createTCPAcceptor 56 | - [ ] MasterConnection::createTCPAcceptor - Creates a listening TCP 57 | server connection and adds this to the TCPAcceptor list 58 | - [ ] MasterConnection::removeTCPAcceptor - Destroys a TCPAcceptor and 59 | it's child connections 60 | - [x] UDP Support 61 | - [x] MasterConnection::addConnection - this will be used to decrease 62 | duplicated code between TCP and udp connection additions 63 | - [x] Simplify the MasterTCPConnection addition to utilize the new 64 | addConnection function 65 | - [x] MasterConnection::addUDP 66 | - [ ] Because we want to support multiple processHanders, we should have 67 | a map of Connections and processHandlers so that different connections 68 | can be processed differently. But perhaps all TCP will be handled one 69 | way and UDP will be handled another so there will likely be a large 70 | difference between the number of connections and number of 71 | connectionHandlers. They should be shared pointers such that when the 72 | connection is added, it can check if the pointer already exists and 73 | then simply refer to that. Additionally, each master function should 74 | have it's own accept function, the accept function is what adds new 75 | TCPConnections and so all accepted TCPConnections under the same 76 | master connection will inherit the same process function. 77 | UDPConnections however, can do their own thing and do not have master 78 | connections, although this could simply be implemented, that's not 79 | their purpose. Instead the master UDP connection should simply handle 80 | the data that comes in on them and send a response appropriately. At 81 | least for our case. 82 | - [x] Comment out bad code until we can replace it 83 | - [ ] MasterConnection::MasterConnection - Constructor - Consider what 84 | the class should start out with 85 | - [ ] What will the class need? 86 | - [ ] IPConnections map that maps ID's to connections 87 | - [ ] processHandler map that maps ID's to processHandlers 88 | - Note, masterTCP processHandlers will be those that accept 89 | incomping connections. Consider whether bool keeps the 90 | TCPconnection alive or whether that means to accept the connection 91 | or what? 92 | - [ ] a map of listening states for all masterTCPs. ONLY if we need 93 | them. it is possible that the Listening state is only required for 94 | the running thread, in which case this needs to be redefined to 95 | `running` state. Do we actually need to keep track of the 96 | "listening" state of the masterTCP connections? 97 | - [ ] MasterConnection::AddMasterTCP() to add another TCP connection 98 | that listens for incomming connection. Should take one parameter that 99 | is std::shared_ptr handle to the accept handler function 100 | - [ ] TCPConnection 101 | - [ ] UDPConnection 102 | - [ ] sendFrom 103 | - [x] Interface 104 | - [x] Implementation 105 | - [ ] Test 106 | - [ ] recvFrom 107 | - [ ] DPServer Class 108 | - [ ] Server class 109 | - [ ] Client class 110 | -------------------------------------------------------------------------------- /include/ArgParser.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DPS_ARGPARSER_HPP 3 | #define DPS_ARGPARSER_HPP 4 | 5 | #include 6 | #include 7 | 8 | class ArgParser 9 | { 10 | public: 11 | 12 | ArgParser(int argc, char const** argv); 13 | 14 | static std::vector vectorize(int argc, char const** argv); 15 | 16 | std::vector getArgs(void) const; 17 | 18 | private: 19 | 20 | int nArgs; 21 | std::vector args; 22 | }; 23 | 24 | #endif /* DPS_ARGPARSER_HPP */ 25 | -------------------------------------------------------------------------------- /include/CustomSink.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class CustomSink 6 | { 7 | public: 8 | void ReceiveLogMessage(g3::LogMessageMover logEntry) 9 | { 10 | std::cout << logEntry.get().toString() << std::endl; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /include/DPServer.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DPS_DPSERVER_HPP 3 | #define DPS_DPSERVER_HPP 4 | 5 | #include 6 | #include 7 | 8 | class DPServer 9 | { 10 | 11 | public: 12 | 13 | enum APPSTATE 14 | { 15 | NOT_SET, 16 | SERVER, 17 | CLIENT 18 | }; 19 | 20 | DPServer(int argc, char const* argv[]); 21 | 22 | void start(void); 23 | void processArgs(void); 24 | 25 | APPSTATE getAppState(void) const; 26 | std::string getHostIPAddress(void) const; 27 | int getConnPort(void) const; 28 | 29 | private: 30 | int nArgs; 31 | std::vector args; 32 | 33 | APPSTATE appState = NOT_SET; 34 | std::string hostIPAddress = ""; 35 | int connPort = 0; 36 | 37 | void usage(void) const; 38 | 39 | bool setAppState(APPSTATE as); 40 | 41 | }; 42 | 43 | #endif /* DPS_DPSERVER_HPP */ 44 | -------------------------------------------------------------------------------- /include/inet/IPConnection.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_IP_CONNECTION_HPP 3 | #define INET_IP_CONNECTION_HPP 4 | 5 | #include "inet/config.hpp" 6 | 7 | #include "inet/Socket.hpp" 8 | #include "inet/ServiceAddress.hpp" 9 | #include 10 | 11 | #ifdef HAVE_SOCKET_H 12 | #define SOCKLEN socklen_t 13 | #endif /* HAVE_SOCKET_H */ 14 | #ifdef HAVE_WINSOCK2_H 15 | #define SOCKLEN int 16 | #endif /* HAVE_WINSOCK2_H */ 17 | 18 | 19 | namespace inet 20 | { 21 | 22 | class IPConnection 23 | { 24 | public: 25 | IPConnection(int type, int protocol); 26 | IPConnection(int captureRawSocket, int type, int protocol, IPConnection const& parentConnection, sockaddr_in& destAddr); 27 | 28 | virtual ~IPConnection() = default; 29 | 30 | std::string const getAddressString(void) const; 31 | std::string const getIPAddressString(void) const; 32 | std::string const getPortString(void) const; 33 | std::string const getPort(void) const; 34 | void setAddress(std::string const& address); 35 | void listen(void); 36 | bool isDataReady(double timeout) const; 37 | int connect(std::string addressString); 38 | int send(char const* data, unsigned int const data_len) const; 39 | int recv(char* buffer, unsigned int buffer_len) const; 40 | 41 | operator int const() const; 42 | 43 | protected: 44 | mutable std::mutex socket_mutex; 45 | mutable std::mutex srcAddr_mutex; 46 | mutable std::mutex destAddr_mutex; 47 | Socket socket; 48 | ServiceAddress srcAddress {}; 49 | ServiceAddress destAddress {}; 50 | 51 | void updateSrcAddr(void); 52 | }; 53 | } 54 | 55 | #endif /* INET_IP_CONNECTION_HPP */ 56 | -------------------------------------------------------------------------------- /include/inet/MasterConnection.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_MASTER_CONNECTION_HPP 3 | #define INET_MASTER_CONNECTION_HPP 4 | 5 | #include "inet/config.hpp" 6 | 7 | #include 8 | #include 9 | #include "inet/TCPConnection.hpp" 10 | #include "inet/UDPConnection.hpp" 11 | #include "inet/TCPAcceptor.hpp" 12 | 13 | namespace inet 14 | { 15 | class MasterConnection 16 | { 17 | public: 18 | typedef std::function ProcessHandler; 19 | 20 | MasterConnection(double const t = 5.0); 21 | ~MasterConnection(void); 22 | 23 | // Listening Control 24 | bool isListening(void) const; 25 | 26 | // General Connection Control 27 | unsigned int getNumTCPAcceptors(void) const; 28 | unsigned int getNumTCPConnections(void) const; 29 | unsigned int getNumUDPConnections(void) const; 30 | unsigned int getNumConnections(void) const; 31 | 32 | // TCP Connection Control 33 | unsigned int createTCPAcceptor(TCPAcceptor::AcceptHandler const& pAcceptPH, TCPAcceptor::ProcessHandler const& pChildPH); 34 | std::vector getAcceptors(void) const; 35 | void removeTCPAcceptor(unsigned int acceptConnID); 36 | 37 | // UDP Connection Control 38 | unsigned int createUDPConnection(std::unique_ptr& pPH); 39 | std::vector getUDPConnections(void) const; 40 | void removeUDPConnection(unsigned int connID); 41 | 42 | std::shared_ptr const answerIncomingConnection(void) const; 43 | 44 | private: 45 | double timeout = 5.0; 46 | 47 | // Thread Management 48 | std::thread listeningThread; 49 | std::mutex listeningThread_mutex; 50 | 51 | bool listening = false; 52 | mutable std::mutex listening_mutex; 53 | 54 | // TCP Connections 55 | std::vector> acceptors; 56 | mutable std::mutex acceptor_mutex; 57 | 58 | // UDP Connections 59 | std::vector> udpConnections; 60 | mutable std::mutex udp_mutex; 61 | 62 | std::map> processHandlers; 63 | mutable std::mutex proc_mutex; 64 | 65 | void stopListening(void); 66 | void setListeningState(bool state); 67 | void beginListening(); 68 | void startListening(); 69 | 70 | std::unique_ptr> getAllConnections(void) const; 71 | 72 | // Connection Processing 73 | bool loadFdSetConnections(fd_set&) const; 74 | bool loadFdSetTCPConnections(fd_set&) const; 75 | bool loadFdSetUDPConnections(fd_set&) const; 76 | int waitForFdSetConnections(fd_set&) const; 77 | void checkAndProcessConnections(); 78 | void checkAndProcessTCPConnections(fd_set& fdSet); 79 | void checkAndProcessUDPConnections(fd_set& fdSet); 80 | 81 | int getLargestSocket(void) const; 82 | int getLargestTCPSocket(void) const; 83 | int getLargestUDPSocket(void) const; 84 | }; 85 | } 86 | 87 | #endif /* INET_MASTER_CONNECTION_HPP */ 88 | -------------------------------------------------------------------------------- /include/inet/ServiceAddress.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_SERVICE_ADDRESS_HPP 3 | #define INET_SERVICE_ADDRESS_HPP 4 | 5 | #include "inet/config.hpp" 6 | #include "inet/Socket.hpp" 7 | 8 | #include 9 | #include 10 | #ifdef HAVE_NETINET_IN_H 11 | #include 12 | #define CSTR(X) X.c_str() 13 | #define INET_ATON(X, Y) ::inet_aton(CSTR(X), Y) 14 | #define ATON_ERROR 0 15 | #define SOCKLEN socklen_t 16 | #endif /* HAVE_NETINET_IN_H */ 17 | #ifdef HAVE_WINSOCK2_H 18 | #include 19 | #define WSTR(X) std::wstring(X.begin(), X.end()).c_str() 20 | #define INET_ATON(X, Y) InetPtonW(AF_INET, WSTR(X), Y) 21 | #define ATON_ERROR -1 22 | #define SOCKLEN int 23 | //#ifdef HAVE_WS2TCPIP_H 24 | //#include 25 | //#endif [> HAVE_WS2TCPIP_H <] 26 | #endif /* HAVE_WINSOCK2_H */ 27 | #include 28 | 29 | namespace inet 30 | { 31 | class Socket; 32 | class ServiceAddress 33 | { 34 | public: 35 | ServiceAddress(void); 36 | ServiceAddress(std::string const& AddressString); 37 | ServiceAddress(sockaddr_in const& captureAddr); 38 | 39 | std::string const getAddressString(void) const; 40 | std::string const getIPAddressString(void) const; 41 | std::string const getPortString(void) const; 42 | unsigned int getPort(void) const; 43 | void setAddressString(std::string const& address); 44 | void setIPAddressString(std::string const& IPAddress); 45 | void setPortString(std::string const& port); 46 | void setPort(int port); 47 | 48 | operator sockaddr const* () const; 49 | operator sockaddr * (); 50 | private: 51 | sockaddr_in addr {}; 52 | mutable std::mutex addr_mutex; 53 | 54 | sockaddr const* getAddr(void) const; 55 | static const std::vector getIPandPort(const std::string AddressString); 56 | }; 57 | } 58 | 59 | #endif /* INET_SERVICE_ADDRESS_HPP */ 60 | -------------------------------------------------------------------------------- /include/inet/Socket.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_SOCKET_HPP 3 | #define INET_SOCKET_HPP 4 | 5 | #ifdef HAVE_SOCKET_H 6 | #define ERRORCODE errno 7 | #define INVALID_SOCKET -1 8 | #define SOCKET_ERROR -1 9 | #define _CLOSE(X) ::close(X) 10 | #define ERR(X) X 11 | #endif /* HAVE_SOCKET_H */ 12 | 13 | #ifdef HAVE_WINSOCK2_H 14 | #define ERRORCODE WSAGetLastError() 15 | #define _CLOSE(X) closesocket(X) 16 | #define ERR(X) WSA ## X 17 | #endif /* HAVE_WINSOCK2_H */ 18 | 19 | namespace inet 20 | { 21 | class Socket 22 | { 23 | public: 24 | Socket(int f, int t, int p); 25 | Socket(int capture, int f, int t, int p); 26 | virtual ~Socket(); 27 | void listen(void); 28 | operator int() const; 29 | private: 30 | int close(void); 31 | 32 | void startup(void); 33 | void shutdown(void); 34 | 35 | int socket {-1}; 36 | int family {-1}; 37 | int type {-1}; 38 | int protocol {-1}; 39 | 40 | static int n_sockets; 41 | }; 42 | } 43 | 44 | #endif /* INET_SOCKET_HPP */ 45 | -------------------------------------------------------------------------------- /include/inet/TCPAcceptor.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_TCP_ACCEPTOR 3 | #define INET_TCP_ACCEPTOR 4 | 5 | #include 6 | #include 7 | #include "inet/TCPConnection.hpp" 8 | 9 | namespace inet 10 | { 11 | class TCPAcceptor : public TCPConnection 12 | { 13 | 14 | public: 15 | typedef std::function AcceptHandler; 16 | typedef std::function ProcessHandler; 17 | 18 | TCPAcceptor(AcceptHandler const& AcceptHandler, ProcessHandler const& ConnectionHandler); 19 | 20 | int getLargestSocket(void) const; 21 | 22 | std::vector getConnections(void) const; 23 | void removeConnection(int connectionSocket); 24 | TCPConnection const& accept(void); 25 | void loadFdSetConnections(fd_set& fdSet); 26 | void checkAndProcessConnections(fd_set const& fdSet); 27 | 28 | AcceptHandler const getAcceptHandler() const; 29 | ProcessHandler const getConnectionHandler() const; 30 | 31 | protected: 32 | std::vector> childConnections; 33 | mutable std::mutex child_mutex; 34 | 35 | std::mutex acceptHandler_mutex; 36 | AcceptHandler acceptHandler; 37 | std::mutex connectionHandler_mutex; 38 | ProcessHandler connectionHandler; 39 | }; 40 | } 41 | 42 | #endif /* INET_TCP_ACCEPTOR */ 43 | -------------------------------------------------------------------------------- /include/inet/TCPConnection.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_TCP_CONNECTION_HPP 3 | #define INET_TCP_CONNECTION_HPP 4 | 5 | #include 6 | #include "inet/IPConnection.hpp" 7 | 8 | namespace inet 9 | { 10 | class TCPConnection : public IPConnection 11 | { 12 | public: 13 | TCPConnection(void); 14 | TCPConnection(int captureRawSocket, IPConnection const& parentConnection, sockaddr_in& destAddr); 15 | }; 16 | } 17 | 18 | #endif /* INET_TCP_CONNECTION_HPP */ 19 | -------------------------------------------------------------------------------- /include/inet/UDPConnection.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_UDP_CONNECTION_HPP 3 | #define INET_UDP_CONNECTION_HPP 4 | 5 | #include "inet/IPConnection.hpp" 6 | 7 | namespace inet 8 | { 9 | class UDPConnection : public IPConnection 10 | { 11 | public: 12 | UDPConnection(void); 13 | int sendTo(char const* data, int const data_len, ServiceAddress const& addr) const; 14 | int recvFrom(char* buffer, int const buffer_len, ServiceAddress& addr) const; 15 | }; 16 | } 17 | 18 | #endif /* INET_UDP_CONNECTION_HPP */ 19 | -------------------------------------------------------------------------------- /include/inet/config.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef INET_CONFIG_HPP 2 | #define INET_CONFIG_HPP 3 | 4 | #cmakedefine HAVE_UNISTD_H 5 | #cmakedefine HAVE_ERRNO_H 6 | #cmakedefine HAVE_SOCKET_H 7 | #cmakedefine HAVE_WINSOCK2_H 8 | #cmakedefine HAVE_NETINET_IN_H 9 | #cmakedefine HAVE_ARPA_INET_H 10 | #cmakedefine HAVE_WS2TCPIP_H 11 | #cmakedefine HAVE_SELECT_H 12 | 13 | #endif /* INET_CONFIG_HPP */ 14 | -------------------------------------------------------------------------------- /scripts/local/winbuild.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: Move into the projects directory 3 | cd C:/Users/Kevin/Documents/Computer-Projects/C++-Projects 4 | 5 | :: Check if the project directory exists 6 | IF exist ".\DPServer\" ( 7 | echo "YES" 8 | ::echo "removing folder and recloning" 9 | RMDIR DPServer /S /Q 10 | git clone https://github.com/kevincar/DPServer.git 11 | ) ELSE ( 12 | :: NO DPServer directory... cloning 13 | echo "NO" 14 | git clone https://github.com/kevincar/DPServer.git 15 | ) 16 | 17 | :: Create build directory 18 | cd .\DPServer 19 | git checkout windev 20 | echo "Creating build directory" 21 | mkdir build 22 | cd build 23 | 24 | call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" 25 | :: call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat" 26 | 27 | :: Install Dependencies 28 | echo "Installing Dependencies" 29 | call "../scripts/win/install-dependencies.bat" 30 | 31 | :: CMAKE 32 | echo "Running cmake" 33 | cmake -Dg3logger_DIR="C:\Users\Kevin\Documents\Computer-Projects\C++-Projects\DPServer\build\g3log\build" .. 34 | -------------------------------------------------------------------------------- /scripts/local/winrun.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | cd "$(dirname "$0")" 4 | 5 | ssh Kevin@192.168.8.4 < "./winbuild.bat" 6 | -------------------------------------------------------------------------------- /scripts/osx/build.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | # This file should be run from the repository root directory 4 | 5 | echo "Checking for build directory..." 6 | if [ -d build ] ; then 7 | echo "build directory already exists. Remvoing..." 8 | rm -rf ./build 9 | fi 10 | 11 | echo "Creating build directory" 12 | mkdir build 13 | 14 | echo "Moving into build directory" 15 | cd ./build 16 | 17 | echo "Generating build environment..." 18 | cmake .. 19 | 20 | echo "Making..." 21 | cmake --build . 22 | -------------------------------------------------------------------------------- /scripts/osx/install-dependencies.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | echo "Install OSX Dependencies!" 3 | 4 | #brew install g3log 5 | #brew install gtest 6 | -------------------------------------------------------------------------------- /scripts/windows/build.bat: -------------------------------------------------------------------------------- 1 | ECHO "Checking for build directory..." 2 | 3 | IF exist ".\build\" ( 4 | ECHO "build directory already exists. Removing..." 5 | RMDIR /S /Q build 6 | ) 7 | 8 | ECHO "Creating build directory" 9 | MKDIR build 10 | 11 | ECHO "Moving into build directory" 12 | CD build 13 | 14 | ECHO "Enabling MSVC Dev Tools" 15 | REM CALL "%MSVCTools%/VsDevCmd.bat" 16 | 17 | ECHO "Generating build environment..." 18 | REM cmake -DCMAKE_PREFIX_PATH="C:\g3log" .. 19 | cmake .. 20 | 21 | ECHO "Making..." 22 | cmake --build . --config Release 23 | -------------------------------------------------------------------------------- /scripts/windows/install-dependencies.bat: -------------------------------------------------------------------------------- 1 | echo "Install Windows Dependencies!" 2 | 3 | :: G3LOGGER 4 | REM git clone https://github.com/KjellKod/g3log.git 5 | REM cd g3log 6 | 7 | REM echo "Creating a build directory" 8 | REM mkdir build 9 | REM echo "Moving into build directory for g3log" 10 | REM cd build 11 | 12 | REM echo "calling CMAKE for g3log dependency" 13 | REM cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 16 2019" -DCMAKE_INSTALL_PREFIX=C:/g3log .. 14 | 15 | REM echo "Building g3log" 16 | REM ::msbuild g3log.sln /p:Configuration=Release 17 | REM cmake --build . --config Release 18 | 19 | REM echo "Install" 20 | REM cmake --build . --target install 21 | 22 | REM cd .. 23 | REM cd .. 24 | -------------------------------------------------------------------------------- /src/ArgParser.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ArgParser.hpp" 3 | 4 | ArgParser::ArgParser(int argc, char const** argv) : nArgs(argc) 5 | { 6 | this->args = this->vectorize(argc, argv); 7 | return; 8 | } 9 | 10 | std::vector ArgParser::vectorize(int argc, char const** argv) 11 | { 12 | std::vector result; 13 | 14 | for(int i = 0; i < argc; i++) 15 | { 16 | std::string curArg = std::string(argv[i]); 17 | result.emplace_back(curArg); 18 | } 19 | 20 | return result; 21 | } 22 | 23 | std::vector ArgParser::getArgs(void) const 24 | { 25 | return this->args; 26 | } 27 | -------------------------------------------------------------------------------- /src/DPServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "DPServer.hpp" 5 | #include "ArgParser.hpp" 6 | 7 | DPServer::DPServer(int argc, char const* argv[]) : nArgs(argc) 8 | { 9 | ArgParser argParser {argc, argv}; 10 | std::vector parsedArgs = argParser.getArgs(); 11 | this->args = parsedArgs; 12 | } 13 | 14 | void DPServer::start(void) 15 | { 16 | this->processArgs(); 17 | return; 18 | } 19 | 20 | void DPServer::processArgs(void) 21 | { 22 | if(this->nArgs <= 1) 23 | { 24 | return this->usage(); 25 | } 26 | 27 | for(unsigned int i = 0; i < this->args.size(); i++) 28 | { 29 | std::string curArg = this->args[i]; 30 | LOG(DEBUG) << "Processing Arg: " << curArg; 31 | if( (curArg == "-s") || (curArg == "--server") ) 32 | { 33 | LOG(DEBUG) << "Server argument found"; 34 | if(this->setAppState(SERVER) == false) 35 | { 36 | LOG(INFO) << "DPServer state has already been set"; 37 | this->appState = NOT_SET; 38 | return; 39 | } 40 | } 41 | else if( (curArg == "-c") || (curArg == "--client") ) 42 | { 43 | LOG(DEBUG) << "Client argument found"; 44 | if(this->setAppState(CLIENT) == false) 45 | { 46 | LOG(INFO) << "DPServer state has already been set"; 47 | this->appState = NOT_SET; 48 | return; 49 | } 50 | 51 | if(i == this->args.size()) 52 | { 53 | LOG(INFO) << "No host IP Address provided for client"; 54 | this->appState = NOT_SET; 55 | return; 56 | } 57 | 58 | std::string potentialHostIPAddress = (i+1 >= this->args.size()) ? "" : this->args[i+1]; 59 | LOG(DEBUG) << "Potential Host Address: \"" << potentialHostIPAddress << "\""; 60 | std::regex pattern("([0-9]+\\.){3}[0-9]+"); 61 | bool match = std::regex_match(potentialHostIPAddress, pattern); 62 | if(match == false) 63 | { 64 | LOG(INFO) << potentialHostIPAddress << " is not a properly formated host name"; 65 | this->appState = NOT_SET; 66 | return; 67 | } 68 | 69 | this->hostIPAddress = potentialHostIPAddress; 70 | i++; 71 | } 72 | else if( (curArg == "-p") || (curArg == "--port") ) 73 | { 74 | LOG(DEBUG) << "Port argument found"; 75 | if(i == this->args.size()) 76 | { 77 | LOG(INFO) << "Port flag used but no port number provided"; 78 | } 79 | 80 | std::string potentialPortNum = this->args[i+1]; 81 | this->connPort = std::stoi(potentialPortNum); 82 | } 83 | } 84 | 85 | return; 86 | } 87 | 88 | DPServer::APPSTATE DPServer::getAppState(void) const 89 | { 90 | return this->appState; 91 | } 92 | 93 | std::string DPServer::getHostIPAddress(void) const 94 | { 95 | return this->hostIPAddress; 96 | } 97 | 98 | int DPServer::getConnPort(void) const 99 | { 100 | return this->connPort; 101 | } 102 | 103 | void DPServer::usage(void) const 104 | { 105 | std::cout << "Usage: DPServer <-s | -c> [options]" << "\n" 106 | << "\n" 107 | << "Arguments" << "\n" 108 | << "\n" 109 | << " -s | --server Start the application as a server" << "\n" 110 | << " -c | --client Start the application as a client. Host is the IP Address of the server to connect to." << "\n" 111 | << "\n" 112 | << " -p | --port Set the server-client connection port to " << "\n" 113 | << std::endl; 114 | 115 | return; 116 | } 117 | 118 | bool DPServer::setAppState(DPServer::APPSTATE as) 119 | { 120 | if(this->appState != NOT_SET) 121 | { 122 | return false; 123 | } 124 | this->appState = as; 125 | return true; 126 | } 127 | -------------------------------------------------------------------------------- /src/inet/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_STANDARD 17) 2 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 3 | set(CMAKE_CXX_EXTENSIONS OFF) 4 | 5 | # Dependencies 6 | include(CheckIncludeFiles) 7 | check_include_files("unistd.h" HAVE_UNISTD_H) 8 | check_include_files("errno.h" HAVE_ERRNO_H) 9 | check_include_files("sys/socket.h" HAVE_SOCKET_H) 10 | check_include_files("winsock2.h" HAVE_WINSOCK2_H) 11 | check_include_files("netinet/in.h" HAVE_NETINET_IN_H) 12 | check_include_files("arpa/inet.h" HAVE_ARPA_INET_H) 13 | check_include_files("ws2tcpip.h" HAVE_WS2TCPIP_H) 14 | check_include_files("sys/select.h" HAVE_SELECT_H) 15 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../../include/inet/config.hpp.in" "${CMAKE_CURRENT_BINARY_DIR}/include/inet/config.hpp") 16 | 17 | add_library(inet 18 | Socket.cpp 19 | ServiceAddress.cpp 20 | IPConnection.cpp 21 | TCPConnection.cpp 22 | UDPConnection.cpp 23 | TCPAcceptor.cpp 24 | MasterConnection.cpp 25 | ) 26 | 27 | target_include_directories(inet BEFORE 28 | PUBLIC 29 | ../../include 30 | ${install_dir}/include 31 | "${CMAKE_CURRENT_BINARY_DIR}/include" # For config.hpp 32 | ) 33 | -------------------------------------------------------------------------------- /src/inet/IPConnection.cpp: -------------------------------------------------------------------------------- 1 | #include "inet/config.hpp" 2 | 3 | #ifdef HAVE_SOCKET_H 4 | #include 5 | #include 6 | #include 7 | #endif /* HAVE_SOCKET_H */ 8 | #ifdef HAVE_WINSOCK2_H 9 | #include 10 | #endif /* HAVE_WINSOCK2_H */ 11 | 12 | #include 13 | #include "inet/Socket.hpp" 14 | #include "inet/ServiceAddress.hpp" 15 | #include "inet/IPConnection.hpp" 16 | 17 | namespace inet 18 | { 19 | IPConnection::IPConnection(int type, int protocol) : socket(AF_INET, type, protocol) { } 20 | 21 | IPConnection::IPConnection(int capture, int type, int protocol, IPConnection const& parentConnection, sockaddr_in& destAddr) : 22 | socket(capture, AF_INET, type, protocol), 23 | srcAddress(parentConnection.srcAddress.getAddressString()), 24 | destAddress(destAddr) { } 25 | 26 | std::string const IPConnection::getAddressString(void) const 27 | { 28 | std::lock_guard lock {this->srcAddr_mutex}; 29 | return this->srcAddress.getAddressString(); 30 | } 31 | 32 | std::string const IPConnection::getIPAddressString(void) const 33 | { 34 | std::lock_guard lock {this->srcAddr_mutex}; 35 | return this->srcAddress.getIPAddressString(); 36 | } 37 | 38 | void IPConnection::setAddress(std::string const& address) 39 | { 40 | { 41 | // Set the address 42 | std::lock_guard srcAddr_lock {this->srcAddr_mutex}; 43 | this->srcAddress.setAddressString(address); 44 | 45 | // Bind 46 | std::lock_guard sock_lock {this->socket_mutex}; 47 | //this->srcAddress->bind(this->socket); 48 | int result = ::bind(this->socket, this->srcAddress, sizeof(sockaddr_in)); 49 | if(result == -1) 50 | { 51 | throw std::out_of_range(std::string("IPConnection::setAddress Failed to set address binding: ") + std::to_string(ERRORCODE)); 52 | } 53 | } 54 | 55 | // using ADDR_ANY will result in a random assignment so update to be accurate 56 | // Removed the condition because 0.0.0.0 and 127.0.0.1 behave 57 | // differently on different OS's so it's better to just update the 58 | // address to however the OS interpreted it 59 | //if(address == "0.0.0.0:0") 60 | //{ 61 | this->updateSrcAddr(); 62 | //} 63 | } 64 | 65 | void IPConnection::listen(void) 66 | { 67 | std::lock_guard socket_lock {this->socket_mutex}; 68 | this->socket.listen(); 69 | 70 | this->updateSrcAddr(); 71 | } 72 | 73 | bool IPConnection::isDataReady(double timeout) const 74 | { 75 | fd_set fs; 76 | FD_ZERO(&fs); 77 | 78 | std::lock_guard lock {this->socket_mutex}; 79 | FD_SET(this->socket, &fs); 80 | 81 | timeout = std::abs(timeout); 82 | double seconds = floor(timeout); 83 | double remainder = timeout - seconds; 84 | double microseconds = floor(remainder * 1e6); 85 | struct timeval tv; 86 | tv.tv_sec = static_cast(seconds); 87 | tv.tv_usec = static_cast(microseconds); 88 | 89 | int result = ::select(this->socket+1, &fs, nullptr, nullptr, &tv); 90 | if(result == -1) 91 | { 92 | throw std::out_of_range(std::string("IPConnection::isDataReady failed with errno: ") + std::to_string(ERRORCODE)); 93 | } 94 | 95 | return FD_ISSET(this->socket, &fs); 96 | } 97 | 98 | int IPConnection::connect(std::string addressString) 99 | { 100 | // Initiate the destAddress 101 | std::lock_guard destAddr_lock {this->destAddr_mutex}; 102 | this->destAddress.setAddressString(addressString); 103 | 104 | // connect to the address 105 | std::lock_guard socket_lock {this->socket_mutex}; 106 | int result = ::connect(this->socket, this->destAddress, sizeof(sockaddr_in)); 107 | if(result == SOCKET_ERROR) 108 | { 109 | return ERRORCODE; 110 | } 111 | 112 | 113 | this->updateSrcAddr(); 114 | 115 | return 0; 116 | } 117 | 118 | int IPConnection::send(char const* data, unsigned int const data_len) const 119 | { 120 | long result = ::send(*this, data, data_len, 0); 121 | return static_cast(result); 122 | } 123 | 124 | int IPConnection::recv(char* buffer, unsigned int buffer_len) const 125 | { 126 | long result = ::recv(*this, buffer, buffer_len, 0); 127 | return static_cast(result); 128 | } 129 | 130 | IPConnection::operator int const() const 131 | { 132 | return static_cast(this->socket); 133 | } 134 | 135 | void IPConnection::updateSrcAddr(void) 136 | { 137 | std::lock_guard addr_lock {this->srcAddr_mutex}; 138 | SOCKLEN addrlen {sizeof(sockaddr_in)}; 139 | int result = ::getsockname(this->socket, this->srcAddress, &addrlen); 140 | if(result == -1) 141 | { 142 | throw std::out_of_range(std::string("IPConnection::listen failed to update address after listen: ") + std::to_string(ERRORCODE)); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/inet/MasterConnection.cpp: -------------------------------------------------------------------------------- 1 | #include "inet/MasterConnection.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef HAVE_SELECT_H 9 | #include 10 | #endif /* HAVE_SELECT_H */ 11 | #ifdef HAVE_WINSOCK2_H 12 | #include 13 | #endif /* HAVE_WINSOCK2_H */ 14 | 15 | #include 16 | 17 | namespace inet 18 | { 19 | MasterConnection::MasterConnection(double const t) : timeout(t) 20 | { 21 | this->startListening(); 22 | } 23 | 24 | MasterConnection::~MasterConnection(void) 25 | { 26 | if(this->isListening()) 27 | { 28 | this->stopListening(); 29 | } 30 | } 31 | 32 | bool MasterConnection::isListening(void) const 33 | { 34 | std::lock_guard lock {this->listening_mutex}; 35 | return this->listening; 36 | } 37 | 38 | unsigned int MasterConnection::getNumTCPAcceptors(void) const 39 | { 40 | unsigned int result = 0; 41 | 42 | std::lock_guard acceptorLock {this->acceptor_mutex}; 43 | result = static_cast(this->acceptors.size()); 44 | 45 | return result; 46 | } 47 | 48 | unsigned int MasterConnection::getNumTCPConnections(void) const 49 | { 50 | unsigned int result = 0; 51 | 52 | result += this->getNumTCPAcceptors(); 53 | 54 | // TCPAcceptor connections 55 | { 56 | std::lock_guard acceptor_lock {this->acceptor_mutex}; 57 | for(std::unique_ptr const& acceptor : this->acceptors) 58 | { 59 | std::vector acceptorConnections = acceptor->getConnections(); 60 | result += acceptorConnections.size(); 61 | } 62 | } 63 | 64 | return result; 65 | } 66 | 67 | unsigned int MasterConnection::getNumUDPConnections(void) const 68 | { 69 | unsigned int result = 0; 70 | 71 | std::lock_guard udp_lock { this->udp_mutex }; 72 | result += this->udpConnections.size(); 73 | 74 | return result; 75 | } 76 | 77 | unsigned int MasterConnection::getNumConnections(void) const 78 | { 79 | unsigned int result = 0; 80 | 81 | //LOG(DEBUG) << "getting numTCPConnections"; 82 | result += this->getNumTCPConnections(); 83 | 84 | //LOG(DEBUG) << "getting numUDPConnections"; 85 | result += this->getNumUDPConnections(); 86 | 87 | return result; 88 | } 89 | 90 | unsigned int MasterConnection::createTCPAcceptor(TCPAcceptor::AcceptHandler const& pAcceptPH, TCPAcceptor::ProcessHandler const& pChildPH) 91 | { 92 | std::lock_guard acceptorLock {this->acceptor_mutex}; 93 | TCPAcceptor* acceptor = new TCPAcceptor(pAcceptPH, pChildPH); 94 | std::unique_ptr pAcceptor {std::move(acceptor)}; 95 | this->acceptors.push_back(std::move(pAcceptor)); 96 | 97 | return 0; 98 | } 99 | 100 | std::vector MasterConnection::getAcceptors(void) const 101 | { 102 | std::lock_guard acceptorLock {this->acceptor_mutex}; 103 | std::vector result {}; 104 | 105 | for(std::vector>::const_iterator it = this->acceptors.begin(); it != this->acceptors.end(); it++) 106 | { 107 | TCPAcceptor const* curAcceptor = &(*it->get()); 108 | result.push_back(curAcceptor); 109 | } 110 | 111 | return result; 112 | } 113 | 114 | void MasterConnection::removeTCPAcceptor(unsigned int acceptorID) 115 | { 116 | // Is there an easy way to shut down the acceptor? 117 | // I suppose we'll simply try it and see how it goes 118 | std::lock_guard acceptorLock {this->acceptor_mutex}; 119 | 120 | for(std::vector>::iterator it = this->acceptors.begin(); it != this->acceptors.end(); ) 121 | { 122 | TCPAcceptor const* curAcceptor = &(*it->get()); 123 | unsigned int curAcceptorID = static_cast(*curAcceptor); 124 | if(acceptorID == curAcceptorID) 125 | { 126 | it = this->acceptors.erase(it); 127 | } 128 | else 129 | { 130 | ++it; 131 | } 132 | } 133 | } 134 | 135 | unsigned int MasterConnection::createUDPConnection(std::unique_ptr& pPH) 136 | { 137 | std::unique_ptr newConnection = std::make_unique(); 138 | 139 | std::scoped_lock locks {this->udp_mutex, this->proc_mutex}; 140 | 141 | UDPConnection const* pConn = newConnection.get(); 142 | unsigned int connectionID = static_cast(*pConn); 143 | this->udpConnections.push_back(std::move(newConnection)); 144 | 145 | std::unique_ptr ph = std::move(pPH); 146 | this->processHandlers.emplace(connectionID, std::move(ph)); 147 | 148 | return 0; 149 | } 150 | 151 | std::vector MasterConnection::getUDPConnections(void) const 152 | { 153 | std::vector result; 154 | 155 | std::lock_guard udp_lock {this->udp_mutex}; 156 | 157 | for(std::unique_ptr const& curConn: this->udpConnections) 158 | { 159 | UDPConnection const* pCurConn = curConn.get(); 160 | result.push_back(pCurConn); 161 | } 162 | 163 | return result; 164 | } 165 | 166 | void MasterConnection::removeUDPConnection(unsigned int connID) 167 | { 168 | std::scoped_lock locks {this->udp_mutex, this->proc_mutex}; 169 | 170 | // remove procedures firts 171 | for(std::map>::iterator it = this->processHandlers.begin(); it != this->processHandlers.end(); ) 172 | { 173 | std::pair const&> curPair = *it; 174 | unsigned const procConnID = curPair.first; 175 | if(connID == procConnID) 176 | { 177 | it = this->processHandlers.erase(it); 178 | } 179 | else 180 | { 181 | ++it; 182 | } 183 | } 184 | 185 | // Remove UDPConnection 186 | for(std::vector>::iterator it = this->udpConnections.begin(); it != this->udpConnections.end(); ) 187 | { 188 | std::unique_ptr const& curConn = *it; 189 | unsigned const curConnID = static_cast(*curConn); 190 | if(curConnID == connID) 191 | { 192 | it = this->udpConnections.erase(it); 193 | } 194 | else 195 | { 196 | ++it; 197 | } 198 | } 199 | } 200 | 201 | void MasterConnection::stopListening(void) 202 | { 203 | std::lock_guard listenThreadLock {this->listeningThread_mutex}; 204 | if(std::this_thread::get_id() == this->listeningThread.get_id()) 205 | { 206 | this->setListeningState(false); 207 | } 208 | else 209 | { 210 | this->setListeningState(false); 211 | this->listeningThread.join(); 212 | } 213 | } 214 | 215 | void MasterConnection::setListeningState(bool state) 216 | { 217 | std::lock_guard lock {this->listening_mutex}; 218 | this->listening = state; 219 | } 220 | 221 | void MasterConnection::beginListening() 222 | { 223 | //Check for a new connection every 5 seconds 224 | while(this->isListening()) 225 | { 226 | this->checkAndProcessConnections(); 227 | } 228 | } 229 | 230 | void MasterConnection::startListening() 231 | { 232 | if(this->isListening() == true) return; 233 | this->setListeningState(true); 234 | 235 | std::lock_guard lock {this->listeningThread_mutex}; 236 | this->listeningThread = std::thread([=]{this->beginListening();}); 237 | } 238 | 239 | void MasterConnection::checkAndProcessConnections() 240 | { 241 | fd_set fdSet; 242 | unsigned int nConnections = 0; 243 | 244 | // Only continue if there are connections to check 245 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - getting number of connections"; 246 | nConnections = this->getNumConnections(); 247 | 248 | if(nConnections < 1) 249 | { 250 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - No connections to check"; 251 | return; 252 | } 253 | 254 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - loading fd_set connections"; 255 | this->loadFdSetConnections(fdSet); 256 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - calling select..."; 257 | int connectionsWaiting = this->waitForFdSetConnections(fdSet); 258 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - select finished"; 259 | 260 | // Only continue if there are connections waiting 261 | if(connectionsWaiting < 1) 262 | { 263 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - no connections waiting with data"; 264 | return; 265 | } 266 | 267 | // TCPAcceptors 268 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - checking and processing TCP Connections"; 269 | this->checkAndProcessTCPConnections(fdSet); 270 | 271 | // UDPConnections 272 | //LOG(DEBUG) << "MasterConnection::checkAndProcessConnections - checking and processing UDP Connections"; 273 | this->checkAndProcessUDPConnections(fdSet); 274 | 275 | return; 276 | } 277 | 278 | bool MasterConnection::loadFdSetConnections(fd_set& fdSet) const 279 | { 280 | // Clear the set 281 | FD_ZERO(&fdSet); 282 | 283 | // Add all sockets to the set 284 | this->loadFdSetTCPConnections(fdSet); 285 | this->loadFdSetUDPConnections(fdSet); 286 | 287 | return true; 288 | } 289 | 290 | bool MasterConnection::loadFdSetTCPConnections(fd_set& fdSet) const 291 | { 292 | std::lock_guard acceptorLock {this->acceptor_mutex}; 293 | for(std::unique_ptr const& acceptor : this->acceptors) 294 | { 295 | acceptor->loadFdSetConnections(fdSet); 296 | } 297 | 298 | return true; 299 | } 300 | 301 | bool MasterConnection::loadFdSetUDPConnections(fd_set& fdSet) const 302 | { 303 | std::lock_guard udpConnectionLock {this->udp_mutex}; 304 | for(std::unique_ptr const& udpConnection : this->udpConnections) 305 | { 306 | int fd = static_cast(*udpConnection); 307 | FD_SET(fd, &fdSet); 308 | } 309 | return true; 310 | } 311 | 312 | int MasterConnection::waitForFdSetConnections(fd_set& fdSet) const 313 | { 314 | struct timeval tv; 315 | int largestFD = this->getLargestSocket(); 316 | 317 | // Set timeout 318 | int seconds = static_cast(floor(this->timeout)); 319 | double remainder = timeout - seconds; 320 | double remainder_us = remainder * 1e6; 321 | int microseconds = static_cast(floor(remainder_us)); 322 | 323 | tv.tv_sec = seconds; 324 | tv.tv_usec = microseconds; 325 | 326 | int retval = ::select(largestFD+1, &fdSet, nullptr, nullptr, &tv); 327 | 328 | if(retval == -1) { 329 | throw std::logic_error(std::string("MasterConnection::waitForFdSetConnections - failed to select! ERR CODE: ") + std::to_string(ERRORCODE)); 330 | } 331 | 332 | return retval; 333 | } 334 | 335 | void MasterConnection::checkAndProcessTCPConnections(fd_set& fdSet) 336 | { 337 | std::lock_guard acceptorLock {this->acceptor_mutex}; 338 | for(std::unique_ptr const& acceptor : this->acceptors) 339 | { 340 | acceptor->checkAndProcessConnections(fdSet); 341 | } 342 | return; 343 | } 344 | 345 | void MasterConnection::checkAndProcessUDPConnections(fd_set& fdSet) 346 | { 347 | { 348 | std::scoped_lock locks {this->udp_mutex, this->proc_mutex}; 349 | for(std::vector>::iterator it = this->udpConnections.begin(); it != this->udpConnections.end(); ) 350 | { 351 | std::unique_ptr const& udpConnection = *it; 352 | unsigned fd = static_cast(*udpConnection); 353 | if(FD_ISSET(fd, &fdSet) != false) 354 | { 355 | std::unique_ptr const& curProcHandler = this->processHandlers.at(fd); 356 | bool keepConnection = (*curProcHandler)(*udpConnection); 357 | if(!keepConnection) 358 | { 359 | it = this->udpConnections.erase(it); 360 | } 361 | else 362 | { 363 | ++it; 364 | } 365 | } 366 | } 367 | } 368 | return; 369 | } 370 | 371 | int MasterConnection::getLargestSocket(void) const 372 | { 373 | int result = -1; 374 | int largestTCPSocket = this->getLargestTCPSocket(); 375 | 376 | if(largestTCPSocket > result) 377 | { 378 | result = largestTCPSocket; 379 | } 380 | 381 | int largestUDPSocket = this->getLargestUDPSocket(); 382 | 383 | if(largestUDPSocket > result) 384 | { 385 | result = largestUDPSocket; 386 | } 387 | 388 | return result; 389 | } 390 | 391 | int MasterConnection::getLargestTCPSocket(void) const 392 | { 393 | int result = -1; 394 | 395 | std::lock_guard acceptorLock {this->acceptor_mutex}; 396 | for(std::vector>::const_iterator it = this->acceptors.begin(); it != this->acceptors.end(); it++) 397 | { 398 | TCPAcceptor const* curAcceptor = it->get(); 399 | int curAcceptorLargestSocket = curAcceptor->getLargestSocket(); 400 | if(curAcceptorLargestSocket > result) 401 | { 402 | result = curAcceptorLargestSocket; 403 | } 404 | } 405 | 406 | return result; 407 | } 408 | 409 | int MasterConnection::getLargestUDPSocket(void) const 410 | { 411 | int result = -1; 412 | 413 | std::lock_guard udpConnLock {this->udp_mutex}; 414 | for(std::vector>::const_iterator it = this->udpConnections.begin(); it != this->udpConnections.end(); it++) 415 | { 416 | UDPConnection const* curConn = it->get(); 417 | int curConnSocket = *curConn; 418 | if(curConnSocket > result) 419 | { 420 | result = curConnSocket; 421 | } 422 | } 423 | 424 | return result; 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /src/inet/ServiceAddress.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "inet/config.hpp" 3 | 4 | #include 5 | #include 6 | #include "inet/Socket.hpp" 7 | #include "inet/ServiceAddress.hpp" 8 | 9 | #ifdef HAVE_ARPA_INET_H 10 | #include "arpa/inet.h" 11 | #endif /* HAVE_ARPA_INET_H */ 12 | #ifdef HAVE_WINSOCK2_H 13 | #include 14 | #ifdef HAVE_WS2TCPIP_H 15 | #include 16 | #endif /* HAVE_WS2TCPIP_H */ 17 | #endif /* HAVE_WINSOCK2_H */ 18 | 19 | namespace inet 20 | { 21 | ServiceAddress::ServiceAddress(void) 22 | { 23 | this->addr.sin_family = AF_INET; 24 | } 25 | 26 | ServiceAddress::ServiceAddress(std::string const& AddressString) 27 | { 28 | this->addr.sin_family = AF_INET; 29 | this->setAddressString(AddressString); 30 | } 31 | 32 | ServiceAddress::ServiceAddress(sockaddr_in const& captureAddr) 33 | { 34 | this->addr = std::move(captureAddr); 35 | } 36 | 37 | std::string const ServiceAddress::getAddressString(void) const 38 | { 39 | return this->getIPAddressString() + ":" + this->getPortString(); 40 | } 41 | 42 | std::string const ServiceAddress::getIPAddressString(void) const 43 | { 44 | std::lock_guard lock(this->addr_mutex); 45 | char* result = ::inet_ntoa(this->addr.sin_addr); 46 | return std::string(result); 47 | } 48 | 49 | std::string const ServiceAddress::getPortString(void) const 50 | { 51 | // no need to lock the addr_mutex since this function 52 | // doesn't directly access addr 53 | return std::to_string(this->getPort()); 54 | } 55 | 56 | unsigned int ServiceAddress::getPort(void) const 57 | { 58 | std::lock_guard lock(this->addr_mutex); 59 | return ntohs(this->addr.sin_port); 60 | } 61 | 62 | void ServiceAddress::setAddressString(std::string const& address) 63 | { 64 | std::vector const ipAndPort = getIPandPort(address); 65 | std::string const IPAddressString = ipAndPort.at(0); 66 | std::string const portString = ipAndPort.at(1); 67 | 68 | // Set the IP Address 69 | this->setIPAddressString(IPAddressString); 70 | 71 | // Set the port 72 | this->setPortString(portString); 73 | } 74 | 75 | void ServiceAddress::setIPAddressString(std::string const& IPAddress) 76 | { 77 | std::lock_guard lock(this->addr_mutex); 78 | int result = INET_ATON(IPAddress, &this->addr.sin_addr); 79 | if(result == ATON_ERROR) 80 | { 81 | throw std::out_of_range(IPAddress + std::string(" is an invalid IP Address. error: ") + std::to_string(ERRORCODE)); 82 | } 83 | //#ifdef HAVE_ARPA_INET_H 84 | //int result = ::inet_aton(IPAddress.data(), &this->addr.sin_addr); 85 | //#endif [> HAVE_ARPA_INET_H <] 86 | //#ifdef HAVE_WS2TCPIP_H 87 | //std::wstring wIPAddress = std::wstring(IPAddress.begin(), IPAddress.end()); 88 | //int result = InetPtonW(AF_INET, wIPAddress.c_str(), &this->addr.sin_addr); 89 | //if (result == -1) 90 | //{ 91 | //throw std::logic_error(IPAddress + std::string(" error: ") + std::to_string(ERRORCODE)); 92 | //} 93 | //#endif [> HAVE_ARPA_INET_H <] 94 | //if(result == 0) 95 | //{ 96 | //throw std::out_of_range(IPAddress + std::string(" is an invalid IP Address")); 97 | //} 98 | } 99 | 100 | void ServiceAddress::setPortString(std::string const& port) 101 | { 102 | this->setPort(std::stoi(port)); 103 | } 104 | 105 | void ServiceAddress::setPort(int port) 106 | { 107 | std::lock_guard lock(this->addr_mutex); 108 | this->addr.sin_port = htons(port); 109 | } 110 | 111 | ServiceAddress::operator sockaddr const* () const 112 | { 113 | return this->getAddr(); 114 | } 115 | 116 | ServiceAddress::operator sockaddr * () 117 | { 118 | std::lock_guard lock {this->addr_mutex}; 119 | return (sockaddr*)&this->addr; 120 | } 121 | 122 | sockaddr const* ServiceAddress::getAddr(void) const 123 | { 124 | std::lock_guard lock {this->addr_mutex}; 125 | return (sockaddr const*)&this->addr; 126 | } 127 | 128 | std::vector const ServiceAddress::getIPandPort(std::string const AddressString) 129 | { 130 | std::string::size_type const colonPosition = AddressString.find(":"); 131 | if(colonPosition == std::string::npos) 132 | { 133 | throw std::out_of_range("ServiceAddress::getIPandPort " + AddressString + " is a bad address"); 134 | } 135 | std::string const IPAddress = AddressString.substr(0, colonPosition); 136 | std::string const port = AddressString.substr(colonPosition+1); 137 | std::vector const result {IPAddress, port}; 138 | return result; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/inet/Socket.cpp: -------------------------------------------------------------------------------- 1 | #include "inet/config.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #ifdef HAVE_UNISTD_H 7 | #include 8 | #endif /* HAVE_UNISTD_H */ 9 | #ifdef HAVE_ERRNO_H 10 | #include 11 | #endif /* HAVE_ERRNO_H */ 12 | #ifdef HAVE_SOCKET_H 13 | #include 14 | #endif /* HAVE_SOCKET_H */ 15 | #ifdef HAVE_WINSOCK2_H 16 | #include 17 | #endif /* HAVE_WINSOCK2_H */ 18 | 19 | 20 | #include "inet/Socket.hpp" 21 | 22 | namespace inet 23 | { 24 | Socket::Socket(int f, int t, int p) : family(f), type(t), protocol(p) 25 | { 26 | if(Socket::n_sockets < 1) this->startup(); 27 | 28 | this->socket = ::socket(f, t, p); 29 | 30 | if(this->socket == INVALID_SOCKET) 31 | { 32 | int const err = ERRORCODE; 33 | throw std::out_of_range(std::string("Socket::Socket() Error opening socket: ") + (std::to_string(err))); 34 | } 35 | Socket::n_sockets++; 36 | } 37 | 38 | Socket::Socket(int capture, int f, int t, int p) : socket(capture), family(f), type(t), protocol(p) 39 | { 40 | if(Socket::n_sockets < 1) this->startup(); 41 | Socket::n_sockets++; 42 | } 43 | 44 | Socket::~Socket(void) 45 | { 46 | this->close(); 47 | Socket::n_sockets--; 48 | if(Socket::n_sockets < 1) this->shutdown(); 49 | } 50 | 51 | void Socket::listen(void) 52 | { 53 | // If socket is SOCK_DGRAM (UDP) simply bind 54 | int result = ::listen(this->socket, SOMAXCONN); 55 | if(result == SOCKET_ERROR) 56 | { 57 | throw std::logic_error(std::string("Socket::listen failed: ") + std::to_string(ERRORCODE)); 58 | } 59 | } 60 | 61 | Socket::operator int() const 62 | { 63 | return this->socket; 64 | } 65 | 66 | int Socket::close(void) 67 | { 68 | int result = _CLOSE(this->socket); 69 | if(result == SOCKET_ERROR) 70 | { 71 | int const err = ERRORCODE; 72 | std::cout << std::string("Socket::~Socket() Error closing socket: ") + std::to_string(err) << std::endl; 73 | return -1; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | void Socket::startup(void) 80 | { 81 | #ifdef HAVE_WINSOCK2_H 82 | WORD wVersion = MAKEWORD(2, 2); 83 | WSADATA wsaData; 84 | 85 | int err = WSAStartup(wVersion, &wsaData); 86 | if(err != 0) 87 | { 88 | throw std::logic_error(std::string("Socket::Failed to initialize WSA: ") + std::to_string(err)); 89 | } 90 | #endif /* HAVE_WINSOCK2_H */ 91 | return; 92 | } 93 | 94 | void Socket::shutdown(void) 95 | { 96 | #ifdef HAVE_WINSOCK2_H 97 | WSACleanup(); 98 | #endif /* HAVE_WINSOCK2_H */ 99 | } 100 | int Socket::n_sockets {0}; 101 | } 102 | -------------------------------------------------------------------------------- /src/inet/TCPAcceptor.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | namespace inet 7 | { 8 | TCPAcceptor::TCPAcceptor(AcceptHandler const& AcceptHandler, ProcessHandler const& ConnectionHandler) : acceptHandler(AcceptHandler), connectionHandler(ConnectionHandler) { } 9 | 10 | int TCPAcceptor::getLargestSocket(void) const 11 | { 12 | int result = *this; 13 | std::vector connections = this->getConnections(); 14 | 15 | for(TCPConnection const* curConn : connections) 16 | { 17 | int curConnSocket = *curConn; 18 | if(curConnSocket > result) 19 | { 20 | result = curConnSocket; 21 | } 22 | } 23 | 24 | return result; 25 | } 26 | 27 | std::vector TCPAcceptor::getConnections(void) const 28 | { 29 | std::vector result; 30 | std::lock_guard lock {this->child_mutex}; 31 | 32 | for(std::unique_ptr const& curConn : this->childConnections) 33 | { 34 | TCPConnection* pCurConn = &(*curConn); 35 | result.push_back(pCurConn); 36 | } 37 | 38 | return result; 39 | } 40 | 41 | void TCPAcceptor::removeConnection(int connectionSocket) 42 | { 43 | std::lock_guard childLock {this->child_mutex}; 44 | for(std::vector>::iterator it = this->childConnections.begin(); it != this->childConnections.end(); ) 45 | { 46 | std::unique_ptr const& curConnection = *it; 47 | int curConnSocket = static_cast(*curConnection); 48 | if(curConnSocket == connectionSocket) 49 | { 50 | it = this->childConnections.erase(it); 51 | } 52 | else 53 | { 54 | it++; 55 | } 56 | } 57 | } 58 | 59 | TCPConnection const& TCPAcceptor::accept(void) 60 | { 61 | sockaddr_in peerAddr; 62 | SOCKLEN addrSize = sizeof(sockaddr_in); 63 | this->listen(); 64 | int capturedSocket = ::accept(static_cast(*this), reinterpret_cast(&peerAddr), &addrSize); 65 | 66 | if(capturedSocket <= -1) 67 | { 68 | throw std::out_of_range(std::string("TCPAcceptor::accept - Failed to accept connection ") + std::to_string(ERRORCODE)); 69 | } 70 | 71 | //std::cout << "about to make a new TCPConnection from new socket..." << std::endl; 72 | std::unique_ptr pNewConn = std::make_unique(capturedSocket, *this, peerAddr); 73 | 74 | std::lock_guard acceptLock {this->acceptHandler_mutex}; 75 | bool accepted = this->acceptHandler(*pNewConn); 76 | if(accepted) 77 | { 78 | std::lock_guard childLock {this->child_mutex}; 79 | this->childConnections.push_back(std::move(pNewConn)); 80 | return *this->childConnections.at(this->childConnections.size()-1); 81 | } 82 | 83 | return *pNewConn; 84 | } 85 | 86 | void TCPAcceptor::loadFdSetConnections(fd_set& fdSet) 87 | { 88 | // Self first 89 | //std::cout << "Server: loading fd " << static_cast(*this) << std::endl; 90 | FD_SET(static_cast(*this), &fdSet); 91 | 92 | // Now child connections 93 | std::lock_guard childLock {this->child_mutex}; 94 | for(std::unique_ptr const& conn : this->childConnections) 95 | { 96 | FD_SET(static_cast(*conn), &fdSet); 97 | } 98 | return; 99 | } 100 | 101 | void TCPAcceptor::checkAndProcessConnections(fd_set const& fdSet) 102 | { 103 | // Select must have been called previously 104 | 105 | // Check and Process Self 106 | //std::cout << "Server: checking fd " << static_cast(*this) << std::endl; 107 | if(FD_ISSET(static_cast(*this), &fdSet) != false) 108 | { 109 | bool accepted = false; 110 | TCPConnection const& newConnection = this->accept(); 111 | 112 | std::lock_guard acceptLock{this->acceptHandler_mutex}; 113 | { 114 | accepted = this->acceptHandler(newConnection); 115 | 116 | // TODO: false acceptions should remove the connection 117 | } 118 | } 119 | 120 | // Check and Process Children 121 | std::lock_guard childLock {this->child_mutex}; 122 | for(std::unique_ptr const& conn : this->childConnections) 123 | { 124 | if(FD_ISSET(static_cast(*conn), &fdSet) != false) 125 | { 126 | std::lock_guard procLock {this->connectionHandler_mutex}; 127 | { 128 | bool keepConnection = this->connectionHandler(*conn); 129 | } 130 | } 131 | } 132 | return; 133 | } 134 | 135 | TCPAcceptor::AcceptHandler const TCPAcceptor::getAcceptHandler(void) const 136 | { 137 | return this->acceptHandler; 138 | } 139 | 140 | TCPAcceptor::ProcessHandler const TCPAcceptor::getConnectionHandler(void) const 141 | { 142 | return this->connectionHandler; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/inet/TCPConnection.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "inet/TCPConnection.hpp" 3 | 4 | namespace inet 5 | { 6 | TCPConnection::TCPConnection(void) : IPConnection(SOCK_STREAM, 0) {} 7 | 8 | TCPConnection::TCPConnection(int captureRawSocket, IPConnection const& parentConnection, sockaddr_in& destAddr) : IPConnection(captureRawSocket, SOCK_STREAM, 0, parentConnection, destAddr) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/inet/UDPConnection.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "inet/config.hpp" 3 | 4 | #include 5 | 6 | #ifdef HAVE_SOCKET_H 7 | #include "sys/socket.h" 8 | #endif /* HAVE_SOCKET_H */ 9 | #ifdef HAVE_WINSOCK2_H 10 | #include 11 | #endif /* HAVE_WINSOCK2_H */ 12 | 13 | #include "inet/UDPConnection.hpp" 14 | 15 | namespace inet 16 | { 17 | UDPConnection::UDPConnection() : IPConnection(SOCK_DGRAM, 0) {} 18 | 19 | int UDPConnection::sendTo(char const* data, int const data_len, ServiceAddress const& addr) const 20 | { 21 | long result = ::sendto(*this, data, data_len, 0, addr, sizeof(sockaddr_in)); 22 | return static_cast(result); 23 | } 24 | 25 | int UDPConnection::recvFrom(char* buffer, int const buffer_len, ServiceAddress& addr) const 26 | { 27 | SOCKLEN addr_len = sizeof(sockaddr); 28 | long result = ::recvfrom(*this, buffer, buffer_len, 0, addr, &addr_len); 29 | return static_cast(result); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "g3log/g3log.hpp" 4 | #include "g3log/logworker.hpp" 5 | #include "DPServer.hpp" 6 | #include "CustomSink.hpp" 7 | 8 | int main(int argc, char const* argv[]) 9 | { 10 | 11 | // Initialize our logger 12 | std::unique_ptr logWorker{g3::LogWorker::createLogWorker()}; 13 | logWorker->addSink(std::make_unique(), &CustomSink::ReceiveLogMessage); 14 | g3::initializeLogging(logWorker.get()); 15 | 16 | 17 | //std::unique_ptr app = std::unique_ptr(new DPServer(argc, argv)); 18 | //app->start(); 19 | 20 | 21 | LOG(DEBUG) << "Hello, World!"; 22 | //std::cout << "Hello, world!" << std::endl; 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TEST_PROJECT_NAME "${PROJECT_NAME}Test") 2 | 3 | # GTEST Dependency 4 | configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) 5 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 6 | RESULT_VARIABLE result 7 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) 8 | if(result) 9 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 10 | endif() 11 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 12 | RESULT_VARIABLE result 13 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) 14 | if(result) 15 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 16 | endif() 17 | 18 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 19 | 20 | # Add googletest directly to our build. This defines 21 | # the gtest and gtest_main targets. 22 | add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src 23 | ${CMAKE_CURRENT_BINARY_DIR}/googletest-build 24 | EXCLUDE_FROM_ALL) 25 | 26 | # Make the test executable 27 | add_executable("${TEST_PROJECT_NAME}" 28 | ../src/DPServer.cpp 29 | ../src/ArgParser.cpp 30 | 31 | ./src/main_test.cpp 32 | ./src/DPServer_test.cpp 33 | ./src/ArgParser_test.cpp 34 | ./src/inet/Socket_test.cpp 35 | ./src/inet/ServiceAddress_test.cpp 36 | ./src/inet/TCPConnection_test.cpp 37 | ./src/inet/UDPConnection_test.cpp 38 | ./src/inet/TCPAcceptor_test.cpp 39 | ./src/inet/MasterConnection_test.cpp 40 | ) 41 | 42 | # Includes 43 | target_include_directories("${TEST_PROJECT_NAME}" BEFORE 44 | PUBLIC 45 | ../include 46 | ./include 47 | "${gtest_SOURCE_DIR}/include" 48 | "${install_dir}/include" 49 | ) 50 | 51 | # Link dependencies 52 | target_link_libraries("${TEST_PROJECT_NAME}" 53 | PUBLIC 54 | g3log 55 | "${ws2_32}" 56 | inet 57 | gtest_main) 58 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG master 9 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /tests/include/inet/TCPAcceptor_test.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INET_TCP_ACCEPTOR_TEST 3 | #define INET_TCP_ACCEPTOR_TEST 4 | 5 | #include 6 | 7 | std::unique_ptr startTestServer(std::string& serviceAddress, std::string& status, std::mutex& statusMutex, std::condition_variable& statusCV); 8 | std::unique_ptr startTestClient(std::string& serviceAddress, std::string& status, std::mutex& statusMutex, std::condition_variable& statusCV); 9 | 10 | #endif /* INET_TCP_ACCEPTOR_TEST */ 11 | -------------------------------------------------------------------------------- /tests/src/ArgParser_test.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "ArgParser.hpp" 3 | 4 | TEST(ArgParserTest, vectorize) 5 | { 6 | char const* argumentString[] = { 7 | "--server", 8 | "--client", 9 | "192.168.1.1", 10 | "--port", 11 | "80", 12 | "--name", 13 | "dingus" 14 | }; 15 | std::vector result = ArgParser::vectorize(7, argumentString); 16 | 17 | ASSERT_EQ(static_cast(result.size()), 7); 18 | 19 | EXPECT_EQ(result[0], "--server"); 20 | EXPECT_EQ(result[1], "--client"); 21 | EXPECT_EQ(result[2], "192.168.1.1"); 22 | EXPECT_EQ(result[3], "--port"); 23 | EXPECT_EQ(result[4], "80"); 24 | EXPECT_EQ(result[5], "--name"); 25 | EXPECT_EQ(result[6], "dingus"); 26 | } 27 | -------------------------------------------------------------------------------- /tests/src/DPServer_test.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "DPServer.hpp" 3 | 4 | TEST(DPServerTest, setServer) 5 | { 6 | int argc = 2; 7 | char const* argv[] = { 8 | "DPServer", 9 | "-s" 10 | }; 11 | DPServer dps{argc, argv}; 12 | dps.processArgs(); 13 | EXPECT_EQ(dps.getAppState(), DPServer::SERVER); 14 | EXPECT_EQ(dps.getHostIPAddress(), std::string("")); 15 | EXPECT_EQ(dps.getConnPort(), 0); 16 | } 17 | 18 | TEST(DPServerTest, setClient) 19 | { 20 | int argc = 3; 21 | char const* argv[] = { 22 | "DPServer", 23 | "--client", 24 | "192.168.1.1" 25 | }; 26 | 27 | DPServer dps(argc, argv); 28 | dps.processArgs(); 29 | EXPECT_EQ(dps.getAppState(), DPServer::CLIENT); 30 | EXPECT_EQ(dps.getHostIPAddress(), std::string("192.168.1.1")); 31 | EXPECT_EQ(dps.getConnPort(), 0); 32 | } 33 | 34 | TEST(DPServerTest, setClientNoHost) 35 | { 36 | int argc = 2; 37 | char const* argv[] = { 38 | "DPServer", 39 | "-c" 40 | }; 41 | 42 | DPServer dps(argc, argv); 43 | dps.processArgs(); 44 | EXPECT_EQ(dps.getAppState(), DPServer::NOT_SET); 45 | EXPECT_EQ(dps.getHostIPAddress(), std::string("")); 46 | EXPECT_EQ(dps.getConnPort(), 0); 47 | } 48 | 49 | TEST(DPServerTest, setServerAndClient) 50 | { 51 | int argc = 3; 52 | char const* argv[] = { 53 | "DPServer", 54 | "--server", 55 | "--client" 56 | }; 57 | 58 | DPServer dps(argc, argv); 59 | dps.processArgs(); 60 | EXPECT_EQ(dps.getAppState(), DPServer::NOT_SET); 61 | EXPECT_EQ(dps.getHostIPAddress(), ""); 62 | EXPECT_EQ(dps.getConnPort(), 0); 63 | } 64 | 65 | TEST(DPServerTest, setPort) 66 | { 67 | int argc = 5; 68 | char const* argv[] = { 69 | "DPServer", 70 | "--client", 71 | "192.168.8.8", 72 | "--port", 73 | "2300" 74 | }; 75 | 76 | DPServer dps(argc, argv); 77 | dps.processArgs(); 78 | EXPECT_EQ(dps.getAppState(), DPServer::CLIENT); 79 | EXPECT_EQ(dps.getHostIPAddress(), "192.168.8.8"); 80 | EXPECT_EQ(dps.getConnPort(), 2300); 81 | } 82 | -------------------------------------------------------------------------------- /tests/src/inet/MasterConnection_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "inet/MasterConnection.hpp" 7 | #include 8 | #include "gtest/gtest.h" 9 | 10 | TEST(MasterConnectionTest, constructor) 11 | { 12 | // 1. MasterConnection should not throw any exceptions when constructing 13 | ASSERT_NO_THROW({ 14 | inet::MasterConnection mc; 15 | }); 16 | 17 | inet::MasterConnection mc; 18 | 19 | // 2. MasterConnection should begin running 20 | ASSERT_EQ(mc.isListening(), true); 21 | } 22 | 23 | TEST(MasterConnectionTest, getNumConnections) 24 | { 25 | inet::MasterConnection mc; 26 | 27 | ASSERT_EQ(mc.getNumConnections(), static_cast(0)); 28 | } 29 | 30 | TEST(MasterConnectionTest, createAndRemoveAcceptorTCP) 31 | { 32 | inet::MasterConnection mc(0.5); 33 | 34 | inet::TCPAcceptor::AcceptHandler ah = [](inet::TCPConnection const& conn) -> bool { 35 | if(conn) 36 | return true; 37 | return true; 38 | }; 39 | 40 | inet::TCPAcceptor::ProcessHandler ph = [](inet::TCPConnection const& conn) -> bool { 41 | if(conn) 42 | return true; 43 | return true; 44 | }; 45 | 46 | mc.createTCPAcceptor(ah, ph); 47 | 48 | unsigned numAcceptors = mc.getNumTCPAcceptors(); 49 | 50 | ASSERT_EQ(numAcceptors, 1u); 51 | 52 | std::vector acceptors = mc.getAcceptors(); 53 | inet::TCPAcceptor const* acceptor = acceptors[0]; 54 | int acceptorID = static_cast(*acceptor); 55 | 56 | mc.removeTCPAcceptor(acceptorID); 57 | 58 | ASSERT_EQ(mc.getNumTCPAcceptors(), 0u); 59 | } 60 | 61 | TEST(MasterConnectionTest, createAndRemoveUDPConnection) 62 | { 63 | inet::MasterConnection mc(0.5); 64 | 65 | std::unique_ptr ph = std::make_unique([](inet::IPConnection const& conn)->bool{return true;}); 66 | 67 | mc.createUDPConnection(ph); 68 | 69 | //ASSERT_EQ(mc.getNumUDPConnections(), 1u); 70 | 71 | std::vector udpConnections = mc.getUDPConnections(); 72 | //inet::UDPConnection const* curUDPConn = udpConnections[0]; 73 | //unsigned int const curConnID = static_cast(*curUDPConn); 74 | 75 | //mc.removeUDPConnection(curConnID); 76 | 77 | //ASSERT_EQ(mc.getNumUDPConnections(), 0u); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /tests/src/inet/ServiceAddress_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "inet/config.hpp" 3 | 4 | #include 5 | 6 | #ifdef HAVE_ARPA_INET_H 7 | #include "arpa/inet.h" 8 | #endif /* HAVE_ARPA_INET_H */ 9 | #ifdef HAVE_WINSOCK2_H 10 | #include 11 | #ifdef HAVE_WS2TCPIP_H 12 | #include 13 | #endif /* HAVE_WS2TCPIP_H */ 14 | #endif /* HAVE_WINSOCK2_H */ 15 | 16 | #include "inet/ServiceAddress.hpp" 17 | #include "inet/Socket.hpp" 18 | #include "gtest/gtest.h" 19 | 20 | TEST(ServiceAddressTest, constructor) 21 | { 22 | // Good blank construction 23 | ASSERT_NO_THROW({ 24 | inet::ServiceAddress addr {}; 25 | }); 26 | 27 | // Good construction 28 | ASSERT_NO_THROW({ 29 | inet::ServiceAddress addr{"192.168.1.100:80"}; 30 | }); 31 | 32 | // Bad Construction 33 | ASSERT_THROW({ 34 | inet::ServiceAddress addr{"18293.2"}; 35 | }, std::out_of_range); 36 | 37 | // Capture Construction 38 | sockaddr_in addr = {}; 39 | addr.sin_family = AF_INET; 40 | std::string ipAddress = "10.0.0.2"; 41 | unsigned int port = 47624; 42 | #ifdef HAVE_ARPA_INET_H 43 | int inet_aton_result = ::inet_aton(ipAddress.data(), &addr.sin_addr); 44 | #endif /* HAVE_ARPA_INET_H */ 45 | #ifdef HAVE_WS2TCPIP_H 46 | std::wstring wIPAddress = std::wstring(ipAddress.begin(), ipAddress.end()); 47 | int inet_aton_result = InetPtonW(AF_INET, wIPAddress.c_str(), &addr.sin_addr); 48 | #endif /* HAVE_WS2TCPIP_H */ 49 | addr.sin_port = htons(port); 50 | 51 | ASSERT_NE(inet_aton_result, 0); 52 | 53 | inet::ServiceAddress saddr (addr); 54 | EXPECT_STREQ(saddr.getAddressString().data(), "10.0.0.2:47624"); 55 | } 56 | 57 | TEST(ServiceAddressTest, getAddressString) 58 | { 59 | inet::ServiceAddress addr {"10.0.0.1:8080"}; 60 | ASSERT_STREQ(addr.getAddressString().data(), "10.0.0.1:8080"); 61 | } 62 | 63 | TEST(ServiceAddressTest, getIPAddressString) 64 | { 65 | inet::ServiceAddress addr {"0.0.0.0:25"}; 66 | ASSERT_STREQ(addr.getIPAddressString().data(), "0.0.0.0"); 67 | } 68 | 69 | TEST(ServiceAddressTest, getPortString) 70 | { 71 | inet::ServiceAddress addr {"127.0.0.1:21"}; 72 | ASSERT_STREQ(addr.getPortString().data(), "21"); 73 | ASSERT_EQ(addr.getPort(), static_cast(21)); 74 | } 75 | 76 | TEST(ServiceAddressTest, sets) 77 | { 78 | inet::ServiceAddress addr {"0.0.0.0:0"}; 79 | 80 | addr.setAddressString("192.168.8.150:2300"); 81 | EXPECT_STREQ(addr.getIPAddressString().data(), "192.168.8.150"); 82 | EXPECT_EQ(addr.getPort(), static_cast(2300)); 83 | 84 | addr.setPort(2350); 85 | EXPECT_EQ(addr.getPort(), static_cast(2350)); 86 | 87 | addr.setPortString("2400"); 88 | EXPECT_EQ(addr.getPort(), static_cast(2400)); 89 | 90 | addr.setIPAddressString("127.0.0.1"); 91 | EXPECT_STREQ(addr.getIPAddressString().data(), "127.0.0.1"); 92 | 93 | EXPECT_STREQ(addr.getAddressString().data(), "127.0.0.1:2400"); 94 | } 95 | 96 | TEST(ServiceAddressTest, bind) 97 | { 98 | inet::ServiceAddress addr {"0.0.0.0:0"}; 99 | inet::Socket sock {AF_INET, SOCK_STREAM, 0}; 100 | std::shared_ptr pSock; 101 | 102 | //EXPECT_ANY_THROW({ 103 | //addr.bind(pSock); 104 | //}); 105 | 106 | //pSock = std::shared_ptr(&sock); 107 | //EXPECT_NO_THROW({ 108 | //addr.bind(pSock); 109 | //}); 110 | } 111 | 112 | TEST(ServiceAddressTest, operator_sockaddr_const_ptr) 113 | { 114 | inet::ServiceAddress sa {"192.168.1.100:1234"}; 115 | const sockaddr* addr = sa; 116 | int port = ntohs(((sockaddr_in const*)addr)->sin_port); 117 | EXPECT_EQ(port, 1234); 118 | } 119 | -------------------------------------------------------------------------------- /tests/src/inet/Socket_test.cpp: -------------------------------------------------------------------------------- 1 | #include "inet/config.hpp" 2 | 3 | #ifdef HAVE_SOCKET_H 4 | #include 5 | #endif /* HAVE_SOCKET_H */ 6 | #ifdef HAVE_WINSOCK2_H 7 | #include 8 | #endif /* HAVE_WINSOCK2_H */ 9 | 10 | #include "inet/Socket.hpp" 11 | #include "gtest/gtest.h" 12 | 13 | TEST(SocketTest, constructor) 14 | { 15 | EXPECT_NO_THROW({ 16 | inet::Socket socket(AF_INET, SOCK_STREAM, 0); 17 | }); 18 | 19 | EXPECT_THROW({ 20 | inet::Socket socket(999, 999, 999); 21 | }, std::out_of_range); 22 | } 23 | 24 | TEST(SocketTest, captureConstructor) 25 | { 26 | int sock = ::socket(AF_INET, SOCK_STREAM, 0); 27 | EXPECT_NO_THROW({ 28 | inet::Socket socket (sock, AF_INET, SOCK_STREAM, 0); 29 | }); 30 | } 31 | 32 | // Until we test for bound sockets we shouldn't test for this 33 | //TEST(SocketTest, listen) 34 | //{ 35 | //// This should throw on unbound sockets 36 | //inet::Socket socket(AF_INET, SOCK_STREAM, 0); 37 | //EXPECT_THROW({ 38 | //socket.listen(); 39 | //}); 40 | //} 41 | 42 | TEST(SocketTest, operatorInt) 43 | { 44 | inet::Socket sock {AF_INET, SOCK_STREAM, 0}; 45 | EXPECT_GE(sock, 0); 46 | } 47 | -------------------------------------------------------------------------------- /tests/src/inet/TCPAcceptor_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "gtest/gtest.h" 3 | #include 4 | #include "inet/TCPAcceptor.hpp" 5 | #include "inet/TCPAcceptor_test.hpp" 6 | 7 | // ================================================================== 8 | 9 | std::unique_ptr startTestServer(std::string& serverAddress, std::mutex& serverAddressMutex, std::string& status, std::mutex& statusMutex, std::condition_variable& statusCV) 10 | { 11 | //LOG(DEBUG) << "Server: Starting thread..."; 12 | return std::make_unique([&] 13 | { 14 | bool done = false; 15 | std::mutex done_mutex; 16 | inet::TCPAcceptor::AcceptHandler acceptHandler = [](inet::TCPConnection const& connection)->bool 17 | { 18 | if(connection) 19 | { 20 | //LOG(DEBUG) << "Server: new connection from: " << connection.getAddressString(); 21 | } 22 | return true; 23 | }; 24 | 25 | inet::TCPAcceptor::ProcessHandler processHandler = [&](inet::IPConnection const& connection)->bool 26 | { 27 | if(connection) 28 | { 29 | int const bufsize = 1024*4; 30 | char buffer[bufsize] {}; 31 | int result = connection.recv(buffer, bufsize); 32 | EXPECT_NE(result, -1); 33 | if(result == 0) 34 | { 35 | return false; 36 | } 37 | 38 | if(std::string(buffer) == "Test Data.") 39 | { 40 | std::lock_guard done_lock {done_mutex}; 41 | done = true; 42 | } 43 | //LOG(DEBUG) << "Server: Received data: " << std::string(buffer); 44 | } 45 | return true; 46 | }; 47 | 48 | // Start up the server 49 | //LOG(DEBUG) << "Server: initializting..."; 50 | inet::TCPAcceptor tcpa(acceptHandler, processHandler); 51 | //LOG(DEBUG) << "Server: main listening fd is " << static_cast(tcpa); 52 | tcpa.setAddress("127.0.0.1:0"); 53 | { 54 | std::lock_guard serverAddressLock {serverAddressMutex}; 55 | serverAddress = tcpa.getAddressString(); 56 | tcpa.listen(); 57 | //LOG(DEBUG) << "Server: server started with address as " << serverAddress; 58 | } 59 | 60 | // This proccessor thread is responsible for checking for incoming data 61 | //LOG(DEBUG) << "Server: starting processor thread..."; 62 | std::thread processor([&] 63 | { 64 | // continue while not done 65 | bool isServerDone = false; 66 | while(!isServerDone) 67 | { 68 | fd_set fdSet; 69 | FD_ZERO(&fdSet); 70 | 71 | // load the fd_set with connections 72 | tcpa.loadFdSetConnections(fdSet); 73 | 74 | // call select 75 | int const largestSocket = tcpa.getLargestSocket(); 76 | //std::cout << "Server: largest socket = " << largestSocket << std::endl; 77 | struct timeval tv; 78 | tv.tv_sec = 5; 79 | tv.tv_usec = 0; 80 | int result = ::select(largestSocket + 1, &fdSet, NULL, NULL, &tv); 81 | ASSERT_NE(result, -1); 82 | 83 | // check connections 84 | tcpa.checkAndProcessConnections(fdSet); 85 | 86 | // Check done 87 | { 88 | std::lock_guard done_lock {done_mutex}; 89 | isServerDone = done; 90 | } 91 | } 92 | }); 93 | 94 | processor.join(); 95 | 96 | std::unique_lock statusLock {statusMutex}; 97 | statusCV.wait(statusLock, [&]{return status == "done";}); 98 | 99 | return; 100 | }); 101 | } 102 | 103 | std::unique_ptr startTestClient(std::string& serverAddress, std::mutex& serverAddressMutex, std::string& status, std::mutex& statusMutex, std::condition_variable& statusCV) 104 | { 105 | return std::make_unique([&] 106 | { 107 | // Start the client 108 | bool connected = false; 109 | //LOG(DEBUG) << "Client: initializing client..."; 110 | inet::TCPConnection tcpc{}; 111 | //LOG(DEBUG) << "Client: client fd = " << static_cast(tcpc); 112 | while(!connected) 113 | { 114 | std::lock_guard serverAddressLock {serverAddressMutex}; 115 | //LOG(DEBUG) << "Client: Attempting to connect to " << serverAddress; 116 | int result = tcpc.connect(serverAddress); 117 | if(result == 0) 118 | { 119 | connected = true; 120 | } 121 | } 122 | 123 | // Send Data 124 | std::string sendData = "Test Data."; 125 | int result = tcpc.send(sendData.c_str(), static_cast(sendData.size()+1)); 126 | //LOG(DEBUG) << "Client: test data sent."; 127 | EXPECT_NE(result, -1); 128 | 129 | // Set status 130 | { 131 | std::unique_lock statusLock {statusMutex}; 132 | status = "done"; 133 | } 134 | statusCV.notify_one(); 135 | return; 136 | }); 137 | } 138 | 139 | // ================================================================== 140 | 141 | TEST(TCPAcceptorTest, constructor) 142 | { 143 | ASSERT_NO_THROW({ 144 | // Acceptor 145 | inet::TCPAcceptor::AcceptHandler acceptHandler = [](inet::TCPConnection const& connection)->bool{ 146 | if(connection) return true; 147 | return true; 148 | }; 149 | 150 | // processor 151 | inet::TCPAcceptor::ProcessHandler connectionHandler = [](inet::IPConnection const& connection)->bool{ 152 | if(connection) return true; 153 | return true; 154 | }; 155 | 156 | inet::TCPAcceptor tcpa (acceptHandler, connectionHandler); 157 | }); 158 | } 159 | 160 | TEST(TCPAcceptorTest, getLargestSocket) 161 | { 162 | // Acceptor 163 | inet::TCPAcceptor::AcceptHandler acceptHandler = [](inet::TCPConnection const& connection)->bool{ 164 | if(connection) return true; 165 | return true; 166 | }; 167 | 168 | // processor 169 | inet::TCPAcceptor::ProcessHandler connectionHandler = [](inet::IPConnection const& connection)->bool{ 170 | if(connection) return true; 171 | return true; 172 | }; 173 | 174 | inet::TCPAcceptor tcpa {acceptHandler, connectionHandler}; 175 | int largestSocket = -1; 176 | 177 | largestSocket = tcpa.getLargestSocket(); 178 | 179 | EXPECT_NE(largestSocket, -1); 180 | } 181 | 182 | TEST(TCPAcceptorTest, getConnections) 183 | { 184 | //Acceptor 185 | inet::TCPAcceptor::AcceptHandler acceptHandler = [](inet::TCPConnection const& connection)->bool{ 186 | if(connection) return true; 187 | return true; 188 | }; 189 | 190 | // processor 191 | inet::TCPAcceptor::ProcessHandler connectionHandler = [](inet::IPConnection const& connection)->bool{ 192 | if(connection) return true; 193 | return true; 194 | }; 195 | 196 | //inet::TCPAcceptor tcpa (acceptHandler, connectionHandler); 197 | std::shared_ptr tcpa = std::make_shared(acceptHandler, connectionHandler); 198 | 199 | //std::shared_ptr> pConnections = tcpa->getConnections(); 200 | std::vector const connections = tcpa->getConnections(); 201 | 202 | size_t nConnections = connections.size(); 203 | 204 | ASSERT_EQ(nConnections, static_cast(0)); 205 | } 206 | 207 | TEST(TCPAcceptorTest, accpetProcessAndRemove) 208 | { 209 | unsigned int acceptHandlerCount = 0; 210 | unsigned int processHandlerCount = 0; 211 | std::mutex addressMutex; 212 | std::string serverAddress{}; 213 | std::mutex statusMutex; 214 | std::string status{}; 215 | std::condition_variable statusCV; 216 | std::mutex doneMutex; 217 | bool processDone = false; 218 | 219 | // AcceptHandler 220 | inet::TCPAcceptor::AcceptHandler acceptHandler = [&](inet::TCPConnection const& connection) -> bool 221 | { 222 | if(connection){} 223 | acceptHandlerCount++; 224 | //std::cout << "Server: Connection accepted" << std::endl; 225 | return true; 226 | }; 227 | 228 | // Process Handler Template 229 | inet::TCPAcceptor::ProcessHandler processHandler = [&](inet::TCPConnection const& connection) -> bool 230 | { 231 | int const bufsize = 1024*4; 232 | char buffer[bufsize] {}; 233 | int result = connection.recv(buffer, bufsize); 234 | EXPECT_NE(result, -1); 235 | if(result == 0) 236 | { 237 | //std::cout << "Server: Client connection closed." << std::endl; 238 | return false; 239 | } 240 | std::string data {buffer}; 241 | 242 | if(connection){} 243 | processHandlerCount++; 244 | 245 | //std::cout << "Data: " << data << std::endl; 246 | 247 | data = "Thank you."; 248 | result = connection.send(data.c_str(), static_cast(data.size()+1)); 249 | EXPECT_GE(result, 1); 250 | 251 | return true; 252 | }; 253 | 254 | std::thread server([&]{ 255 | std::unique_lock statusLock {statusMutex, std::defer_lock}; 256 | 257 | // Set up server 258 | //std::cout << "Server: Starting..." << std::endl; 259 | inet::TCPAcceptor tcpa{acceptHandler, processHandler}; 260 | tcpa.setAddress("127.0.0.1:0"); 261 | { 262 | std::lock_guard addressLock{addressMutex}; 263 | serverAddress = tcpa.getAddressString(); 264 | tcpa.listen(); 265 | //std::cout << "Server: set address to " << serverAddress << std::endl; 266 | } 267 | 268 | //std::cout << "Server: Starting acceptor..." << std::endl; 269 | std::thread acceptor([&]{ 270 | bool isServerDone = false; 271 | while(!isServerDone) 272 | { 273 | { 274 | std::lock_guard doneLock {doneMutex}; 275 | isServerDone = processDone; 276 | } 277 | 278 | if(isServerDone) continue; 279 | 280 | //std::cout << "Acceptor: Checking for new connections" << std::endl; 281 | if(tcpa.isDataReady(0.25)) 282 | { 283 | //std::cout << "Acceptor: New connection incoming" << std::endl; 284 | tcpa.accept(); 285 | ASSERT_EQ(acceptHandlerCount, static_cast(1)); 286 | } 287 | } 288 | }); 289 | //std::cout << "Server: acceptor started" << std::endl; 290 | 291 | //std::cout << "Server: starting connection processor..." << std::endl; 292 | std::thread connectionProcessing([&] 293 | { 294 | bool isServerDone = false; 295 | while(!isServerDone) 296 | { 297 | { 298 | std::lock_guard doneLock {doneMutex}; 299 | isServerDone = processDone; 300 | } 301 | 302 | if(isServerDone) continue; 303 | 304 | std::vector connections = tcpa.getConnections(); 305 | for(unsigned long i = 0; i < connections.size(); i++) 306 | { 307 | //std::cout << "i: " << i << std::endl; 308 | inet::TCPConnection const* curConn = connections.at(i); 309 | //std::cout << "Proc: Checking connection for data" << std::endl; 310 | if(curConn->isDataReady(1)) 311 | { 312 | //std::cout << "Proc: connection has data" << std::endl; 313 | bool keepConnection = tcpa.getConnectionHandler()(*curConn); 314 | ASSERT_LE(processHandlerCount, static_cast(1)); 315 | 316 | if(!keepConnection) 317 | { 318 | // Remove Connection 319 | tcpa.removeConnection(*curConn); 320 | ASSERT_EQ(tcpa.getConnections().size(), static_cast(0)); 321 | //std::cout << "Proc: Connection removed" << std::endl; 322 | 323 | if(connections.size() == 1) 324 | { 325 | std::lock_guard doneLock {doneMutex}; 326 | processDone = true; 327 | } 328 | } 329 | } 330 | } 331 | } 332 | }); 333 | //std::cout << "Server: Proc Started" << std::endl; 334 | 335 | statusLock.lock(); 336 | status = "Server Started."; 337 | statusLock.unlock(); 338 | statusCV.notify_one(); 339 | 340 | statusLock.lock(); 341 | statusCV.wait(statusLock, [&]{return status == "Client Complete.";}); 342 | 343 | acceptor.join(); 344 | connectionProcessing.join(); 345 | 346 | }); 347 | 348 | std::thread client([&]{ 349 | ////std::cout << "Client: starting..." << std::endl; 350 | inet::TCPConnection tcp{}; 351 | std::unique_lock statusLock {statusMutex}; 352 | //std::cout << "Client: Waiting for server to start" << std::endl; 353 | statusCV.wait(statusLock, [&]{return status == "Server Started.";}); 354 | statusLock.unlock(); 355 | 356 | { 357 | std::lock_guard addressLock {addressMutex}; 358 | //std::cout << "Client: connecting to " << serverAddress << std::endl; 359 | int result = tcp.connect(serverAddress); 360 | //std::cout << "Client: connected" << std::endl; 361 | EXPECT_EQ(result, 0); 362 | } 363 | 364 | std::string sendData = "Test Data."; 365 | int result = tcp.send(sendData.c_str(), static_cast(sendData.size()+1)); 366 | //std::cout << "sent data" << std::endl; 367 | EXPECT_NE(result, -1); 368 | 369 | const unsigned int bufSize = 255; 370 | char buffer[bufSize]; 371 | 372 | tcp.recv(buffer, bufSize); 373 | std::string data {buffer}; 374 | 375 | ASSERT_STREQ(data.data(), "Thank you."); 376 | 377 | statusLock.lock(); 378 | status = "Client Complete."; 379 | statusLock.unlock(); 380 | statusCV.notify_one(); 381 | }); 382 | 383 | server.join(); 384 | client.join(); 385 | } 386 | 387 | TEST(TCPAcceptorTest, loadFdSetConnections) 388 | { 389 | // Setup handlers 390 | inet::TCPAcceptor::AcceptHandler acceptHandler = [&](inet::TCPConnection const& connection) -> bool {return true;}; 391 | inet::TCPAcceptor::ProcessHandler processHandler = [&](inet::TCPConnection const& connection) -> bool {return true;}; 392 | 393 | inet::TCPAcceptor tcpa(acceptHandler, processHandler); 394 | 395 | fd_set fdSet; 396 | FD_ZERO(&fdSet); 397 | tcpa.loadFdSetConnections(fdSet); 398 | 399 | struct timeval tv; 400 | tv.tv_sec = 0; 401 | tv.tv_usec = 500; 402 | int result = ::select(tcpa.getLargestSocket()+1, &fdSet, NULL, NULL, &tv); 403 | ASSERT_NE(result, -1); 404 | } 405 | 406 | TEST(TCPAcceptorTest, checkAndProcessConnections) 407 | { 408 | std::string status; 409 | std::mutex statusMutex; 410 | std::condition_variable statusCV; 411 | 412 | // Server 413 | std::string serverAddress {"127.0.0.1:0"}; 414 | std::mutex serverAddressMutex; 415 | std::unique_ptr serverThread = startTestServer(serverAddress, serverAddressMutex, status, statusMutex, statusCV); 416 | 417 | // Client 418 | std::unique_ptr clientThread = startTestClient(serverAddress, serverAddressMutex, status, statusMutex, statusCV); 419 | 420 | serverThread->join(); 421 | clientThread->join(); 422 | } 423 | -------------------------------------------------------------------------------- /tests/src/inet/TCPConnection_test.cpp: -------------------------------------------------------------------------------- 1 | #include "inet/TCPConnection.hpp" 2 | #include "gtest/gtest.h" 3 | 4 | #ifdef HAVE_ARPA_INET_H 5 | #include "arpa/inet.h" 6 | #endif /* HAVE_ARPA_INET_H */ 7 | #ifdef HAVE_WS2TCPIP_H 8 | #include 9 | #endif /* HAVE_WS2TCPIP_H */ 10 | 11 | TEST(TCPConnectionTest, Constructor) 12 | { 13 | ASSERT_NO_THROW({ 14 | inet::TCPConnection tcpc; 15 | }); 16 | 17 | // Capture Constructor 18 | inet::TCPConnection oldConnection; 19 | int newSocket = ::socket(AF_INET, SOCK_STREAM, 0); 20 | ASSERT_NE(newSocket, -1); 21 | 22 | std::string ipAddress = "127.0.0.1"; 23 | unsigned int port = 8080; 24 | sockaddr_in addr {}; 25 | addr.sin_family = AF_INET; 26 | int aton_result = INET_ATON(ipAddress, &addr.sin_addr); 27 | ASSERT_NE(aton_result, ATON_ERROR); 28 | 29 | addr.sin_port = htons(port); 30 | 31 | EXPECT_NO_THROW({ 32 | inet::TCPConnection capturedTCP(newSocket, oldConnection, addr); 33 | }); 34 | } 35 | 36 | TEST(TCPConnectionTest, getAddressString) 37 | { 38 | inet::TCPConnection tcpc; 39 | EXPECT_STREQ(tcpc.getAddressString().data(), "0.0.0.0:0"); 40 | } 41 | 42 | TEST(TCPConnectionTest, setAddress) 43 | { 44 | inet::TCPConnection tcpc; 45 | ASSERT_NO_THROW({ 46 | tcpc.setAddress("0.0.0.0:0"); 47 | }); 48 | 49 | } 50 | 51 | TEST(TCPConnectionTest, listen) 52 | { 53 | inet::TCPConnection tcpc; 54 | tcpc.setAddress("0.0.0.0:0"); 55 | ASSERT_NO_THROW({ 56 | tcpc.listen(); 57 | }); 58 | } 59 | 60 | TEST(TCPConnectionTest, isDataReady) 61 | { 62 | std::mutex serverAddress_mutex; 63 | std::string serverAddress {}; 64 | 65 | std::string status {}; 66 | std::mutex status_mutex; 67 | std::condition_variable status_cv {}; 68 | 69 | std::function serverFunction = [&]{ 70 | std::unique_lock statusLock {status_mutex, std::defer_lock}; 71 | 72 | // Start our server 73 | inet::TCPConnection tcp_server {}; 74 | tcp_server.setAddress("127.0.0.1:0"); 75 | { 76 | tcp_server.listen(); 77 | std::lock_guard addressLock {serverAddress_mutex}; 78 | serverAddress = tcp_server.getAddressString(); 79 | 80 | statusLock.lock(); 81 | status = "Server Started"; 82 | statusLock.unlock(); 83 | } 84 | status_cv.notify_one(); 85 | 86 | statusLock.lock(); 87 | status_cv.wait(statusLock, [&]{return status == "Client Connect Attempt";}); 88 | 89 | bool ready = tcp_server.isDataReady(2); 90 | ASSERT_EQ(ready, true); 91 | 92 | status = "Server Accept Attempt"; 93 | 94 | statusLock.unlock(); 95 | status_cv.notify_one(); 96 | return; 97 | }; 98 | 99 | std::function clientFunction = [&]{ 100 | std::unique_lock statusLock {status_mutex}; 101 | status_cv.wait(statusLock, [&]{return status == "Server Started";}); 102 | 103 | inet::TCPConnection tcp_client {}; 104 | int connect_result = 0; 105 | // Try to connect 106 | { 107 | std::lock_guard addressLock {serverAddress_mutex}; 108 | connect_result = tcp_client.connect(serverAddress); 109 | EXPECT_EQ(connect_result, 0); 110 | } 111 | status = "Client Connect Attempt"; 112 | statusLock.unlock(); 113 | status_cv.notify_one(); 114 | 115 | 116 | statusLock.lock(); 117 | status_cv.wait(statusLock, [&]{return status == "Server Accept Attempt";}); 118 | }; 119 | 120 | std::thread serverWorker{serverFunction}; 121 | std::thread clientWorker{clientFunction}; 122 | serverWorker.join(); 123 | clientWorker.join(); 124 | } 125 | 126 | TEST(TCPConnectionTest, connect) 127 | { 128 | inet::TCPConnection tcpc; 129 | int result = tcpc.connect("127.0.0.1:2300"); 130 | ASSERT_EQ(result == ERR(ETIMEDOUT) || result == ERR(ECONNREFUSED), true); 131 | } 132 | 133 | TEST(TCPConnectionTest, castInt) 134 | { 135 | inet::TCPConnection tcpc; 136 | int result = tcpc; 137 | ASSERT_NE(result, -1); 138 | } 139 | -------------------------------------------------------------------------------- /tests/src/inet/UDPConnection_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "inet/UDPConnection.hpp" 3 | #include "gtest/gtest.h" 4 | 5 | TEST(UDPConnection, constructor) 6 | { 7 | EXPECT_NO_THROW({ 8 | inet::UDPConnection udpc {}; 9 | }); 10 | } 11 | 12 | TEST(UDPConnection, sendAndRecv) 13 | { 14 | std::mutex m; 15 | std::condition_variable cv; 16 | std::string worker1_address {}; 17 | std::string worker2_address {}; 18 | std::string status {}; 19 | 20 | std::thread worker1 {[&]{ 21 | std::string data {}; 22 | std::unique_lock lk {m, std::defer_lock}; 23 | inet::UDPConnection udpc {}; 24 | worker1_address = udpc.getAddressString(); 25 | 26 | status = "worker1 ready"; 27 | cv.notify_one(); 28 | lk.lock(); 29 | cv.wait(lk, [&]{return status == "worker 2 ready";}); 30 | 31 | data = "my data"; 32 | int bytes_sent = udpc.send(data.data(), static_cast(data.size())); 33 | ASSERT_EQ(bytes_sent, -1); 34 | 35 | int result = udpc.connect(worker2_address); 36 | EXPECT_EQ(result, 0) << ("failed to connect to: " + worker2_address); 37 | bytes_sent = udpc.send(data.data(), static_cast(data.size())); 38 | EXPECT_GT(bytes_sent, 0) << ("sending error: " + std::to_string(ERRORCODE)); 39 | 40 | status = "data sent"; 41 | lk.unlock(); 42 | cv.notify_one(); 43 | 44 | lk.lock(); 45 | cv.wait(lk, [&]{return status == "worker 2 received and sent";}); 46 | 47 | char buffer[500] = {}; 48 | udpc.recv(buffer, 500); 49 | EXPECT_STREQ(buffer, "your data received"); 50 | 51 | status = "worker 1 received"; 52 | lk.unlock(); 53 | cv.notify_one(); 54 | }}; 55 | 56 | std::thread worker2 {[&]{ 57 | std::unique_lock lk{m}; 58 | cv.wait(lk, [&]{return status == "worker1 ready";}); 59 | 60 | inet::UDPConnection udpc {}; 61 | udpc.setAddress("127.0.0.1:0"); 62 | worker2_address = udpc.getAddressString(); 63 | 64 | status = "worker 2 ready"; 65 | lk.unlock(); 66 | cv.notify_one(); 67 | 68 | lk.lock(); 69 | cv.wait(lk, [&]{return status == "data sent";}); 70 | 71 | char buffer[500] = {}; 72 | inet::ServiceAddress sa {}; 73 | udpc.recvFrom(buffer, 500, sa); 74 | EXPECT_STREQ(buffer, "my data"); 75 | 76 | std::string data = "your data received"; 77 | int bytes_sent = udpc.sendTo(data.data(), static_cast(data.size()), sa); 78 | EXPECT_GT(bytes_sent, 0); 79 | 80 | status = "worker 2 received and sent"; 81 | lk.unlock(); 82 | cv.notify_one(); 83 | 84 | lk.lock(); 85 | cv.wait(lk, [&]{return status == "worker 1 received";}); 86 | }}; 87 | 88 | worker1.join(); 89 | worker2.join(); 90 | } 91 | -------------------------------------------------------------------------------- /tests/src/main_test.cpp: -------------------------------------------------------------------------------- 1 | #define G3_DYNAMIC_LOGGING 2 | 3 | #include 4 | #include 5 | #include "../include/CustomSink.hpp" 6 | #include "gtest/gtest.h" 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | // Initialize the logger 11 | std::unique_ptr logWorker{g3::LogWorker::createLogWorker()}; 12 | logWorker->addSink(std::make_unique(), &CustomSink::ReceiveLogMessage); 13 | g3::initializeLogging(logWorker.get()); 14 | g3::log_levels::setHighest(INFO); 15 | 16 | // Begin testing 17 | ::testing::InitGoogleTest(&argc, argv); 18 | return RUN_ALL_TESTS(); 19 | } 20 | --------------------------------------------------------------------------------