From ffed41d99202d32e93d094e9b75e5550c938bca5 Mon Sep 17 00:00:00 2001 From: Gondos Date: Fri, 27 Jan 2023 17:46:28 +0100 Subject: [PATCH 1/4] Refactor network code --- .../Build/VC2017/ApolloRTCCMFD.vcxproj | 2 + .../VC2017/ApolloRTCCMFD.vcxproj.filters | 6 + .../ProjectApollo/Build/VC2017/LEM.vcxproj | 1 + .../Build/VC2017/LEM.vcxproj.filters | 3 + .../Build/VC2017/ProjectApolloMFD.vcxproj | 2 + .../VC2017/ProjectApolloMFD.vcxproj.filters | 6 + .../Build/VC2017/Saturn1b.vcxproj | 1 + .../Build/VC2017/Saturn1b.vcxproj.filters | 3 + .../Build/VC2017/Saturn5NASP.vcxproj | 2 + .../Build/VC2017/Saturn5NASP.vcxproj.filters | 6 + .../ProjectApollo/src_csm/csm_telecom.cpp | 157 ++++-------- .../ProjectApollo/src_csm/csm_telecom.h | 9 +- .../ProjectApollo/src_lm/lm_telecom.cpp | 158 ++++-------- .../samples/ProjectApollo/src_lm/lm_telecom.h | 9 +- .../src_mfd/ProjectApolloMFD.cpp | 132 ++++------ .../ProjectApollo/src_rtccmfd/ARCore.cpp | 81 ++---- .../samples/ProjectApollo/src_sys/socket.cpp | 235 ++++++++++++++++++ .../samples/ProjectApollo/src_sys/socket.h | 142 +++++++++++ 18 files changed, 565 insertions(+), 390 deletions(-) create mode 100644 Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp create mode 100644 Orbitersdk/samples/ProjectApollo/src_sys/socket.h diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj index 543f5cac68..c758183c1e 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj @@ -37,6 +37,7 @@ + @@ -65,6 +66,7 @@ + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/ApolloRTCCMFD.vcxproj.filters index 367ce3f974..6479c6d51d 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 + @@ -173,5 +176,8 @@ Source Files + + Source Files + \ No newline at end of file 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 281b2af6b7..e3fb157c08 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn1b.vcxproj @@ -383,6 +383,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 c1224d91f8..17af76ac1f 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 + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj index fc02439f95..5cd77143c6 100644 --- a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj +++ b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj @@ -343,6 +343,7 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) @@ -467,6 +468,7 @@ + diff --git a/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj.filters b/Orbitersdk/samples/ProjectApollo/Build/VC2017/Saturn5NASP.vcxproj.filters index 15459f7de7..cf585bd902 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 + @@ -461,6 +464,9 @@ 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 d015ba3495..86075eb112 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" @@ -2036,15 +2035,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){ @@ -2056,46 +2050,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; } @@ -5019,73 +4983,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 @@ -5102,28 +5045,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 70b1450198..4faee5ac08 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 8ca204b7b8..eeb96536bb 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" @@ -371,16 +370,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) @@ -394,48 +387,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; } @@ -567,73 +532,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 @@ -650,31 +594,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 9862fb53de..8cc610af7b 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 b49ae7d26d..aaf21ba09a 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 c7f0f8e954..83221bed93 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"); @@ -827,7 +825,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; } @@ -836,7 +834,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(); } } } @@ -2150,31 +2148,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"); @@ -2206,33 +2191,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); @@ -2262,33 +2235,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..f1ad05ca25 --- /dev/null +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** + 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" +#include + +bool NetStartup() { +#ifdef _WIN32 + WSADATA wsaData; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + return (iResult == NO_ERROR); +#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 = WSAGetLastError(); + 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 = WSAGetLastError(); + 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::Send(const char* buf, size_t len) noexcept { + m_error = 0; + int bytesSent = send(m_socket, buf, len, 0); + if (bytesSent == SOCKET_ERROR) { + m_error = WSAGetLastError(); + switch (m_error) { + // KNOWN CODES that we can ignore + case 10035: // Operation Would Block + // We can ignore this entirely. It's not an error. + 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; + + default: // If unknown + Close(); + return CommandStatus::UNEXPECTED_ERROR; + break; + } + } + return OK; +} + +TcpConnection::CommandStatus TcpConnection::RecvChar(char* buf) noexcept { + int bytesRecv = recv(m_socket, buf, 1, 0); + assert(bytesRecv != 0); + if (bytesRecv == SOCKET_ERROR) { + m_error = WSAGetLastError(); + switch (m_error) { + // 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 RETRY; + + case 10053: // Software caused connection abort + case 10038: // Socket isn't a socket + case 10054: // Connection reset by peer + Close(); + return CommandStatus::CONNECTION_ERROR; + + default: // If unknown + Close(); + return CommandStatus::UNEXPECTED_ERROR; + } + } + 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 = WSAGetLastError(); + NetCleanup(); + return; + } + // Be nonblocking + int iMode = 1; // 0 = BLOCKING, 1 = NONBLOCKING + if (ioctlsocket(m_socket, FIONBIO, (u_long FAR*) & iMode) != 0) { + m_error = WSAGetLastError(); + 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 = WSAGetLastError(); + closesocket(m_socket); + NetCleanup(); + return; + } + if (listen(m_socket, 1) == SOCKET_ERROR) { + m_error = WSAGetLastError(); + 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 = WSAGetLastError(); + return TcpConnection(); + } + else { + m_error = 0; + return TcpConnection(s); + } +} diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.h b/Orbitersdk/samples/ProjectApollo/src_sys/socket.h new file mode 100644 index 0000000000..a2fbed12a9 --- /dev/null +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.h @@ -0,0 +1,142 @@ +/*************************************************************************** + 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 + +#ifdef _WIN32 +#include +using NativeSocket = SOCKET; +#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); + + 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; +}; From 114d9609b25e044b31f16e3d4424ab05a676e76a Mon Sep 17 00:00:00 2001 From: Gondos Date: Sun, 5 Feb 2023 14:34:03 +0100 Subject: [PATCH 2/4] Factorize error handling and add unix support for reference --- .../samples/ProjectApollo/src_sys/socket.cpp | 121 +++++++++++------- .../samples/ProjectApollo/src_sys/socket.h | 7 + 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp index f1ad05ca25..5008f5e347 100644 --- a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp @@ -21,13 +21,36 @@ **************************************************************************/ #include "socket.h" -#include + +#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() { @@ -46,7 +69,7 @@ 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 = WSAGetLastError(); + m_error = NativeError(); return; } @@ -57,7 +80,7 @@ TcpConnection::TcpConnection(const char *dst, uint16_t tcpport) { if (connect(m_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) { m_status = ConnectionStatus::UNINITIALIZED; - m_error = WSAGetLastError(); + m_error = NativeError(); Close(); return; } @@ -86,54 +109,57 @@ 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: + 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) { - m_error = WSAGetLastError(); - switch (m_error) { - // KNOWN CODES that we can ignore - case 10035: // Operation Would Block - // We can ignore this entirely. It's not an error. - 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; - - default: // If unknown - Close(); - return CommandStatus::UNEXPECTED_ERROR; - break; - } + return HandleError(); } return OK; } TcpConnection::CommandStatus TcpConnection::RecvChar(char* buf) noexcept { int bytesRecv = recv(m_socket, buf, 1, 0); - assert(bytesRecv != 0); if (bytesRecv == SOCKET_ERROR) { - m_error = WSAGetLastError(); - switch (m_error) { - // 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 RETRY; - - case 10053: // Software caused connection abort - case 10038: // Socket isn't a socket - case 10054: // Connection reset by peer - Close(); - return CommandStatus::CONNECTION_ERROR; - - default: // If unknown - Close(); - return CommandStatus::UNEXPECTED_ERROR; - } + return HandleError(); } m_error = 0; return OK; @@ -154,6 +180,7 @@ TcpConnection::TcpConnection(NativeSocket s) { m_status = ConnectionStatus::CONNECTED; } + TcpService::TcpService() { m_status = ServiceStatus::DISABLED; } @@ -187,14 +214,14 @@ TcpService::TcpService(uint16_t tcpport) { sockaddr_in service; m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (m_socket == INVALID_SOCKET) { - m_error = WSAGetLastError(); + m_error = NativeError(); NetCleanup(); return; } // Be nonblocking int iMode = 1; // 0 = BLOCKING, 1 = NONBLOCKING - if (ioctlsocket(m_socket, FIONBIO, (u_long FAR*) & iMode) != 0) { - m_error = WSAGetLastError(); + if (ioctlsocket(m_socket, FIONBIO, (u_long *) & iMode) != 0) { + m_error = NativeError(); closesocket(m_socket); NetCleanup(); return; @@ -205,14 +232,14 @@ TcpService::TcpService(uint16_t tcpport) { 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 = WSAGetLastError(); + 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 = WSAGetLastError(); + m_error = NativeError(); closesocket(m_socket); NetCleanup(); return; @@ -225,7 +252,7 @@ TcpService::TcpService(uint16_t tcpport) { TcpConnection TcpService::Accept() noexcept { NativeSocket s = accept(m_socket, NULL, NULL); if (s == INVALID_SOCKET) { - m_error = WSAGetLastError(); + m_error = NativeError(); return TcpConnection(); } else { diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.h b/Orbitersdk/samples/ProjectApollo/src_sys/socket.h index a2fbed12a9..120d3ff006 100644 --- a/Orbitersdk/samples/ProjectApollo/src_sys/socket.h +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.h @@ -21,10 +21,15 @@ **************************************************************************/ #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 @@ -96,6 +101,8 @@ class TcpConnection { // Used by TcpService TcpConnection(NativeSocket s); + CommandStatus HandleError() noexcept; + ConnectionStatus m_status; NativeSocket m_socket; int m_error; From ad272860b3757b05b4701947f436eebe91b83eed Mon Sep 17 00:00:00 2001 From: Gondos Date: Sun, 5 Feb 2023 14:43:47 +0100 Subject: [PATCH 3/4] Close on connection error --- Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp index 5008f5e347..328a4212f1 100644 --- a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp @@ -137,6 +137,7 @@ TcpConnection::CommandStatus TcpConnection::HandleError() noexcept { case ENOTCONN: case ENOTSOCK: case EPIPE: + Close(); return CommandStatus::CONNECTION_ERROR; #else #error Platform not supported From e7a75a20347efd149dcf77c00e839f8feceef651 Mon Sep 17 00:00:00 2001 From: Gondos Date: Tue, 7 Feb 2023 13:38:43 +0100 Subject: [PATCH 4/4] Document rate limiting hack for reference --- .../samples/ProjectApollo/src_sys/socket.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp index 328a4212f1..f955584964 100644 --- a/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_sys/socket.cpp @@ -261,3 +261,27 @@ TcpConnection TcpService::Accept() noexcept { 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