diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index d48ac02..208ed7f 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -117,13 +117,16 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port) exit(EXIT_FAILURE); } - // Set timeout to 100ms + // Set socket timeout period for sendUDP to 1 millisecond + // Without this, playback may become choppy due to process blocking #ifdef _WIN32 - DWORD timeout = 100; + // Minimum socket timeout in Windows is 1 millisecond (0 makes it blocking) + DWORD timeout = 1; #else + // Set socket timeout to 1 millisecond = 1,000 microseconds to make it the same as Windows (0 makes it blocking) struct timeval timeout; timeout.tv_sec = 0; - timeout.tv_usec = 250000; + timeout.tv_usec = 1000; #endif if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { @@ -188,13 +191,13 @@ int sendUDP(XPCSocket sock, char buffer[], int len) /// \returns If an error occurs, a negative number. Otherwise, the number of bytes read. int readUDP(XPCSocket sock, char buffer[], int len) { -#ifdef _WIN32 - // Windows readUDP needs the select command- minimum timeout is 1ms. - // Without this playback becomes choppy + // For readUDP, use the select command - minimum timeout of 0 makes it polling. + // Without this, playback may become choppy due to process blocking // Definitions - FD_SET stReadFDS; - FD_SET stExceptFDS; + fd_set stReadFDS; + fd_set stExceptFDS; + struct timeval timeout; // Setup for Select FD_ZERO(&stReadFDS); @@ -202,12 +205,13 @@ int readUDP(XPCSocket sock, char buffer[], int len) FD_ZERO(&stExceptFDS); FD_SET(sock.sock, &stExceptFDS); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100000; + // Set timeout period for select to 0.05 sec = 50 milliseconds = 50,000 microseconds (0 makes it polling) + // TO DO - This could be set to 0 if a message handling system were implemented, like in the plugin. + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // Select Command - int status = select(-1, &stReadFDS, (FD_SET*)0, &stExceptFDS, &tv); + int status = select(sock.sock+1, &stReadFDS, NULL, &stExceptFDS, &timeout); if (status < 0) { printError("readUDP", "Select command error"); @@ -218,11 +222,9 @@ int readUDP(XPCSocket sock, char buffer[], int len) // No data return 0; } + + // If no error: Read Data status = recv(sock.sock, buffer, len, 0); -#else - // For apple or linux-just read - will timeout in 0.5 ms - int status = (int)recv(sock.sock, buffer, len, 0); -#endif if (status < 0) { printError("readUDP", "Error reading socket"); diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index 6eaf3e1..5925ebe 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -56,22 +56,25 @@ namespace XPC return; } - // Set Timout - int usTimeOut = 500; - + // Set timeout period for SendTo to 1 millisecond + // Without this, playback may become choppy due to process blocking #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) + // Minimum socket timeout in Windows is 1 millisecond (0 makes it blocking) + DWORD timeout = 1; +#else + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 1000; +#endif + if (setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { +#ifdef _WIN32 int err = WSAGetLastError(); Log::FormatLine(LOG_ERROR, tag, "ERROR: Failed to set timeout. (Error code %i)", err); - } #else - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = usTimeOut; - setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); + Log::WriteLine(LOG_ERROR, tag, "ERROR: Failed to set timeout."); #endif + } } UDPSocket::~UDPSocket() @@ -87,43 +90,54 @@ namespace XPC int UDPSocket::Read(unsigned char* dst, int maxLen, sockaddr* recvAddr) const { 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 + // For Read, use the select command - minimum timeout of 0 makes it polling. + // Without this, playback may become choppy due to process blocking // Definitions - FD_SET stReadFDS; - FD_SET stExceptFDS; - timeval tv; + fd_set stReadFDS; + fd_set stExceptFDS; + struct timeval timeout; // Setup for Select FD_ZERO(&stReadFDS); FD_SET(sock, &stReadFDS); FD_ZERO(&stExceptFDS); FD_SET(sock, &stExceptFDS); - tv.tv_sec = 0; - tv.tv_usec = 250; + + // Set timeout period for select to 0 to poll the socket + timeout.tv_sec = 0; + timeout.tv_usec = 0; // Select Command - int result = select(-1, &stReadFDS, (FD_SET *)0, &stExceptFDS, &tv); - if (result == SOCKET_ERROR) + int status = select(sock+1, &stReadFDS, NULL, &stExceptFDS, &timeout); + if (status < 0) { +#ifdef _WIN32 int err = WSAGetLastError(); Log::FormatLine(LOG_ERROR, tag, "ERROR: Select failed. (Error code %i)", err); +#else + Log::WriteLine(LOG_ERROR, tag, "ERROR: Select failed."); +#endif + return -1; } - if (result <= 0) // No Data or error + if (status == 0) { + // No data return -1; } // If no error: Read Data status = recvfrom(sock, (char*)dst, maxLen, 0, recvAddr, &recvaddrlen); + if (status < 0) + { +#ifdef _WIN32 + int err = WSAGetLastError(); + Log::FormatLine(LOG_ERROR, tag, "ERROR: Receive failed. (Error code %i)", err); #else - // For apple or linux-just read - will timeout in 0.5 ms - status = (int)recvfrom(sock, dst, 5000, 0, recvAddr, &recvaddrlen); + Log::WriteLine(LOG_ERROR, tag, "ERROR: Receive failed."); #endif + } return status; } diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h index a1ee920..ea86930 100644 --- a/xpcPlugin/UDPSocket.h +++ b/xpcPlugin/UDPSocket.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #endif