diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b0051c..4eed507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ list( APPEND subdirs "controls" "msl" + "network" "graphics" "audio" diff --git a/memory/CMakeLists.txt b/memory/CMakeLists.txt index feb4f4c..7bd806d 100644 --- a/memory/CMakeLists.txt +++ b/memory/CMakeLists.txt @@ -15,6 +15,9 @@ set( sources "arena.hpp" "observer.hpp" + + "utility/uint8_msb_stream.hpp" + "utility/uint8_split_merge.hpp" ) motor_vs_src_dir( sources ) diff --git a/memory/manager/manager.cpp b/memory/manager/manager.cpp index d533290..2cdf944 100644 --- a/memory/manager/manager.cpp +++ b/memory/manager/manager.cpp @@ -95,7 +95,7 @@ void_ptr_t manager::alloc( size_t const sib, char_cptr_t purpose, bool_t const m if ( _observer->on_alloc( sib, managed ) > 500000 ) { _observer->swap_and_clear() ; - std::cout << "[MOTOR_MEMORY_OBSERVER] : swap and clear" << std::endl ; + //std::cout << "[MOTOR_MEMORY_OBSERVER] : swap and clear" << std::endl ; } #endif diff --git a/memory/utility/uint8_msb_stream.hpp b/memory/utility/uint8_msb_stream.hpp new file mode 100644 index 0000000..c11d4b9 --- /dev/null +++ b/memory/utility/uint8_msb_stream.hpp @@ -0,0 +1,103 @@ + +#pragma once + +#include "../typedefs.h" +#include "uint8_split_merge.hpp" + +namespace motor +{ + namespace memory + { + /// streams certain numbers to an array set in the ctor. + /// @note not thread safe + class uint8_msb_stream + { + typedef uint8_msb_stream this_t ; + typedef this_t & this_ref_t ; + + uint8_t * _array ; + size_t _max_len ; + + size_t _cur_pos = size_t(0) ; + + public: + + uint8_msb_stream( uint8_t * ar, size_t max_len ) noexcept : _array(ar), _max_len(max_len) {} + + public: + + this_ref_t operator << ( uint8_t const n ) noexcept + { + _array[_cur_pos++] = n ; + return *this ; + } + + this_ref_t operator << ( uint16_t const n ) noexcept + { + motor::memory::uint8_msb_split::of( n, &_array[_cur_pos] ) ; + _cur_pos += 2 ; + return *this ; + } + + this_ref_t operator << ( uint32_t const n ) noexcept + { + motor::memory::uint8_msb_split::of( n, &_array[_cur_pos] ) ; + _cur_pos += 4 ; + return *this ; + } + + this_ref_t operator << ( uint64_t const n ) noexcept + { + motor::memory::uint8_msb_split::of( n, &_array[_cur_pos] ) ; + _cur_pos += 8 ; + return *this ; + } + + }; + + class uint8c_msb_stream + { + typedef uint8c_msb_stream this_t ; + typedef this_t & this_ref_t ; + + uint8_t const * _array ; + size_t _max_len ; + + size_t _cur_pos = size_t(0) ; + + public: + + uint8c_msb_stream( uint8_t const * ar, size_t max_len ) noexcept : _array(ar), _max_len(max_len) {} + + public: + + this_ref_t operator >> ( uint8_t & n ) noexcept + { + n = _array[_cur_pos++] ; + return *this ; + } + + this_ref_t operator >> ( uint16_t & n ) noexcept + { + n = motor::memory::uint8_msb_merge::uint16( &_array[_cur_pos] ) ; + _cur_pos += 2 ; + return *this ; + } + + this_ref_t operator >> ( uint32_t & n ) noexcept + { + n = motor::memory::uint8_msb_merge::uint32( &_array[_cur_pos] ) ; + _cur_pos += 4 ; + return *this ; + } + + this_ref_t operator >> ( uint64_t & n ) noexcept + { + n = motor::memory::uint8_msb_merge::uint64( &_array[_cur_pos] ) ; + _cur_pos += 8 ; + return *this ; + } + + }; + } +} diff --git a/memory/utility/uint8_split_merge.hpp b/memory/utility/uint8_split_merge.hpp new file mode 100644 index 0000000..73ca86b --- /dev/null +++ b/memory/utility/uint8_split_merge.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "../typedefs.h" + +namespace motor +{ + namespace memory + { + /// the most significant byte is written + /// first into the stream + struct uint8_msb_split + { + /// split a 16 bit number into 2 bytes + /// @todo SIMD/Vector operation + /// @precondition to must have at least 2 byte space + static void_t of( uint16_t const number, uint8_t * to ) noexcept + { + // vec2(number) >> vec2(8,0) & vec2(255) + *(to+0) = (number >> 8) & 255 ; + *(to+1) = (number >> 0) & 255 ; + } + + /// split a 32 bit number into 4 bytes + /// @todo SIMD/Vector operation + /// @precondition to must have at least 4 byte space + static void_t of( uint32_t const number, uint8_t * to ) noexcept + { + // vec4(number) >> vec4(24,16,8,0) & vec4(255) + *(to+0) = (number >> 24) & 255 ; + *(to+1) = (number >> 16) & 255 ; + *(to+2) = (number >> 8) & 255 ; + *(to+3) = (number >> 0) & 255 ; + } + + /// split a 64 bit number into 8 bytes + /// @todo SIMD/Vector operation + /// @precondition to must have at least 8 byte space + static void_t of( uint64_t const number, uint8_t * to ) noexcept + { + // vec4(number) >> vec4(56,48,40,32) & vec4(255) + // vec4(number) >> vec4(24,16,8,0) & vec4(255) + *(to+0) = (number >> 56) & 255 ; + *(to+1) = (number >> 48) & 255 ; + *(to+2) = (number >> 40) & 255 ; + *(to+3) = (number >> 32) & 255 ; + *(to+4) = (number >> 24) & 255 ; + *(to+5) = (number >> 16) & 255 ; + *(to+6) = (number >> 8) & 255 ; + *(to+7) = (number >> 0) & 255 ; + } + }; + + struct uint8_msb_merge + { + static uint16_t uint16( uint8_t const * from ) noexcept + { + uint16_t const at_1 = *(from+1) ; + uint16_t const at_0 = *(from+0) ; + + return uint16_t( (at_0 << 8) | (at_1 << 0) ) ; + } + + static uint32_t uint32( uint8_t const * from ) noexcept + { + uint32_t const at_3 = *(from+3) ; + uint32_t const at_2 = *(from+2) ; + uint32_t const at_1 = *(from+1) ; + uint32_t const at_0 = *(from+0) ; + + return uint32_t( (at_0 << 24) | (at_1 << 16) | (at_2 << 8) | (at_3 << 0) ) ; + } + + static uint64_t uint64( uint8_t const * from ) noexcept + { + uint64_t const at_7 = *(from+7) ; + uint64_t const at_6 = *(from+6) ; + uint64_t const at_5 = *(from+5) ; + uint64_t const at_4 = *(from+4) ; + uint64_t const at_3 = *(from+3) ; + uint64_t const at_2 = *(from+2) ; + uint64_t const at_1 = *(from+1) ; + uint64_t const at_0 = *(from+0) ; + + return uint64_t( + (at_0 << 56) | (at_1 << 48) | (at_2 << 40) | (at_3 << 32) | + (at_4 << 24) | (at_5 << 16) | (at_6 << 8) | (at_7 << 0) ) ; + } + + }; + } +} diff --git a/network/CMakeLists.txt b/network/CMakeLists.txt new file mode 100644 index 0000000..e1c209f --- /dev/null +++ b/network/CMakeLists.txt @@ -0,0 +1,44 @@ + + +set( SOURCES ${SOURCES} + "api.h" + "typedefs.h" + + "imodule.h" + "system.h" + "system.cpp" + "ipv4_address.hpp" + + "convert.hpp" + + "socket/connection_info.hpp" + "socket/tcp_socket.h" + "socket/tcp_socket.cpp" + ) + +motor_vs_src_dir( SOURCES ) + +add_library( ${cur_lib_name} ${MOTOR_LIBRARY_BUILD_TYPE} ${SOURCES} ) +add_library( ${cur_alias_name} ALIAS ${cur_lib_name} ) + + +target_link_libraries( ${cur_lib_name} +PUBLIC motor::concurrent +PUBLIC motor::log +PUBLIC motor::memory +PUBLIC motor::std +PUBLIC motor::core +) + +########################################################### +# SECTION: Build Tree +########################################################### + +#motor_export( ${cur_lib_name} ) + +########################################################### +# SECTION: Install +########################################################### + +install_headers( "${sources}" "include/${PROJECT_NAME}/${cur_lib_name}" ) +install_library( ${cur_lib_name} ${PROJECT_NAME}-targets ) diff --git a/network/convert.hpp b/network/convert.hpp new file mode 100644 index 0000000..f9d2ffb --- /dev/null +++ b/network/convert.hpp @@ -0,0 +1,49 @@ +#pragma once + + +#include "typedefs.h" +#include + +namespace motor +{ + namespace network + { + /// register -> network byte order(nbo) + struct to_nbo + { + static void_t of( uint16_t const number, uint8_t * to ) + { + motor::memory::uint8_msb_split::of( number, to ) ; + } + + static void_t of( uint32_t const number, uint8_t * to ) + { + motor::memory::uint8_msb_split::of( number, to ) ; + } + + static void_t of( uint64_t const number, uint8_t * to ) + { + motor::memory::uint8_msb_split::of( number, to ) ; + } + } ; + + /// network byte order(nbo) -> register + struct from_nbo + { + static uint16_t data_16( const uint8_t * from ) + { + return motor::memory::uint8_msb_merge::uint16( from ) ; + } + + static uint32_t data_32( const uint8_t * from ) + { + return motor::memory::uint8_msb_merge::uint32( from ) ; + } + + static uint64_t data_64( const uint8_t * from ) + { + return motor::memory::uint8_msb_merge::uint64( from ) ; + } + } ; + } +} diff --git a/network/imodule.h b/network/imodule.h new file mode 100644 index 0000000..5c346be --- /dev/null +++ b/network/imodule.h @@ -0,0 +1,54 @@ + +#pragma once + +#include "api.h" +#include "typedefs.h" + +#include "ipv4_address.hpp" + +namespace motor{ namespace network +{ + class iserver_handler + { + public: + + virtual motor::network::accept_result on_accept( motor::network::client_id_t const, + motor::network::ipv4::address_cref_t ) noexcept = 0 ; + + virtual void_t on_close( motor::network::client_id_t const ) noexcept = 0 ; + + virtual motor::network::receive_result on_receive( + motor::network::client_id_t const, byte_cptr_t, size_t const ) noexcept = 0 ; + + virtual motor::network::transmit_result on_send( + motor::network::client_id_t const, byte_cptr_t & buffer, size_t & num_sib ) noexcept = 0 ; + }; + motor_typedef( iserver_handler ) ; + + struct create_tcp_server_info + { + motor::string_t name ; + motor::network::ipv4::binding_point_t bp ; + iserver_handler_mtr_safe_t handler ; + }; + motor_typedef( create_tcp_server_info ) ; + + struct create_tcp_client_info + { + motor::string_t name ; + motor::network::ipv4::binding_point_t bp ; + send_funk_t send_handler ; + recv_funk_t recv_handler ; + }; + motor_typedef( create_tcp_client_info ) ; + + class MOTOR_NETWORK_API imodule + { + public: + + virtual socket_id_t create_tcp_client( motor::network::create_tcp_client_info_rref_t ) noexcept = 0 ; + virtual socket_id_t create_tcp_server( motor::network::create_tcp_server_info_rref_t ) noexcept = 0 ; + + }; + motor_typedef( imodule ) ; +} } diff --git a/network/ipv4_address.hpp b/network/ipv4_address.hpp new file mode 100644 index 0000000..a1653fa --- /dev/null +++ b/network/ipv4_address.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include "typedefs.h" + +#include +#include + + +namespace motor { namespace network { namespace ipv4 { + + class address + { + motor_this_typedefs( address ) ; + + private: + + uint_t _x ; + uint_t _y ; + uint_t _z ; + uint_t _w ; + + public: + + address( void ) noexcept + { + _x = 127 ; + _y = 0 ; + _z = 0 ; + _w = 1 ; + } + + address( uint_t a, uint_t b, uint_t c, uint_t d ) noexcept + { + _x = a ; _y = b ; _z = c ; _w = d ; + } + + address( this_cref_t rhv ) noexcept + { + _x = rhv._x ; _y = rhv._y ; _z = rhv._z ; _w = rhv._w ; + } + + bool_t operator == ( this_cref_t rhv ) const noexcept + { + return _x == rhv._x && _y == rhv._y && _z == rhv._z && _w == rhv._w ; + } + + uint_t get_a( void ) const noexcept { return _x ; } + uint_t get_b( void ) const noexcept { return _y ; } + uint_t get_c( void ) const noexcept { return _z ; } + uint_t get_d( void ) const noexcept { return _w ; } + + public: + + motor::string_t to_string( void ) const noexcept + { + return motor::to_string( _x ) + "." + motor::to_string( _y ) + + "." + motor::to_string( _z ) + "." + motor::to_string( _w ) ; + } + + static this_t from_string( motor::string_in_t host ) noexcept + { + if ( host == "localhost" ) return this_t( 127, 0, 0, 1 ) ; + + motor::vector< motor::string_t > tokens ; + + { + size_t start = 0 ; + size_t pos = 0 ; + motor::string_t const delimiter = "." ; + + motor::string_t token; + while ( ( pos = host.find( delimiter, start ) ) != std::string::npos ) + { + token = host.substr( start, pos - start ); + tokens.push_back( token ) ; + start = pos + 1 ; + } + + if ( start != host.size() ) + { + token = host.substr( start, ( host.size() ) - start ); + tokens.push_back( token ) ; + } + } + + if ( tokens.size() != 4 ) + { + motor::log::global_t::error( "[ip4_address::from_string] : invalid host address" ) ; + return this_t( 127, 0, 0, 1 ) ; + } + + return this_t( std::atol( tokens[ 0 ].c_str() ), std::atol( tokens[ 1 ].c_str() ), std::atol( tokens[ 2 ].c_str() ), std::atol( tokens[ 3 ].c_str() ) ) ; + } + }; + motor_typedef( address ) ; + motor_typedefs( motor::vector< address_t >, addresses ) ; + + struct binding_point + { + uint_t port ; + address_t address ; + + static binding_point localhost_at( uint_t const port ) noexcept + { + return binding_point { port, address_t() } ; + } + + motor::string_t to_string( void_t ) const noexcept + { + return address.to_string() + ":" + motor::to_string( port ) ; + } + }; + motor_typedef( binding_point ) ; + +} } } \ No newline at end of file diff --git a/network/socket/connection_info.hpp b/network/socket/connection_info.hpp new file mode 100644 index 0000000..e69de29 diff --git a/network/socket/tcp_socket.cpp b/network/socket/tcp_socket.cpp new file mode 100644 index 0000000..e69de29 diff --git a/network/socket/tcp_socket.h b/network/socket/tcp_socket.h new file mode 100644 index 0000000..e69de29 diff --git a/network/system.cpp b/network/system.cpp new file mode 100644 index 0000000..f59467c --- /dev/null +++ b/network/system.cpp @@ -0,0 +1,9 @@ + +#include "system.h" + +using namespace motor::network ; + +system::system( void_t ) noexcept +{ + +} \ No newline at end of file diff --git a/network/system.h b/network/system.h new file mode 100644 index 0000000..e5f1b9c --- /dev/null +++ b/network/system.h @@ -0,0 +1,25 @@ + + +#pragma once + +#include "api.h" +#include "typedefs.h" + +#include "imodule.h" + +namespace motor{ namespace network { + + class MOTOR_NETWORK_API system + { + motor_this_typedefs( system ) ; + + private: + + public: + + system( void_t ) noexcept ; + + }; + motor_typedef( system ) ; +} +} \ No newline at end of file diff --git a/network/typedefs.h b/network/typedefs.h new file mode 100644 index 0000000..b8aeaaa --- /dev/null +++ b/network/typedefs.h @@ -0,0 +1,50 @@ + +#pragma once + +#include +#include + +#include +#include + +#include + +namespace motor +{ + namespace network + { + using namespace motor::core::types ; + + using socket_id_t = size_t ; + using client_id_t = size_t ; + + enum class accept_result + { + ok, + no_accept + }; + + enum class receive_result + { + ok, + close + }; + + enum class transmit_result + { + proceed, + break_now, + have_more, + break_after + }; + + + static const size_t send_buffer_sib = 2048 ; + + using recv_funk_t = std::function< receive_result ( byte_cptr_t, size_t const ) > ; + using send_funk_t = std::function< transmit_result ( byte_ptr_t, size_t &, size_t const ) > ; + + } +} + + diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index 1da5969..bbc9423 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -31,7 +31,7 @@ if( MOTOR_TARGET_OS_WIN ) ################################################### # controls list( APPEND sources ${controls_win32_sources} ) - set( os_libs winmm.lib xinput ) + list( APPEND os_libs winmm xinput ) if( MOTOR_COMPILER_MSC ) # warning C4005 : '__useHeader' : macro redefinition @@ -51,6 +51,25 @@ elseif( MOTOR_TARGET_OS_LIN ) list( APPEND sources ${controls_linux_sources} ) endif() +## +## Network Section +## + +if( MOTOR_TARGET_OS_WIN ) + set( network_win32_sources + "network/win32/win32_net_module.h" + "network/win32/win32_net_module.cpp" + ) + + list( APPEND sources ${network_win32_sources} ) + list( APPEND os_libs winmm Ws2_32 ) + +elseif( MOTOR_TARGET_OS_LIN ) + message( "Platform requires linux network implementation" ) +endif() + +list( APPEND sources "network/network_module_creator.hpp" ) + ## ## Graphics Section ## @@ -130,7 +149,7 @@ elseif( MOTOR_WINDOW_SYSTEM_XLIB ) "application/xlib/xlib_carrier.cpp" ) - set( os_libs ${X11_LIBRARIES} ) + list( APPEND os_libs ${X11_LIBRARIES} ) else() message( FATAL_ERROR "Window System: No Os specified." ) @@ -174,6 +193,7 @@ target_link_libraries( ${cur_lib_name} PUBLIC motor::application PUBLIC motor::audio PUBLIC motor::profiling + PUBLIC motor::network # make oal optional PRIVATE OpenAL diff --git a/platform/network/network_module_creator.hpp b/platform/network/network_module_creator.hpp new file mode 100644 index 0000000..e980d18 --- /dev/null +++ b/platform/network/network_module_creator.hpp @@ -0,0 +1,23 @@ + + +#pragma once + +#ifdef MOTOR_TARGET_OS_WIN +#include "win32/win32_net_module.h" +#endif + +namespace motor { namespace platform +{ + struct network_module_creator + { + static motor::network::imodule_ptr_t create( void_t ) noexcept + { + #ifdef MOTOR_TARGET_OS_WIN + return motor::memory::global_t::alloc( motor::platform::win32::win32_net_module_t(), + "[network_module_creator] : win32 network module" ) ; + #endif + } + + }; + +} } \ No newline at end of file diff --git a/platform/network/win32/win32_net_module.cpp b/platform/network/win32/win32_net_module.cpp new file mode 100644 index 0000000..62d5f16 --- /dev/null +++ b/platform/network/win32/win32_net_module.cpp @@ -0,0 +1,456 @@ + +#include "win32_net_module.h" +#include + +#include + +#include + +using namespace motor::platform ; +using namespace motor::platform::win32 ; + +//***************************************************************** +win32_net_module::win32_net_module( void_t ) noexcept +{ + WSAData data ; + auto const res = WSAStartup( MAKEWORD( 2, 2 ), &data ) ; + if ( res != 0 ) + { + motor::log::global::error( "[win32_net_module::initialize] : WSAStartup" ) ; + } + else _is_init = true ; +} + +//***************************************************************** +win32_net_module::win32_net_module( this_rref_t rhv ) noexcept +{ + _tcp_clients = std::move( rhv._tcp_clients ) ; + _is_init = rhv._is_init ; + rhv._is_init = false ; +} + +//***************************************************************** +win32_net_module::~win32_net_module( void_t ) noexcept +{ + this_t::release_all_tcp() ; + if ( _is_init ) WSACleanup() ; +} + +//***************************************************************** +motor::network::socket_id_t win32_net_module::create_tcp_client( + motor::network::create_tcp_client_info_rref_t info_in ) noexcept +{ + SOCKET s = INVALID_SOCKET ; + addrinfo * result = NULL ; + addrinfo * ptr = NULL ; + addrinfo hints ; + + { + ZeroMemory( &hints, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + + // Resolve the server address and port + { + auto const res = getaddrinfo( info_in.bp.address.to_string().c_str(), + std::to_string( info_in.bp.port ).c_str(), &hints, &result ); + + if ( res != 0 ) + { + motor::log::global::error( "[win32_net_module::create_tcp_client] : getaddrinfo" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_client] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + return motor::network::socket_id_t( -1 ) ; + } + } + + for ( ptr = result; ptr != NULL; ptr = ptr->ai_next ) + { + s = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol ) ; + if ( s == INVALID_SOCKET ) + { + motor::log::global::error( "[win32_net_module::create_tcp_client] : socket" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_client] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + return motor::network::socket_id_t( -1 ) ; + } + + // Connect to server. + auto const res = connect( s, ptr->ai_addr, (int) ptr->ai_addrlen ); + if ( res == SOCKET_ERROR ) + { + closesocket( s ); + s = INVALID_SOCKET; + continue; + } + + break; + } + + freeaddrinfo( result ); + + if ( s == INVALID_SOCKET ) + { + motor::log::global::error( "[win32_net_module::create_tcp_client] : connect" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_client] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + + return motor::network::socket_id_t( -1 ) ; + } + + { + DWORD timeout_ms = 1000 ; + auto const res = setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (char_cptr_t) &timeout_ms, sizeof DWORD ); + motor::log::global_t::error( res != 0, "[win32_net_module::recv] : setsockopt SO_RCVTIMEO" ) ; + } + + { + uint_t max_packet_size ; + int len = sizeof( uint_t ) ; + auto res = getsockopt( s, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *) (&max_packet_size), &len ) ; + motor::log::global_t::error( res != 0, "[win32_net_module::recv] : getsockopt SO_MAX_MSG_SIZE" ) ; + } + + motor::network::socket_id_t const sid = this_t::create_tcp_client_id( s ) ; + + // start sender and receiver + { + this_t::tcp_client_data_ptr_t tcpd = _tcp_clients[ sid ] ; + this_t::start_tcp_sender( tcpd, info_in.send_handler ) ; + this_t::start_tcp_receiver( tcpd, info_in.recv_handler ) ; + } + + return sid ; +} + +//***************************************************************** +motor::network::socket_id_t win32_net_module::create_tcp_server( + motor::network::create_tcp_server_info_rref_t info_in ) noexcept +{ + if ( info_in.handler == nullptr ) + { + motor::log::global_t::error("[win32_net_module] : server handler required") ; + return motor::network::socket_id_t( -1 ) ; + } + + addrinfo * result = NULL; + addrinfo hints; + + ZeroMemory( &hints, sizeof( hints ) ); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + { + // Resolve the server address and port + auto const res = getaddrinfo( NULL, std::to_string( info_in.bp.port ).c_str(), &hints, &result ); + if ( res != 0 ) + { + motor::log::global::error( "[win32_net_module::create_tcp_server] : getaddrinfo" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_server] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + return motor::network::socket_id_t( -1 ) ; + } + } + + SOCKET s = INVALID_SOCKET ; + + // Create server socket + { + s = socket( result->ai_family, result->ai_socktype, result->ai_protocol ); + if ( s == INVALID_SOCKET ) + { + motor::log::global::error( "[win32_net_module::create_tcp_server] : socket" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_server] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + return motor::network::socket_id_t( -1 ) ; + } + } + + // bind to endpoint + { + auto const res = bind( s, result->ai_addr, (int) result->ai_addrlen ); + if ( res == SOCKET_ERROR ) + { + motor::log::global::error( "[win32_net_module::create_tcp_server] : socket" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_server] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + return motor::network::socket_id_t( -1 ) ; + } + } + + // start listening for incoming connections + { + auto const res = listen( s, SOMAXCONN ); + if ( res == SOCKET_ERROR ) + { + motor::log::global::error( "[win32_net_module::create_tcp_server] : listen" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_server] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + return motor::network::socket_id_t( -1 ) ; + } + } + + // do non-blocking mode + { + u_long mode = 1; // 1 to enable non-blocking socket + auto const res = ioctlsocket( s, FIONBIO, &mode ); + if ( res != 0 ) + { + motor::log::global::error( "[win32_net_module::create_tcp_server] : ioctlsocket" ) ; + motor::log::global::error( "[win32_net_module::create_tcp_server] : WSAGetLastError " + + motor::to_string( WSAGetLastError() ) ) ; + } + } + + freeaddrinfo( result ); + + motor::network::socket_id_t const sid = this_t::create_tcp_server_id( s ) ; + + // thread for accepting, handshaking and recv/send + { + this_t::tcp_server_data_ptr_t tcpd = _tcp_servers[ sid ] ; + tcpd->port = info_in.bp.port ; + tcpd->handler = motor::move( info_in.handler ) ; + + tcpd->t = std::thread( [=] ( void_t ) + { + motor::string_t const log_start = "[TCP server " + info_in.name + "] : " ; + motor::log::global::status( log_start + "online and accepting incoming connections." ) ; + + tcpd->running = true ; + + while ( tcpd->running ) + { + sockaddr_in addr ; + int_t addr_sib = sizeof( sockaddr_in ) ; + + SOCKET s = accept( tcpd->s, (sockaddr *) &addr, &addr_sib ); + if ( s != INVALID_SOCKET ) + { + motor::network::ipv4::binding_point const bp = { + uint_t( ntohs( addr.sin_port ) ), + motor::network::ipv4::address { + addr.sin_addr.S_un.S_un_b.s_b1, + addr.sin_addr.S_un.S_un_b.s_b2, + addr.sin_addr.S_un.S_un_b.s_b3, + addr.sin_addr.S_un.S_un_b.s_b4 } + } ; + motor::log::global::status( log_start + "client connected via " + bp.to_string() ) ; + + + auto const cid = tcpd->get_client_id( this_t::tcp_server_data_t::client_info { s, bp } ) ; + auto const ares = tcpd->handler->on_accept( cid, bp.address ) ; + if ( ares == motor::network::accept_result::no_accept ) + { + motor::log::global::status( log_start + "client rejected via " + bp.to_string() ) ; + tcpd->invalidate_client( cid ) ; + } + } + else if ( s == INVALID_SOCKET ) + { + auto const res = WSAGetLastError() ; + if ( res == WSAEWOULDBLOCK ) + {} + else + { + motor::log::global::error( "[win32_net_module::server_thread] : accept" ) ; + motor::log::global::error( "[win32_net_module::server_thread] : WSAGetLastError " + + motor::to_string(res) ) ; + } + } + + { + // handle clients + size_t idx = 0 ; + for ( auto & ci : tcpd->clients ) + { + // send + { + byte_cptr_t buffer = nullptr ; + size_t sib = 0 ; + + { + auto const res = tcpd->handler->on_send( idx, buffer, sib ) ; + // handle res here + } + + auto const has_sent = send( ci.s, (const char *) buffer, int( sib), 0 ) ; + if ( has_sent == SOCKET_ERROR ) + { + motor::log::global::error( "[win32_net_module::server_thread] : send" ) ; + motor::log::global::error( "[win32_net_module::server_thread] : WSAGetLastError " + + motor::to_string( has_sent ) ) ; + } + if ( has_sent != sib ) + { + // need to fix here. need to call on_send multiple times + motor::log::global_t::error("[win32 tcp_server] : has_sent != sib") ; + } + + } + // receive + + ++idx ; + } + } + + + std::this_thread::sleep_for( std::chrono::milliseconds( 5 ) ) ; + } + } ) ; + } + + return sid ; +} + +//***************************************************************** +bool_t win32_net_module::start_tcp_sender( this_t::tcp_client_data_ptr_t tcpd, motor::network::send_funk_t send_funk ) noexcept +{ + tcpd->send_funk = std::move( send_funk ) ; + + tcpd->send_thread = std::thread( [=] ( void ) + { + motor::log::global_t::status("[win32_net_module] : start sender") ; + + size_t to_be_send = 0 ; + size_t cur_buffer_idx = 0 ; + size_t const num_buffers = 3 ; + byte_t send_stuff[ num_buffers * motor::network::send_buffer_sib ] ; + + while ( true ) + { + //memset( cur_buffer.data(), 0, cur_buffer.size() ) ; + + size_t cur_send_sib = 0 ; + + byte_ptr_t cur_ptr = send_stuff + cur_buffer_idx * motor::network::send_buffer_sib ; + auto res = tcpd->send_funk( cur_ptr, to_be_send, motor::network::send_buffer_sib ) ; + + cur_send_sib += to_be_send ; + + if ( res == motor::network::transmit_result::have_more ) + { + // flush the buffer array + if ( ++cur_buffer_idx == num_buffers ) + { + int const num_bytes = motor::network::send_buffer_sib * num_buffers ; + auto const has_sent = send( tcpd->s, (const char *) send_stuff, num_bytes, 0 ) ; + if ( has_sent != cur_send_sib ) + { + motor::log::global_t::status( "[win32_net_module::send] : has_sent != num_bytes" ) ; + } + + cur_buffer_idx = 0 ; + cur_send_sib = 0 ; + } + continue ; + } + + if ( cur_send_sib != 0 ) + { + auto const has_sent = send( tcpd->s, (const char *) cur_ptr, (int) cur_send_sib, 0 ) ; + if ( has_sent != cur_send_sib ) + { + motor::log::global_t::status( "[win32_net_module::send] : has_sent != num_bytes" ) ; + } + + } + } + } ) ; + + return true ; +} + +//***************************************************************** +bool_t win32_net_module::start_tcp_receiver( this_t::tcp_client_data_ptr_t tcpd, motor::network::recv_funk_t recv_funk ) noexcept +{ + tcpd->recv_funk = std::move( recv_funk ) ; + + tcpd->recv_thread = std::thread( [=] ( void ) + { + motor::log::global_t::status( "[win32_net_module] : start receiver" ) ; + + while ( true ) + { + size_t const buflen = 2048 ; + char_t buffer[ buflen ] ; + + auto const received = recvfrom( tcpd->s, buffer, buflen, 0, NULL, NULL ); + + // closed + if ( received == 0 ) break ; + + // timeout and others + if ( received == -1 ) continue ; + + auto const res = tcpd->recv_funk( (byte_cptr_t) buffer, received ) ; + if ( res == motor::network::receive_result::close ) break ; + } + + } ) ; + + return true ; +} + +//***************************************************************** +size_t win32_net_module::create_tcp_client_id( SOCKET s ) noexcept +{ + std::lock_guard< std::mutex > lk( _mtx_tcp_clients ) ; + + for ( size_t i=0; i< _tcp_clients.size(); ++i ) + { + if ( _tcp_clients[i]->s == INVALID_SOCKET ) + { + auto & tcpd = *_tcp_clients[ i ] ; + + tcpd.s = s ; + + return i ; + } + } + + size_t const id = _tcp_clients.size() ; + _tcp_clients.resize( _tcp_clients.size() + 1 ) ; + _tcp_clients[ id ] = motor::memory::global_t::alloc( tcp_client_data( s ), + "[win32_net_module] : tcp data" ) ; + + return id ; +} + +//***************************************************************** +size_t win32_net_module::create_tcp_server_id( SOCKET s ) noexcept +{ + std::lock_guard< std::mutex > lk( _mtx_tcp_servers ) ; + + for ( size_t i = 0; i < _tcp_servers.size(); ++i ) + { + if ( _tcp_servers[ i ]->s == INVALID_SOCKET ) + { + auto & tcpd = *_tcp_servers[ i ] ; + + tcpd.s = s ; + + return i ; + } + } + + size_t const id = _tcp_servers.size() ; + _tcp_servers.resize( _tcp_servers.size() + 1 ) ; + _tcp_servers[ id ] = motor::memory::global_t::alloc( tcp_server_data( s ), + "[win32_net_module] : tcp data" ) ; + + return id ; +} + +//***************************************************************** +void_t win32_net_module::release_all_tcp( void_t ) noexcept +{ + for ( auto * ptr : _tcp_clients ) + { + + } +} \ No newline at end of file diff --git a/platform/network/win32/win32_net_module.h b/platform/network/win32/win32_net_module.h new file mode 100644 index 0000000..f59233d --- /dev/null +++ b/platform/network/win32/win32_net_module.h @@ -0,0 +1,141 @@ + +#pragma once + +#include "../../api.h" +#include "../../typedefs.h" + +#include + +#include + +#include + +namespace motor { namespace platform { namespace win32 +{ + class MOTOR_PLATFORM_API win32_net_module : public motor::network::imodule + { + motor_this_typedefs( win32_net_module ) ; + + private: + + bool_t _is_init = false ; + + private: // tcp server data + + struct tcp_server_data + { + struct client_info + { + SOCKET s ; + motor::network::ipv4::binding_point bp ; + }; + + SOCKET s ; + std::thread t ; + motor::vector< client_info > clients ; + bool_t running = false ; + uint_t port ; // server port + + motor::network::iserver_handler_mtr_t handler ; + + public: + + tcp_server_data( void_t ) noexcept : s( INVALID_SOCKET ){} + tcp_server_data( SOCKET s_ ) noexcept : s(s_){} + tcp_server_data( tcp_server_data && rhv ) noexcept : s( rhv.s), t( std::move( rhv.t) ), + clients( std::move( rhv.clients ) ) + { rhv.s = INVALID_SOCKET ; } + ~tcp_server_data( void_t ) noexcept { assert( s == INVALID_SOCKET ) ; } + + motor::network::client_id_t get_client_id( client_info && ci ) noexcept + { + for ( size_t i=0; i _tcp_servers ; + + private: // tcp client data + + struct tcp_client_data + { + SOCKET s ; + std::thread send_thread ; + std::thread recv_thread ; + + motor::network::send_funk_t send_funk ; + motor::network::recv_funk_t recv_funk ; + + tcp_client_data( void_t ) noexcept : s( INVALID_SOCKET ){} + tcp_client_data( SOCKET s_ ) noexcept : s( s_ ) {} + tcp_client_data( tcp_client_data && rhv ) noexcept : s( rhv.s ), send_thread( std::move( rhv.send_thread ) ), + recv_thread( std::move( rhv.recv_thread ) ), + send_funk( std::move( rhv.send_funk) ) , recv_funk( std::move( rhv.recv_funk ) ) + { rhv.s = INVALID_SOCKET ;} + + ~tcp_client_data( void_t ) noexcept { assert( s == INVALID_SOCKET ) ; } + + tcp_client_data & operator = ( tcp_client_data && rhv ) noexcept + { + assert( s == INVALID_SOCKET ) ; + + s = rhv.s ; + send_thread = std::move( rhv.send_thread ) ; + recv_thread = std::move( rhv.recv_thread ) ; + send_funk = std::move( rhv.send_funk ) ; + recv_funk = std::move( rhv.recv_funk ) ; + return *this ; + } + }; + motor_typedef( tcp_client_data ) ; + + std::mutex _mtx_tcp_clients ; + motor::vector< tcp_client_data_ptr_t > _tcp_clients ; + + public: + + win32_net_module( void_t ) noexcept ; + win32_net_module( this_cref_t ) = delete ; + win32_net_module( this_rref_t ) noexcept ; + ~win32_net_module( void_t ) noexcept ; + + public: // interface + + virtual motor::network::socket_id_t create_tcp_client( + motor::network::create_tcp_client_info_rref_t ) noexcept ; + + virtual motor::network::socket_id_t create_tcp_server( + motor::network::create_tcp_server_info_rref_t ) noexcept ; + + private: + + bool_t start_tcp_sender( this_t::tcp_client_data_ptr_t, motor::network::send_funk_t ) noexcept ; + bool_t start_tcp_receiver( this_t::tcp_client_data_ptr_t, motor::network::recv_funk_t ) noexcept ; + + size_t create_tcp_client_id( SOCKET ) noexcept ; + size_t create_tcp_server_id( SOCKET ) noexcept ; + + void_t release_all_tcp( void_t ) noexcept ; + }; + motor_typedef( win32_net_module ) ; +} } }