diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj index d92ff4ce51..f8cf5724f9 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj @@ -38,6 +38,7 @@ + @@ -68,6 +69,7 @@ + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj.filters index bf1bca2c22..be141992e2 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj.filters +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj.filters @@ -90,6 +90,9 @@ Header Files + + Header Files + Header Files @@ -179,6 +182,9 @@ Source Files + + Source Files + Source Files diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj index 6cea8a7b6e..c3410fbca0 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj @@ -289,6 +289,7 @@ %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj.filters index 21c0e046cf..81ccc389de 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj.filters +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/LEM.vcxproj.filters @@ -222,6 +222,9 @@ Source Files + + Source Files + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj index c880a11c6d..b39af00a88 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj @@ -157,6 +157,7 @@ + @@ -179,6 +180,7 @@ %(PreprocessorDefinitions) + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj.filters index 9e25279581..d8456cc82b 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj.filters +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ProjectApolloMFD.vcxproj.filters @@ -44,6 +44,9 @@ Header Files + + Header Files + @@ -72,5 +75,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj index 653b59a6ad..3427b9920f 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj @@ -386,6 +386,7 @@ %(PreprocessorDefinitions) ProgramDatabase + ProgramDatabase diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj.filters index 356551dd53..86366ff000 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj.filters +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj.filters @@ -237,6 +237,9 @@ Source Files + + Source Files + Source Files diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj index f0243c86c8..06a70a62e5 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj @@ -346,6 +346,7 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) @@ -474,6 +475,7 @@ + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj.filters index b0284a34c7..608078fc9b 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj.filters +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj.filters @@ -237,6 +237,9 @@ Source Files + + Source Files + Source Files @@ -470,6 +473,9 @@ Header Files + + Header Files + Header Files diff --git a/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.cpp b/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.cpp index 9e0628b2ea..baef8d2727 100644 --- a/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.cpp @@ -26,7 +26,6 @@ #include "Orbitersdk.h" #include #include -#include // TODO: Replace with winsock2 after yaAGC updates #include "soundlib.h" #include "resource.h" #include "nasspdefs.h" @@ -2025,15 +2024,10 @@ PCM::PCM() last_rx = 0; frame_addr = 0; frame_count = 0; - m_socket = INVALID_SOCKET; } PCM::~PCM() { - if (m_socket != INVALID_SOCKET) { - shutdown(m_socket, 2); // Shutdown both streams - closesocket(m_socket); - } } void PCM::Init(Saturn *vessel){ @@ -2045,46 +2039,16 @@ void PCM::Init(Saturn *vessel){ last_update = 0; last_rx = MINUS_INFINITY; word_addr = 0; - int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); - if ( iResult != NO_ERROR ){ - sprintf(wsk_emsg,"TELECOM: Error at WSAStartup()"); - wsk_error = 1; - return; - } - m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - if ( m_socket == INVALID_SOCKET ) { - sprintf(wsk_emsg,"TELECOM: Error at socket(): %ld", WSAGetLastError()); - WSACleanup(); - wsk_error = 1; - return; - } - // Be nonblocking - int iMode = 1; // 0 = BLOCKING, 1 = NONBLOCKING - if(ioctlsocket(m_socket, FIONBIO, (u_long FAR*) &iMode) != 0){ - sprintf(wsk_emsg,"TELECOM: ioctlsocket() failed: %ld", WSAGetLastError()); + if (!NetStartup()){ + sprintf(wsk_emsg,"TELECOM: Error at NetStartup()"); wsk_error = 1; - closesocket(m_socket); - WSACleanup(); return; } - // Set up incoming options - service.sin_family = AF_INET; - service.sin_addr.s_addr = htonl(INADDR_ANY); - service.sin_port = htons( 14242 ); - - if ( ::bind( m_socket, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) { - sprintf(wsk_emsg,"Failed to start CSM telemetry. Please completely exit Orbiter and restart. Please file a bug report if this message persists."); - wsk_error = 1; - closesocket(m_socket); - WSACleanup(); - return; - } - if ( listen( m_socket, 1 ) == SOCKET_ERROR ){ + m_tcpserver = TcpService(14242); // CM on 14242, LM on 14243 + if (m_tcpserver.Status() != TcpService::STARTED) { + sprintf(wsk_emsg, "TELECOM: Error starting server: %d", m_tcpserver.ErrorCode()); wsk_error = 1; - sprintf(wsk_emsg,"TELECOM: listen() failed: %ld", WSAGetLastError()); - closesocket(m_socket); - WSACleanup(); return; } @@ -4996,73 +4960,52 @@ void PCM::perform_io(double simt){ } }else{ // Try to accept - AcceptSocket = accept( m_socket, NULL, NULL ); - if(AcceptSocket != INVALID_SOCKET){ + m_connection = m_tcpserver.Accept(); + if(m_connection.Status() == TcpConnection::CONNECTED){ conn_state = 2; // Accept this! wsk_error = 0; // For now } } // Otherwise loop and try again. break; - case 2: // CONNECTED - int bytesSent,bytesRecv; - - bytesSent = send(AcceptSocket, (char *)tx_data, tx_size, 0 ); - if(bytesSent == SOCKET_ERROR){ - long errnumber = WSAGetLastError(); - switch(errnumber){ - // KNOWN CODES that we can ignore - case 10035: // Operation Would Block - // We can ignore this entirely. It's not an error. - break; - - case 10038: // Socket isn't a socket - case 10053: // Software caused connection abort - case 10054: // Connection reset by peer - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; - - default: // If unknown - wsk_error = 1; // do this - sprintf(wsk_emsg,"TELECOM: send() failed: %ld",errnumber); - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; - } + case 2: // CONNECTED + switch (m_connection.Send((char*)tx_data, tx_size)) { + case TcpConnection::CONNECTION_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + break; + case TcpConnection::UNEXPECTED_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + wsk_error = 1; + sprintf(wsk_emsg, "CSM-TELECOM: Send failed : %d", m_connection.ErrorCode()); + break; + default: + break; } - // Should we recieve? + + // Should we receive? if ((fabs(simt - last_rx) / 0.005) < 1 || sat->agc.InterruptPending(ApolloGuidance::Interrupt::UPRUPT)) { return; // No } last_rx = simt; - bytesRecv = recv( AcceptSocket, (char *)(rx_data+rx_offset), 1, 0 ); - if(bytesRecv == SOCKET_ERROR){ - long errnumber = WSAGetLastError(); - switch(errnumber){ - // KNOWN CODES that we can ignore - case 10035: // Operation Would Block - // We can ignore this entirely. It's not an error. - break; - - case 10053: // Software caused connection abort - case 10038: // Socket isn't a socket - case 10054: // Connection reset by peer - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; - - default: // If unknown - wsk_error = 1; // do this - sprintf(wsk_emsg,"TELECOM: recv() failed: %ld",errnumber); - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; + TcpConnection::CommandStatus status = m_connection.RecvChar((char *)(rx_data+rx_offset)); + if(status != TcpConnection::OK){ + switch (status) { + case TcpConnection::CONNECTION_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + break; + case TcpConnection::UNEXPECTED_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + wsk_error = 1; + sprintf(wsk_emsg, "CSM-TELECOM: Recv failed : %d", m_connection.ErrorCode()); + break; + default: + break; } + // Do we have data from MCC instead? if (mcc_size > 0) { // Yes. Take a byte @@ -5079,28 +5022,12 @@ void PCM::perform_io(double simt){ mcc_offset = mcc_size = 0; } } - }else{ + } else { // If the telemetry data-path is disconnected - if(sat->UPTLMSwitch1.GetState() == TOGGLESWITCH_DOWN){ + if(sat->UPTLMSwitch1.GetState() == TOGGLESWITCH_DOWN) { return; // Discard the data } - if(bytesRecv > 0){ - handle_uplink(); - } else { - // Do we have data from MCC instead? - if (mcc_size > 0) { - // Yes. Take a byte - rx_data[rx_offset] = mcc_data[mcc_offset]; - mcc_offset++; - // Handle it - handle_uplink(); - // Are we done? - if (mcc_offset >= mcc_size) { - // We reached the end of the MCC buffer. - mcc_offset = mcc_size = 0; - } - } - } + handle_uplink(); } break; } diff --git a/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.h b/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.h index 33dbbf0e1c..516da9ab6d 100644 --- a/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.h +++ b/Orbitersdk/samples/ProjectApollo/src_csm/csm_telecom.h @@ -25,6 +25,7 @@ #include "RF_calc.h" #include "paCBGmessageID.h" +#include "socket.h" /* PCM DOWN-TELEMETRY @@ -342,11 +343,9 @@ class PCM { void TimeStep(double simt); // TimeStep void SystemTimestep(double simdt); // System Timestep (consume power) - // Winsock2 - WSADATA wsaData; // Winsock subsystem data - SOCKET m_socket; // TCP socket - sockaddr_in service; // SOCKADDR_IN - SOCKET AcceptSocket; // Accept Socket + // Network + TcpService m_tcpserver; + TcpConnection m_connection; int conn_state; // Connection State int uplink_state; // Uplink State void perform_io(double simt); // Get data from here to there diff --git a/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.cpp b/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.cpp index ce224a30eb..2bb5ecec3b 100644 --- a/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.cpp @@ -27,7 +27,6 @@ #include "Orbitersdk.h" #include #include -#include // TODO: Replace with winsock2 when yaAGC updates #include "lmresource.h" #include "nasspdefs.h" @@ -374,16 +373,10 @@ LM_PCM::LM_PCM() last_rx = 0; frame_addr = 0; frame_count = 0; - m_socket = INVALID_SOCKET; } LM_PCM::~LM_PCM() { - // Close telemetry socket - if (m_socket != INVALID_SOCKET) { - shutdown(m_socket, 2); // Shutdown both streams - closesocket(m_socket); - } } void LM_PCM::Init(LEM *vessel, h_HeatLoad *pcmh) @@ -397,48 +390,20 @@ void LM_PCM::Init(LEM *vessel, h_HeatLoad *pcmh) last_update = 0; last_rx = MINUS_INFINITY; word_addr = 0; - int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (iResult != NO_ERROR) { - sprintf(wsk_emsg, "LM-TELECOM: Error at WSAStartup()"); + if (!NetStartup()) { + sprintf(wsk_emsg, "LM-TELECOM: Error at Startup()"); wsk_error = 1; return; } - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - sprintf(wsk_emsg, "LM-TELECOM: Error at socket(): %ld", WSAGetLastError()); - WSACleanup(); - wsk_error = 1; - return; - } - // Be nonblocking - int iMode = 1; // 0 = BLOCKING, 1 = NONBLOCKING - if (ioctlsocket(m_socket, FIONBIO, (u_long FAR*) &iMode) != 0) { - sprintf(wsk_emsg, "LM-TELECOM: ioctlsocket() failed: %ld", WSAGetLastError()); - wsk_error = 1; - closesocket(m_socket); - WSACleanup(); - return; - } - // Set up incoming options - service.sin_family = AF_INET; - service.sin_addr.s_addr = htonl(INADDR_ANY); - service.sin_port = htons(14243); // CM on 14242, LM on 14243 - - if (::bind(m_socket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) { - sprintf(wsk_emsg, "Failed to start LM telemetry. Please completely exit Orbiter and restart. Please file a bug report if this message persists."); + m_tcpserver = TcpService(14243); // CM on 14242, LM on 14243 + if (m_tcpserver.Status() != TcpService::STARTED) { + sprintf(wsk_emsg, "LM-TELECOM: Error at socket(): %ld", m_tcpserver.ErrorCode()); + NetCleanup(); wsk_error = 1; - closesocket(m_socket); - WSACleanup(); - return; - } - if (listen(m_socket, 1) == SOCKET_ERROR) { - wsk_error = 1; - sprintf(wsk_emsg, "LM-TELECOM: listen() failed: %ld", WSAGetLastError()); - closesocket(m_socket); - WSACleanup(); return; } + conn_state = 1; // INITIALIZED, LISTENING uplink_state = 0; rx_offset = 0; } @@ -570,73 +535,52 @@ void LM_PCM::perform_io(double simt){ } else { // Try to accept - AcceptSocket = accept(m_socket, NULL, NULL); - if (AcceptSocket != INVALID_SOCKET) { + m_connection = m_tcpserver.Accept(); + if (m_connection.Status() == TcpConnection::CONNECTED) { conn_state = 2; // Accept this! wsk_error = 0; // For now } } // Otherwise loop and try again. break; - case 2: // CONNECTED - int bytesSent,bytesRecv; - - bytesSent = send(AcceptSocket, (char *)tx_data, tx_size, 0 ); - if(bytesSent == SOCKET_ERROR){ - long errnumber = WSAGetLastError(); - switch(errnumber){ - // KNOWN CODES that we can ignore - case 10035: // Operation Would Block - // We can ignore this entirely. It's not an error. - break; - - case 10038: // Socket isn't a socket - case 10053: // Software caused connection abort - case 10054: // Connection reset by peer - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; - - default: // If unknown - wsk_error = 1; // do this - sprintf(wsk_emsg,"LM-TELECOM: send() failed: %ld",errnumber); - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; - } + case 2: // CONNECTED + switch (m_connection.Send((char*)tx_data, tx_size)) { + case TcpConnection::CONNECTION_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + break; + case TcpConnection::UNEXPECTED_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + wsk_error = 1; + sprintf(wsk_emsg, "LM-TELECOM: Send failed : %d", m_connection.ErrorCode()); + break; + default: + break; } + // Should we receive? if (((simt - last_rx) / 0.005) < 1 || lem->agc.InterruptPending(ApolloGuidance::Interrupt::UPRUPT)) { return; // No } last_rx = simt; - bytesRecv = recv( AcceptSocket, (char *)(rx_data+rx_offset), 1, 0 ); - if(bytesRecv == SOCKET_ERROR){ - long errnumber = WSAGetLastError(); - switch(errnumber){ - // KNOWN CODES that we can ignore - case 10035: // Operation Would Block - // We can ignore this entirely. It's not an error. - break; - - case 10053: // Software caused connection abort - case 10038: // Socket isn't a socket - case 10054: // Connection reset by peer - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; - - default: // If unknown - wsk_error = 1; // do this - sprintf(wsk_emsg,"LM-TELECOM: recv() failed: %ld",errnumber); - closesocket(AcceptSocket); - conn_state = 1; // Accept another - uplink_state = 0; rx_offset = 0; - break; + TcpConnection::CommandStatus status = m_connection.RecvChar((char*)(rx_data + rx_offset)); + if (status != TcpConnection::OK) { + switch (status) { + case TcpConnection::CONNECTION_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + break; + case TcpConnection::UNEXPECTED_ERROR: + conn_state = 1; // Accept another + uplink_state = 0; rx_offset = 0; + wsk_error = 1; + sprintf(wsk_emsg, "LM-TELECOM: Recv failed : %d", m_connection.ErrorCode()); + break; + default: + break; } + // Do we have data from MCC instead? if (mcc_size > 0) { // Yes. Take a byte @@ -653,31 +597,13 @@ void LM_PCM::perform_io(double simt){ mcc_offset = mcc_size = 0; } } - }else{ + } else { // FIXME: Check to make sure the up-data equipment is powered // Reject uplink if switch is not down. if(lem->COMM_UP_DATA_LINK_CB.IsPowered() == false || lem->Panel12UpdataLinkSwitch.GetState() != THREEPOSSWITCH_DOWN){ return; // Discard the data } - if(bytesRecv > 0){ - handle_uplink(); - } - else - { - // Do we have data from MCC instead? - if (mcc_size > 0) { - // Yes. Take a byte - rx_data[rx_offset] = mcc_data[mcc_offset]; - mcc_offset++; - // Handle it - handle_uplink(); - // Are we done? - if (mcc_offset >= mcc_size) { - // We reached the end of the MCC buffer. - mcc_offset = mcc_size = 0; - } - } - } + handle_uplink(); } break; } diff --git a/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.h b/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.h index f80c32a32b..32ecfe4ddc 100644 --- a/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.h +++ b/Orbitersdk/samples/ProjectApollo/src_lm/lm_telecom.h @@ -24,6 +24,7 @@ #include "RF_calc.h" #include "paCBGmessageID.h" +#include "socket.h" /* PCM DOWN-TELEMETRY @@ -251,11 +252,9 @@ class LM_PCM LEM *lem; // Ship we're installed in h_HeatLoad *PCMHeat; //PCM Heat Load - // Winsock2 - WSADATA wsaData; // Winsock subsystem data - SOCKET m_socket; // TCP socket - sockaddr_in service; // SOCKADDR_IN - SOCKET AcceptSocket; // Accept Socket + // Network + TcpService m_tcpserver; + TcpConnection m_connection; int conn_state; // Connection State int uplink_state; // Uplink State void perform_io(double simt); // Get data from here to there diff --git a/Orbitersdk/samples/ProjectApollo/src_mfd/ProjectApolloMFD.cpp b/Orbitersdk/samples/ProjectApollo/src_mfd/ProjectApolloMFD.cpp index f23f650731..06aa7b5f42 100644 --- a/Orbitersdk/samples/ProjectApollo/src_mfd/ProjectApolloMFD.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_mfd/ProjectApolloMFD.cpp @@ -49,6 +49,7 @@ #include "MFDResource.h" #include "ProjectApolloMFD.h" +#include "socket.h" #include @@ -101,10 +102,7 @@ static struct ProjectApolloMFDData { // global data storage int killrot; } g_Data; -static WSADATA wsaData; -static SOCKET m_socket; -static sockaddr_in clientService; -static SOCKET close_Socket = INVALID_SOCKET; +static TcpConnection m_socket; static char debugString[100]; static char debugStringBuffer[100]; static char debugWinsock[100]; @@ -136,12 +134,11 @@ void ProjectApolloMFDopcDLLInit (HINSTANCE hDLL) //Init Emem for(int i = 0; i < 24; i++) g_Data.emem[i] = 0; - int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); - if ( iResult != NO_ERROR ) { - sprintf(debugWinsock,"ERROR AT WSAStartup()"); + if (!NetStartup()) { + sprintf(debugWinsock, "ERROR AT NetStartup()"); } else { - sprintf(debugWinsock,"DISCONNECTED"); + sprintf(debugWinsock, "DISCONNECTED"); } g_Data.uplinkBufferSimt = 0; g_Data.V42angles = _V(0, 0, 0); @@ -351,24 +348,15 @@ void uplink_aeaa_cmd(bool arm, bool set) void UplinkLMRTC(bool arm, bool set) { if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; + if (g_Data.uplinkLEM > 0) { + m_socket = TcpConnection("127.0.0.1", 14243); + } + else { + m_socket = TcpConnection("127.0.0.1", 14242); } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); - if (g_Data.uplinkLEM > 0) { clientService.sin_port = htons(14243); } - else { clientService.sin_port = htons(14242); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + if (m_socket.Status() != TcpConnection::CONNECTED) { g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } sprintf(debugWinsock, "CONNECTED"); @@ -381,24 +369,16 @@ void UplinkLMRTC(bool arm, bool set) void UplinkData() { if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; char buffer[8]; - m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - if ( m_socket == INVALID_SOCKET ) { - g_Data.uplinkDataReady = 0; - sprintf(debugWinsock,"ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; + if(g_Data.uplinkLEM > 0) { + m_socket = TcpConnection("127.0.0.1", 14243); } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); - if(g_Data.uplinkLEM > 0){ clientService.sin_port = htons( 14243 ); }else{ clientService.sin_port = htons( 14242 ); } - if (connect( m_socket, (SOCKADDR*) &clientService, sizeof(clientService)) == SOCKET_ERROR) { + else { + m_socket = TcpConnection("127.0.0.1", 14242); + } + if (m_socket.Status() != TcpConnection::CONNECTED) { g_Data.uplinkDataReady = 0; - sprintf(debugWinsock,"FAILED TO CONNECT, ERROR %ld",WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } sprintf(debugWinsock, "CONNECTED"); @@ -429,27 +409,19 @@ void UpdateClock() { if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; char buffer[8]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - g_Data.updateClockReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; + if (g_Data.uplinkLEM > 0) { + m_socket = TcpConnection("127.0.0.1", 14243); } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); - if (g_Data.uplinkLEM > 0) { clientService.sin_port = htons(14243); } - else { clientService.sin_port = htons(14242); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + else { + m_socket = TcpConnection("127.0.0.1", 14242); + } + if (m_socket.Status() != TcpConnection::CONNECTED) { g_Data.updateClockReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } + sprintf(debugWinsock, "CONNECTED"); g_Data.uplinkState = 0; send_agc_key('V'); @@ -530,24 +502,15 @@ void UplinkSunburstSuborbitalAbort() if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; + if (g_Data.uplinkLEM > 0) { + m_socket = TcpConnection("127.0.0.1", 14243); + } + else { + m_socket = TcpConnection("127.0.0.1", 14242); } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); - if (g_Data.uplinkLEM > 0) { clientService.sin_port = htons(14243); } - else { clientService.sin_port = htons(14242); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + if (m_socket.Status() != TcpConnection::CONNECTED) { g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } sprintf(debugWinsock, "CONNECTED"); @@ -565,24 +528,15 @@ void UplinkSunburstCOI() if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; + if (g_Data.uplinkLEM > 0) { + m_socket = TcpConnection("127.0.0.1", 14243); + } + else { + m_socket = TcpConnection("127.0.0.1", 14242); } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); - if (g_Data.uplinkLEM > 0) { clientService.sin_port = htons(14243); } - else { clientService.sin_port = htons(14242); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + if (m_socket.Status() != TcpConnection::CONNECTED) { g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } sprintf(debugWinsock, "CONNECTED"); @@ -599,7 +553,7 @@ void ProjectApolloMFDopcTimestep (double simt, double simdt, double mjd) if (g_Data.connStatus > 0 && g_Data.uplinkBuffer.size() > 0) { if (simt > g_Data.uplinkBufferSimt + 0.1) { unsigned char data = g_Data.uplinkBuffer.front(); - send(m_socket, (char *) &data, 1, 0); + m_socket.Send((char *) &data, 1); g_Data.uplinkBuffer.pop(); g_Data.uplinkBufferSimt = simt; } @@ -609,7 +563,7 @@ void ProjectApolloMFDopcTimestep (double simt, double simdt, double mjd) g_Data.uplinkDataReady = 0; g_Data.updateClockReady = 0; g_Data.connStatus = 0; - closesocket(m_socket); + m_socket.Close(); } else if (g_Data.connStatus == 2 && g_Data.updateClockReady == 2) { UpdateClock(); } diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp index ad63290a39..fc78018112 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp @@ -15,13 +15,11 @@ #include "TLMCC.h" #include "rtcc.h" #include "nassputils.h" +#include "socket.h" using namespace nassp; -static WSADATA wsaData; -static SOCKET m_socket; -static sockaddr_in clientService; -static SOCKET close_Socket = INVALID_SOCKET; +static TcpConnection m_socket; static char debugString[100]; static char debugStringBuffer[100]; static char debugWinsock[100]; @@ -475,9 +473,9 @@ ARCore::ARCore(VESSEL* v, AR_GCore* gcin) { g_Data.emem[i] = 0; } - int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (iResult != NO_ERROR) { - sprintf(debugWinsock, "ERROR AT WSAStartup()"); + + if(!NetStartup()) { + sprintf(debugWinsock, "ERROR AT NetStartup()"); } else { sprintf(debugWinsock, "DISCONNECTED"); @@ -817,7 +815,7 @@ void ARCore::MinorCycle(double SimT, double SimDT, double mjd) if (g_Data.connStatus > 0 && g_Data.uplinkBuffer.size() > 0) { if (SimT > g_Data.uplinkBufferSimt + 0.1) { unsigned char data = g_Data.uplinkBuffer.front(); - send(m_socket, (char *)&data, 1, 0); + m_socket.Send((char *)&data, 1); g_Data.uplinkBuffer.pop(); g_Data.uplinkBufferSimt = SimT; } @@ -826,7 +824,7 @@ void ARCore::MinorCycle(double SimT, double SimDT, double mjd) if (g_Data.connStatus == 1) { sprintf(debugWinsock, "DISCONNECTED"); g_Data.connStatus = 0; - closesocket(m_socket); + m_socket.Close(); } } } @@ -2145,31 +2143,18 @@ void ARCore::AP12AbortCoefUplink() void ARCore::UplinkData(bool isCSM) { if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; char buffer[8]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - //g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; - } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); if (isCSM) { - clientService.sin_port = htons(14242); + m_socket = TcpConnection("127.0.0.1", 14242); } else { - clientService.sin_port = htons(14243); + m_socket = TcpConnection("127.0.0.1", 14243); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + if (m_socket.Status() != TcpConnection::CONNECTED) { //g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } sprintf(debugWinsock, "CONNECTED"); @@ -2201,33 +2186,21 @@ void ARCore::UplinkData(bool isCSM) void ARCore::UplinkData2(bool isCSM) { if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; char buffer[8]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - //g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; - } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); if (isCSM) { - clientService.sin_port = htons(14242); + m_socket = TcpConnection("127.0.0.1", 14242); } else { - clientService.sin_port = htons(14243); + m_socket = TcpConnection("127.0.0.1", 14243); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + if (m_socket.Status() != TcpConnection::CONNECTED) { //g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } + sprintf(debugWinsock, "CONNECTED"); g_Data.uplinkState = 0; send_agc_key('V', isCSM); @@ -2257,33 +2230,21 @@ void ARCore::UplinkData2(bool isCSM) void ARCore::UplinkDataV70V73(bool v70, bool isCSM) { if (g_Data.connStatus == 0) { - int bytesRecv = SOCKET_ERROR; - char addr[256]; char buffer[8]; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == INVALID_SOCKET) { - //g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "ERROR AT SOCKET(): %ld", WSAGetLastError()); - closesocket(m_socket); - return; - } - sprintf(addr, "127.0.0.1"); - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr(addr); if (isCSM) { - clientService.sin_port = htons(14242); + m_socket = TcpConnection("127.0.0.1", 14242); } else { - clientService.sin_port = htons(14243); + m_socket = TcpConnection("127.0.0.1", 14243); } - if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + if (m_socket.Status() != TcpConnection::CONNECTED) { //g_Data.uplinkDataReady = 0; - sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", WSAGetLastError()); - closesocket(m_socket); + sprintf(debugWinsock, "FAILED TO CONNECT, ERROR %ld", m_socket.ErrorCode()); return; } + sprintf(debugWinsock, "CONNECTED"); g_Data.uplinkState = 0; send_agc_key('V', isCSM); diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp new file mode 100644 index 0000000000..f955584964 --- /dev/null +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp @@ -0,0 +1,287 @@ +/*************************************************************************** + This file is part of Project Apollo - NASSP + Copyright 2023 + + Project Apollo is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Project Apollo is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Project Apollo; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + See http://nassp.sourceforge.net/license/ for more details. + + **************************************************************************/ + +#include "socket.h" + +#ifdef __unix +#include +#include +#include +#include +#include +#include +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define SOCKADDR sockaddr +#define closesocket close +#define ioctlsocket ioctl +#endif + +static int NativeError() { +#ifdef _WIN32 + return WSAGetLastError(); +#elif __unix + return errno; +#endif +} + +bool NetStartup() { +#ifdef _WIN32 + WSADATA wsaData; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + return (iResult == NO_ERROR); +#else + return true; +#endif +} +void NetCleanup() { +#ifdef _WIN32 + WSACleanup(); +#endif +} + +TcpConnection::TcpConnection() { + m_error = 0; + m_status = ConnectionStatus::UNINITIALIZED; + m_socket = INVALID_SOCKET; +} + +TcpConnection::TcpConnection(const char *dst, uint16_t tcpport) { + m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == INVALID_SOCKET) { + m_status = ConnectionStatus::UNINITIALIZED; + m_error = NativeError(); + return; + } + + sockaddr_in clientService; + clientService.sin_family = AF_INET; + clientService.sin_addr.s_addr = inet_addr(dst); + clientService.sin_port = htons(tcpport); + + if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { + m_status = ConnectionStatus::UNINITIALIZED; + m_error = NativeError(); + Close(); + return; + } + m_status = ConnectionStatus::CONNECTED; +} + +TcpConnection::TcpConnection(TcpConnection&& from) noexcept { + m_error = from.m_error; + m_status = from.m_status; + m_socket = from.m_socket; + from.m_socket = INVALID_SOCKET; + from.m_status = ConnectionStatus::UNINITIALIZED; +} + +TcpConnection& TcpConnection::operator=(TcpConnection&& from) noexcept { + Close(); + m_error = from.m_error; + m_status = from.m_status; + m_socket = from.m_socket; + from.m_socket = INVALID_SOCKET; + from.m_status = ConnectionStatus::UNINITIALIZED; + return *this; +} + +TcpConnection::~TcpConnection() { + Close(); +} + +TcpConnection::CommandStatus TcpConnection::HandleError() noexcept { + m_error = NativeError(); + switch (m_error) { +#ifdef _WIN32 + // KNOWN CODES that we can ignore + case 10035: // Operation Would Block + // We can ignore this entirely. It's not an error. + m_error = 0; + return CommandStatus::RETRY; + + case 10038: // Socket isn't a socket + case 10053: // Software caused connection abort + case 10054: // Connection reset by peer + Close(); + return CommandStatus::CONNECTION_ERROR; +#elif __unix + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + case EINTR: + m_error = 0; + return CommandStatus::RETRY; + case EBADF: + case ECONNRESET: + case ENOTCONN: + case ENOTSOCK: + case EPIPE: + Close(); + return CommandStatus::CONNECTION_ERROR; +#else +#error Platform not supported +#endif + default: // If unknown + Close(); + return CommandStatus::UNEXPECTED_ERROR; + } +} + +TcpConnection::CommandStatus TcpConnection::Send(const char* buf, size_t len) noexcept { + m_error = 0; + int bytesSent = send(m_socket, buf, len, 0); + if (bytesSent == SOCKET_ERROR) { + return HandleError(); + } + return OK; +} + +TcpConnection::CommandStatus TcpConnection::RecvChar(char* buf) noexcept { + int bytesRecv = recv(m_socket, buf, 1, 0); + if (bytesRecv == SOCKET_ERROR) { + return HandleError(); + } + m_error = 0; + return OK; +} + +void TcpConnection::Close() noexcept { + if (m_socket != INVALID_SOCKET) { + shutdown(m_socket, 2); // Shutdown both streams + closesocket(m_socket); + m_socket = INVALID_SOCKET; + } + m_status = ConnectionStatus::UNINITIALIZED; +} + +TcpConnection::TcpConnection(NativeSocket s) { + m_error = 0; + m_socket = s; + m_status = ConnectionStatus::CONNECTED; +} + + +TcpService::TcpService() { + m_status = ServiceStatus::DISABLED; +} + +TcpService::TcpService(TcpService&& from) noexcept { + m_status = from.m_status; + m_socket = from.m_socket; + m_error = from.m_error; + from.m_status = TcpService::DISABLED; + from.m_socket = INVALID_SOCKET; +} + +TcpService& TcpService::operator=(TcpService&& from) noexcept { + m_status = from.m_status; + m_socket = from.m_socket; + m_error = from.m_error; + from.m_status = TcpService::DISABLED; + from.m_socket = INVALID_SOCKET; + return *this; +} + +TcpService::~TcpService() { + if (m_socket != INVALID_SOCKET) { + closesocket(m_socket); + } +} + +TcpService::TcpService(uint16_t tcpport) { + m_status = ServiceStatus::DISABLED; + + sockaddr_in service; + m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == INVALID_SOCKET) { + m_error = NativeError(); + NetCleanup(); + return; + } + // Be nonblocking + int iMode = 1; // 0 = BLOCKING, 1 = NONBLOCKING + if (ioctlsocket(m_socket, FIONBIO, (u_long *) & iMode) != 0) { + m_error = NativeError(); + closesocket(m_socket); + NetCleanup(); + return; + } + + // Set up incoming options + service.sin_family = AF_INET; + service.sin_addr.s_addr = htonl(INADDR_ANY); + service.sin_port = htons(tcpport); + + if (bind(m_socket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) { + m_error = NativeError(); + closesocket(m_socket); + NetCleanup(); + return; + } + if (listen(m_socket, 1) == SOCKET_ERROR) { + m_error = NativeError(); + closesocket(m_socket); + NetCleanup(); + return; + } + + m_status = ServiceStatus::STARTED; + return; +} + +TcpConnection TcpService::Accept() noexcept { + NativeSocket s = accept(m_socket, NULL, NULL); + if (s == INVALID_SOCKET) { + m_error = NativeError(); + return TcpConnection(); + } + else { + m_error = 0; + return TcpConnection(s); + } +} + +#if 0 +// TcpService::Accept can be modified to investigate performance impact with time acceleration +#include +TcpConnection TcpService::Accept() noexcept { + static std::chrono::time_point time_prev = std::chrono::steady_clock::now(); + std::chrono::time_point time_now = std::chrono::steady_clock::now(); + + // Limit accept call to 10Hz real-time + if (time_now - time_prev > std::chrono::microseconds(100000)) { + NativeSocket s = accept(m_socket, NULL, NULL); + time_prev = time_now; + if (s == INVALID_SOCKET) { + m_error = NativeError(); + return TcpConnection(); + } + else { + m_error = 0; + return TcpConnection(s); + } + } + return TcpConnection(); +} +#endif diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.h b/Orbitersdk/samples/ProjectApollo/src_sys/socket.h new file mode 100644 index 0000000000..120d3ff006 --- /dev/null +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.h @@ -0,0 +1,149 @@ +/*************************************************************************** + This file is part of Project Apollo - NASSP + Copyright 2023 + + Project Apollo is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Project Apollo is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Project Apollo; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + See http://nassp.sourceforge.net/license/ for more details. + + **************************************************************************/ +#pragma once +#include +#include + +#ifdef _WIN32 +#include +using NativeSocket = SOCKET; +#elif __unix +using NativeSocket = int; +#else +#error Platform not supported +#endif + +// Initialize the network subsystem +bool NetStartup(); + +// Close the network subsystem +void NetCleanup(); + +class TcpService; + +// Move only class that encapsulates a non blocking TCP socket used for reading/writing +// Can be created directly by specifying a destination and port to connect to +// or indirectly by a TcpService instance +class TcpConnection { + friend TcpService; +public: + typedef enum { + UNINITIALIZED, + CONNECTED + } ConnectionStatus; + + typedef enum { + OK, + RETRY, + CONNECTION_ERROR, + UNEXPECTED_ERROR + } CommandStatus; + + // Create a default UNINITIALIZED TcpConnection + TcpConnection(); + + // Tries to connect to the specified address/port and updates m_status and m_error accordingly + TcpConnection(const char *dst, uint16_t tcpport); + + // Move only semantics + TcpConnection(const TcpConnection&) = delete; + TcpConnection& operator=(TcpConnection const&) = delete; + TcpConnection(TcpConnection&& from) noexcept; + TcpConnection& operator=(TcpConnection&& from) noexcept; + + // Close() is called upon destruction + ~TcpConnection(); + ConnectionStatus Status() noexcept { return m_status; } + + // Tries to send len bytes to the other peer + // Return value: + // - OK: the data was sent successfully + // - CONNECTION_ERROR: the connection was closed + // - UNEXPECTED_ERROR: an unexpected error occured + // - RETRY : no data could be sent without blocking + // m_status and m_error are updated accordingly + CommandStatus Send(const char* buf, size_t len) noexcept; + + // Tries to read one byte from the other peer + // Return value: + // - OK: the data was read successfully + // - CONNECTION_ERROR: the connection was closed + // - UNEXPECTED_ERROR: an unexpected error occured + // - RETRY : no data was available + // m_status and m_error are updated accordingly + CommandStatus RecvChar(char* buf) noexcept; + + int ErrorCode() noexcept { return m_error; } + + // Close the connection and sets its status to UNINITIALIZED + void Close() noexcept; + +private: + // Used by TcpService + TcpConnection(NativeSocket s); + + CommandStatus HandleError() noexcept; + + ConnectionStatus m_status; + NativeSocket m_socket; + int m_error; +}; + +// Move only class that encapsulates a TCP "server" socket +// Only one client is accepted in the socket queue +class TcpService { +public: + typedef enum { + DISABLED, + STARTED + } ServiceStatus; + + // Create a default DISABLED TcpService + TcpService(); + + // Create a new service listening on all interfaces on tcpport + // Sets m_status and m_error accordingly + TcpService(uint16_t tcpport); + + // Move only semantics + TcpService(const TcpService&) = delete; + TcpService& operator=(TcpService const&) = delete; + TcpService(TcpService&& from) noexcept; + TcpService& operator=(TcpService&& from) noexcept; + + // The socket is closed on destruction + ~TcpService(); + + // Accepts a new connection if a client is trying to connect to the service (non blocking) + // Returns: + // - a CONNECTED TcpConnection if a client connected to the service + // - an UNINITIALIZED TcpConnection if there was no client + TcpConnection Accept() noexcept; + + ServiceStatus Status() noexcept { return m_status; }; + int ErrorCode() noexcept { return m_error; } + +private: + ServiceStatus m_status; + NativeSocket m_socket; + int m_error; +};