diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp new file mode 100644 index 0000000..da17854 --- /dev/null +++ b/xpcPlugin/UDPSocket.cpp @@ -0,0 +1,168 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#include "Log.h" +#include "UDPSocket.h" + +namespace XPC +{ + UDPSocket::UDPSocket(std::uint16_t recvPort) + { +#if LOG_VERBOSITY > 1 + Log::FormatLine("[UDPSocket] Opening socket (port:%d)", recvPort); +#endif + // Setup Port + struct sockaddr_in localAddr; + localAddr.sin_family = AF_INET; + localAddr.sin_addr.s_addr = INADDR_ANY; + localAddr.sin_port = htons(recvPort); + + //Create and bind the socket +#ifdef _WIN32 + WSADATA wsa; + int startResult = WSAStartup(MAKEWORD(2, 2), &wsa); + if (startResult != 0) + { +#if LOG_VERBOSITY > 0 + Log::FormatLine("[UDPSocket] ERROR: WSAStartup failed with error code %i.", startResult); +#endif + return; + } + if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) + { +#if LOG_VERBOSITY > 0 + int err = WSAGetLastError(); + Log::FormatLine("[UDPSocket] ERROR: Failed to open socket. (Error code %i)", err); +#endif + return; + } +#elif (__APPLE__ || __linux) + if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + Log::WriteLine("[UDPSocket] ERROR: Failed to open socket"); + return; + } + int optval = 1; + setsockopt(theSocket.sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + setsockopt(theSocket.sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); +#endif + if (bind(this->sock, (struct sockaddr*)&localAddr, sizeof(localAddr)) != 0) + { +#if LOG_VERBOSITY > 0 + int err = WSAGetLastError(); + Log::FormatLine("[UDPSocket] ERROR: Failed to bind socket. (Error code %i)", err); +#endif + return; + } + + //Set Timout + int usTimeOut = 500; + +#ifdef _WIN32 + DWORD msTimeOutWin = 1; // Minimum socket timeout in Windows is 1ms + if(setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&msTimeOutWin, sizeof(msTimeOutWin)) != 0) + { +#if LOG_VERBOSITY > 1 + int err = WSAGetLastError(); + Log::FormatLine("[UDPSocket] ERROR: Failed to set timeout. (Error code %i)", err); +#endif + } +#else + struct timeval tv; + tv.tv_sec = 0; /* Sec Timeout */ + tv.tv_usec = usTimeOut; // Microsec Timeout + setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); +#endif + } + + UDPSocket::~UDPSocket() + { +#if LOG_VERBOSITY > 1 + Log::FormatLine("[UDPSocket] Closing socket (%d)", this->sock); +#endif +#ifdef _WIN32 + closesocket(this->sock); +#elif (__APPLE__ || __linux) + close(this->sock); +#endif + } + + int UDPSocket::Read(std::uint8_t* dst, int maxLen, struct sockaddr* recvAddr) + { + socklen_t recvaddrlen = sizeof(*recvAddr); + int status = 0; + +#ifdef _WIN32 + // Windows readUDP needs the select command- minimum timeout is 1ms. + // Without this playback becomes choppy + + // Definitions + FD_SET stReadFDS; + FD_SET stExceptFDS; + struct timeval tv; + struct sockaddr_in sourceAddr; + + // Setup for Select + FD_ZERO(&stReadFDS); + FD_SET(sock, &stReadFDS); + FD_ZERO(&stExceptFDS); + FD_SET(sock, &stExceptFDS); + tv.tv_sec = 0; /* Sec Timeout */ + tv.tv_usec = 250; // Microsec Timeout + + // Select Command + int result = select(-1, &stReadFDS, (FD_SET *)0, &stExceptFDS, &tv); +#if LOG_VERBOSITY > 1 + if (result == SOCKET_ERROR) + { + int err = WSAGetLastError(); + Log::FormatLine("[UDPSocket] ERROR: Select failed. (Error code %i)", err); + } +#endif + if (result <= 0) // No Data or error + { + return -1; + } + + // If no error: Read Data + recvaddrlen = sizeof(sourceAddr); + status = recvfrom(sock, (char*)dst, maxLen, 0, (SOCKADDR *)&sourceAddr, &recvaddrlen); +#else + // For apple or linux-just read - will timeout in 0.5 ms + status = (int)recvfrom(recfd.sock, dataRef, 5000, 0, recvaddr, &recvaddrlen); +#endif + return status; + } + + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, std::string remoteHost, std::uint16_t remotePort) + { + if (remoteHost == "localhost") + { + remoteHost = "127.0.0.1"; + } + std::uint32_t ip = inet_addr(remoteHost.c_str()); + SendTo(buffer, len, ip, remotePort); + } + + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, std::uint32_t remoteIP, std::uint16_t remotePort) + { + struct sockaddr_in remoteAddr; + remoteAddr.sin_family = AF_INET; + remoteAddr.sin_port = htons(remotePort); + remoteAddr.sin_addr.s_addr = remoteIP; + +#ifdef _WIN32 + const char on = 1; +#else + int on = 1; +#endif + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + + buffer[4] = (std::uint8_t)len; + if (sendto(sock, (char*)buffer, len, 0, (const struct sockaddr *) &remoteAddr, sizeof(remoteAddr)) < 0) + { +#if LOG_VERBOSITY > 0 + Log::FormatLine("[UDPSocket] Send failed. (remote: %s:%d)", remoteAddr, remotePort); +#endif + } + } +} \ No newline at end of file diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h new file mode 100644 index 0000000..e28b43a --- /dev/null +++ b/xpcPlugin/UDPSocket.h @@ -0,0 +1,79 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPC_SOCKET_H +#define XPC_SOCKET_H + +#include +#include +#include +#ifdef _WIN32 +#include +#include +#pragma comment(lib,"ws2_32.lib") //Winsock Library +#elif (__APPLE__ || __linux) +#include +#include +#include +#include +#endif + + +namespace XPC +{ + /// Represents a UDP datagram socket used for reading data from and sending + /// data to XPC clients. + /// + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-10 + /// \date Last Updated: 2015-04-11 + class UDPSocket + { + public: + /// Initializes a new instance of the XPCSocket class bound to the + /// specified receive port. + /// + /// \param recvPort The port on which this instance will receive data. + UDPSocket(std::uint16_t recvPort); + + /// Closes the underlying socket for this instance. + ~UDPSocket(); + + /// Reads the specified number of bytes into the data buffer and stores + /// the remote endpoint. + /// + /// \param buffer The array to copy the data into. + /// \param size The number of bytes to read. + /// \param remoteAddr When at least one byte is read, contains the address + /// of the remote host. + /// \returns The number of bytes read, or a negative number if + /// an error occurs. + int Read(std::uint8_t* buffer, int size, struct sockaddr* remoteAddr); + + /// Sends data to the specified remote endpoint. + /// + /// \param data The data to be sent. + /// \param len The number of bytes to send. + /// \param remoteHost The hostname of the destination client. + /// \param remotePort The port of the destination client. + void SendTo(std::uint8_t* buffer, std::size_t len, std::string remoteHost, std::uint16_t remotePort); + + /// Sends data to the specified remote endpoint. + /// + /// \param data The data to be sent. + /// \param len The number of bytes to send. + /// \param remoteHost The hostname of the destination client. + /// \param remotePort The port of the destination client. + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, std::uint32_t remoteIP, std::uint16_t remotePort); + + private: +#ifdef _WIN32 + SOCKET sock; +#else + int sock; +#endif + }; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index 2a6c51c..cde7903 100755 --- a/xpcPlugin/XPCPlugin.cpp +++ b/xpcPlugin/XPCPlugin.cpp @@ -63,6 +63,7 @@ // XPC Includes #include "Log.h" #include "Drawing.h" +#include "UDPSocket.h" #include "xpcPluginTools.h" // XPLM Includes @@ -87,8 +88,8 @@ #define OPS_PER_CYCLE 20 // Max Number of operations per cycle static XPLMDataRef XPLMSwitch; // for turning on/off simulation -struct xpcSocket recSocket; -struct xpcSocket sendSocket; +XPC::UDPSocket* recvSocket = nullptr; +XPC::UDPSocket* sendSocket = nullptr; short number_of_connections = 0; short current_connection = -1; @@ -190,8 +191,10 @@ PLUGIN_API void XPluginStop(void) PLUGIN_API void XPluginDisable(void) { // Close sockets - closeUDP(recSocket); - closeUDP(sendSocket); + delete recvSocket; + delete sendSocket; + recvSocket = nullptr; + sendSocket = nullptr; // Stop rendering messages to screen. XPC::Drawing::ClearMessage(); @@ -205,19 +208,17 @@ PLUGIN_API void XPluginDisable(void) PLUGIN_API int XPluginEnable(void) { // Open sockets - char IP[16] = "127.0.0.1"; - recSocket = openUDP(RECVPORT, IP, 49009); - sendSocket = openUDP(SENDPORT, IP, 49099); + recvSocket = new XPC::UDPSocket(RECVPORT); + sendSocket = new XPC::UDPSocket(SENDPORT); XPC::Log::WriteLine("[EXEC] xpcPlugin Enabled, sockets opened"); if (benchmarkingSwitch > 0) { XPC::Log::FormatLine("[EXEC] Benchmarking Enabled (Verbosity: %i)", benchmarkingSwitch); - } - if (LOG_VERBOSITY > 0) - { - XPC::Log::FormatLine("[EXEC] Debug Logging Enabled (Verbosity: %i)", LOG_VERBOSITY); } +#if LOG_VERBOSITY > 0 + XPC::Log::FormatLine("[EXEC] Debug Logging Enabled (Verbosity: %i)", LOG_VERBOSITY); +#endif return 1; } @@ -254,7 +255,7 @@ float MyFlightLoopCallback( float inElapsedSinceLastCall, start = (double)mach_absolute_time( ) * timeConvert; #endif } - readMessage(&recSocket, &theMessage); + readMessage(recvSocket, &theMessage); result = handleInput(&theMessage); if (benchmarkingSwitch > 0) @@ -274,10 +275,8 @@ float MyFlightLoopCallback( float inElapsedSinceLastCall, if (counter%cyclesToClear==0) { XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer"); - char IP[16] = "127.0.0.1"; - closeUDP(recSocket); - recSocket = openUDP(RECVPORT, IP, 49009); - + delete recvSocket; + recvSocket = new XPC::UDPSocket(RECVPORT); } } return -1; @@ -426,10 +425,7 @@ short handleInput(struct XPCMessage * theMessage) void sendBUF( char buf[], int buflen) { - char IP[16] = "127.0.0.1"; - memcpy(sendSocket.xpIP,IP,16); - sendSocket.xpPort = 49000; - sendUDP(sendSocket,buf,buflen); + sendSocket->SendTo((std::uint8_t*)buf, buflen, "127.0.0.1", 49000); } int handleCONN(char buf[]) @@ -459,9 +455,9 @@ int handleCONN(char buf[]) // SEND CONFIRMATION // TODO: Ivestigate why sending confirmation causes crashes on Windows 8 - //memcpy(sendSocket.xpIP,connectionList[current_connection].IP, sizeof(connectionList[current_connection].IP)); - //sendSocket.xpPort = connectionList[current_connection].recPort; - //sendUDP(sendSocket, the_message, 5); + //memcpy(sendSocketet.xpIP,connectionList[current_connection].IP, sizeof(connectionList[current_connection].IP)); + //sendSocketet.xpPort = connectionList[current_connection].recPort; + //sendUDP(sendSocketet, the_message, 5); return 0; } @@ -952,13 +948,12 @@ int handleGETD(char buf[]) } } the_message[5] = (char) connectionList[current_connection].requestLength; - - memcpy(sendSocket.xpIP,connectionList[current_connection].IP, sizeof(connectionList[current_connection].IP)); - sendSocket.xpPort = connectionList[current_connection].recPort; - + if (count > 6) { - sendUDP(sendSocket, the_message, count); + char* host = connectionList[current_connection].IP; + std::uint16_t port = connectionList[current_connection].recPort; + sendSocket->SendTo((std::uint8_t*)the_message, count, host, port); } return 0; diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index 76fe77a..8de63dd 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index 4f6ac16..9d83882 100644 Binary files a/xpcPlugin/XPlaneConnect/win.xpl and b/xpcPlugin/XPlaneConnect/win.xpl differ diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj index a3e0ebd..b55e7a3 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj @@ -101,12 +101,14 @@ + + diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters index f5b2117..a35a766 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters @@ -27,6 +27,9 @@ Header Files + + Header Files + @@ -44,6 +47,9 @@ Source Files + + Source Files + diff --git a/xpcPlugin/xpcPluginTools.cpp b/xpcPlugin/xpcPluginTools.cpp index 03c43be..776600b 100644 --- a/xpcPlugin/xpcPluginTools.cpp +++ b/xpcPlugin/xpcPluginTools.cpp @@ -36,9 +36,9 @@ XPLMDataRef XPLMDataRefs[134][8]; XPLMDataRef multiplayer[20][17]; XPLMDataRef AIswitch; -void readMessage(struct xpcSocket * recSocket, struct XPCMessage * pMessage) +void readMessage(XPC::UDPSocket* socket, struct XPCMessage * pMessage) { - pMessage->msglen = readUDP( *recSocket, pMessage->msg, &(pMessage->recvaddr) ); + pMessage->msglen = socket->Read((std::uint8_t*)pMessage->msg, 5000, &pMessage->recvaddr); if ( pMessage->msglen <= 0 ) // No Message { diff --git a/xpcPlugin/xpcPluginTools.h b/xpcPlugin/xpcPluginTools.h index b908df4..38a673d 100644 --- a/xpcPlugin/xpcPluginTools.h +++ b/xpcPlugin/xpcPluginTools.h @@ -8,6 +8,8 @@ #ifndef xpcPlugin_xpcPluginTools_h #define xpcPlugin_xpcPluginTools_h + +#include "UDPSocket.h" #include #include "xplaneConnect.h" @@ -26,7 +28,7 @@ struct sockaddr recvaddr; }; - void readMessage(struct xpcSocket * recSocket, struct XPCMessage * pMessage); + void readMessage(XPC::UDPSocket* socket, struct XPCMessage * pMessage); void buildXPLMDataRefs(void);