diff --git a/limelight-common/ControlStream.c b/limelight-common/ControlStream.c index d868021..50da217 100644 --- a/limelight-common/ControlStream.c +++ b/limelight-common/ControlStream.c @@ -33,6 +33,8 @@ static LINKED_BLOCKING_QUEUE invalidReferenceFrameTuples; #define IDX_INVALIDATE_REF_FRAMES 2 #define IDX_LOSS_STATS 3 +#define CONTROL_STREAM_TIMEOUT_SEC 10 + static const short packetTypesGen3[] = { 0x140b, // Start A 0x1410, // Start B @@ -417,7 +419,8 @@ int stopControlStream(void) { int startControlStream(void) { int err; - ctlSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 47995); + ctlSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, + 47995, CONTROL_STREAM_TIMEOUT_SEC); if (ctlSock == INVALID_SOCKET) { return LastSocketFail(); } diff --git a/limelight-common/InputStream.c b/limelight-common/InputStream.c index f03f3d0..b412245 100644 --- a/limelight-common/InputStream.c +++ b/limelight-common/InputStream.c @@ -15,6 +15,7 @@ static PLT_THREAD inputSendThread; static OAES_CTX* oaesContext; #define MAX_INPUT_PACKET_SIZE 128 +#define INPUT_STREAM_TIMEOUT_SEC 10 // Contains input stream packets typedef struct _PACKET_HOLDER { @@ -272,7 +273,8 @@ static void inputSendThreadProc(void* context) { int startInputStream(void) { int err; - inputSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 35043); + inputSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, + 35043, INPUT_STREAM_TIMEOUT_SEC); if (inputSock == INVALID_SOCKET) { return LastSocketFail(); } diff --git a/limelight-common/Platform.h b/limelight-common/Platform.h index a5b566c..b60f855 100644 --- a/limelight-common/Platform.h +++ b/limelight-common/Platform.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #endif diff --git a/limelight-common/PlatformSockets.c b/limelight-common/PlatformSockets.c index 71db8bf..6be2969 100644 --- a/limelight-common/PlatformSockets.c +++ b/limelight-common/PlatformSockets.c @@ -110,34 +110,80 @@ SOCKET bindUdpSocket(int addrfamily, int bufferSize) { return s; } -SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port) { +SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec) { SOCKET s; struct sockaddr_in6 addr; int err; + int val; + struct fd_set writefds, exceptfds; + struct timeval tv; s = socket(dstaddr->ss_family, SOCK_STREAM, IPPROTO_TCP); if (s == INVALID_SOCKET) { Limelog("socket() failed: %d\n", (int)LastSocketError()); return INVALID_SOCKET; } - + #ifdef LC_DARWIN - { - // Disable SIGPIPE on iOS - int val = 1; - setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val)); - } + // Disable SIGPIPE on iOS + val = 1; + setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val)); #endif + + // Enable non-blocking I/O for connect timeout support + val = 1; + ioctlsocket(s, FIONBIO, &val); + // Start async connection memcpy(&addr, dstaddr, sizeof(addr)); addr.sin6_port = htons(port); - if (connect(s, (struct sockaddr*) &addr, addrlen) == SOCKET_ERROR) { + connect(s, (struct sockaddr*) &addr, addrlen); + + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(s, &writefds); + FD_SET(s, &exceptfds); + + tv.tv_sec = timeoutSec; + tv.tv_usec = 0; + + // Wait for the connection to complete or the timeout to elapse + err = select(s + 1, NULL, &writefds, &exceptfds, &tv); + if (err < 0) { + // select() failed err = LastSocketError(); - Limelog("connect() failed: %d\n", err); + Limelog("select() failed: %d\n", err); closeSocket(s); SetLastSocketError(err); return INVALID_SOCKET; } + else if (err == 0) { + // select() timed out + Limelog("select() timed out after %d seconds\n", timeoutSec); + closeSocket(s); +#if defined(LC_WINDOWS) + SetLastSocketError(WSAEWOULDBLOCK); +#else + SetLastSocketError(EWOULDBLOCK); +#endif + return INVALID_SOCKET; + } + else if (FD_ISSET(s, &writefds) || FD_ISSET(s, &exceptfds)) { + // The socket was signalled + SOCKADDR_LEN len = sizeof(err); + getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, &len); + if (err != 0 || FD_ISSET(s, &exceptfds)) { + err = (err != 0) ? err : LastSocketFail(); + Limelog("connect() failed: %d\n", err); + closeSocket(s); + SetLastSocketError(err); + return INVALID_SOCKET; + } + } + + // Disable non-blocking I/O now that the connection is established + val = 0; + ioctlsocket(s, FIONBIO, &val); return s; } diff --git a/limelight-common/PlatformSockets.h b/limelight-common/PlatformSockets.h index fe8dc62..d7e17ed 100644 --- a/limelight-common/PlatformSockets.h +++ b/limelight-common/PlatformSockets.h @@ -25,6 +25,7 @@ typedef int SOCKADDR_LEN; #include #include +#define ioctlsocket ioctl #define LastSocketError() errno #define SetLastSocketError(x) errno = x #define INVALID_SOCKET -1 @@ -41,7 +42,7 @@ typedef socklen_t SOCKADDR_LEN; #define URLSAFESTRING_LEN (INET6_ADDRSTRLEN+2) void addrToUrlSafeString(struct sockaddr_storage* addr, char* string); -SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port); +SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec); SOCKET bindUdpSocket(int addrfamily, int bufferSize); int enableNoDelay(SOCKET s); int recvUdpSocket(SOCKET s, char* buffer, int size); diff --git a/limelight-common/RtspConnection.c b/limelight-common/RtspConnection.c index 2905cea..fdf5728 100644 --- a/limelight-common/RtspConnection.c +++ b/limelight-common/RtspConnection.c @@ -2,7 +2,7 @@ #include "Rtsp.h" #define RTSP_MAX_RESP_SIZE 32768 -#define RTSP_READ_TIMEOUT_SEC 10 +#define RTSP_TIMEOUT_SEC 10 static SOCKET sock = INVALID_SOCKET; static int currentSeqNumber; @@ -88,13 +88,13 @@ static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response, in *error = -1; - sock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 48010); + sock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 48010, RTSP_TIMEOUT_SEC); if (sock == INVALID_SOCKET) { *error = LastSocketError(); return ret; } enableNoDelay(sock); - setRecvTimeout(sock, RTSP_READ_TIMEOUT_SEC); + setRecvTimeout(sock, RTSP_TIMEOUT_SEC); serializedMessage = serializeRtspMessage(request, &messageLen); if (serializedMessage == NULL) { diff --git a/limelight-common/VideoStream.c b/limelight-common/VideoStream.c index ffde1ca..b3464d5 100644 --- a/limelight-common/VideoStream.c +++ b/limelight-common/VideoStream.c @@ -4,6 +4,7 @@ #include "RtpReorderQueue.h" #define FIRST_FRAME_MAX 1500 +#define FIRST_FRAME_TIMEOUT_SEC 10 #define RTP_PORT 47998 #define FIRST_FRAME_PORT 47996 @@ -218,7 +219,8 @@ int startVideoStream(void* rendererContext, int drFlags) { if (ServerMajorVersion == 3) { // Connect this socket to open port 47998 for our ping thread - firstFrameSocket = connectTcpSocket(&RemoteAddr, RemoteAddrLen, FIRST_FRAME_PORT); + firstFrameSocket = connectTcpSocket(&RemoteAddr, RemoteAddrLen, + FIRST_FRAME_PORT, FIRST_FRAME_TIMEOUT_SEC); if (firstFrameSocket == INVALID_SOCKET) { return LastSocketError(); }