diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 405e400d1c..a7c7db0d2b 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -244,7 +244,7 @@ int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode, int timeout tval.tv_sec = timeoutUS / 1000000; tval.tv_usec = timeoutUS % 1000000; - int ret = select(fd + 1, &readfds, &writefds, nullptr, &tval); + int ret = select(fd + 1, readfd? &readfds: nullptr, writefd? &writefds: nullptr, nullptr, &tval); if (errorcode != nullptr) *errorcode = errno; @@ -1990,13 +1990,13 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ if (iResult == SOCKET_ERROR && errorcode != EISCONN) { u64 startTime = (u64)(real_time_now() * 1000.0); - while (IsSocketReady(metasocket, true, true) <= 0) { + while (IsSocketReady(metasocket, false, true) <= 0) { u64 now = (u64)(real_time_now() * 1000.0); if (coreState == CORE_POWERDOWN) return iResult; if (now - startTime > adhocDefaultTimeout) break; sleep_ms(10); } - if (IsSocketReady(metasocket, true, true) <= 0) { + if (IsSocketReady(metasocket, false, true) <= 0) { ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), inet_ntoa(g_adhocServerIP.in.sin_addr), ntohs(g_adhocServerIP.in.sin_port)); host->NotifyUserMessage(n->T("Failed to connect to Adhoc Server"), 1.0f, 0x0000ff); return iResult; diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index c571bcdfea..35d455e609 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -96,7 +96,7 @@ inline bool connectInProgress(int errcode){ return (errcode == EAGAIN || errcode #endif #ifndef SD_BOTH -#define SD_BOTH 0x02 +#define SD_BOTH SHUT_RDWR //0x02 #endif #define IsMatch(buf1, buf2) (memcmp(&buf1, &buf2, sizeof(buf1)) == 0) @@ -326,11 +326,12 @@ typedef struct SceNetAdhocPtpStat { // PDP & PTP Socket Union (Internal use only) typedef struct AdhocSocket { - s32_le type; + s32_le type; // SOCK_PDP/SOCK_PTP s32_le flags; // Socket Alert Flags - u32 send_timeout; - u32 recv_timeout; - s32 connectCount; + u32 send_timeout; // default connect timeout + u32 recv_timeout; // default accept timeout + s32 retry_count; // combined with timeout to be used on keepalive + s32 attemptCount; // connect/accept attempts union { SceNetAdhocPdpStat pdp; SceNetAdhocPtpStat ptp; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index f9a1d8ed8f..84975f95c8 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -440,10 +440,15 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { sockaddr_in sin; memset(&sin, 0, sizeof(sin)); socklen_t sinlen = sizeof(sin); + int ret, sockerr; - // Accept Connection - int ret = accept(uid, (sockaddr*)&sin, &sinlen); - int sockerr = errno; + // Check if listening socket is ready to accept + ret = IsSocketReady(uid, true, false, &sockerr); + if (ret > 0) { + // Accept Connection + ret = accept(uid, (sockaddr*)&sin, &sinlen); + sockerr = errno; + } // Accepted New Connection if (ret > 0) { @@ -451,7 +456,7 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { if (newid > 0) result = newid; } - else if (ret == SOCKET_ERROR && connectInProgress(sockerr)) { + else if (ret == 0 || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT))) { u64 now = (u64)(real_time_now() * 1000000.0); if (sock->flags & ADHOC_F_ALERTACCEPT) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -479,7 +484,7 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { int sockerr; // Wait for Connection (assuming "connect" has been called before) - int ret = IsSocketReady(uid, true, true, &sockerr); + int ret = IsSocketReady(uid, false, true, &sockerr); // Connection is ready if (ret > 0) { @@ -2669,6 +2674,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Socket Type internal->type = SOCK_PTP; internal->send_timeout = rexmt_int; + internal->retry_count = rexmt_cnt; // Copy Infrastructure Socket ID internal->data.ptp.id = tcpsocket; @@ -2737,6 +2743,9 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Enable Port Re-use setSockReuseAddrPort(newsocket); + // Enable KeepAlive + setSockKeepAlive(newsocket, true, socket->recv_timeout / 1000000L, socket->retry_count); + // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? if (g_Config.bTCPNoDelay) setSockNoDelay(newsocket, 1); @@ -2801,6 +2810,9 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther //sceNetPortOpen("TCP", internal->lport); //g_PortManager.Add(IP_PROTOCOL_TCP, internal->lport + portOffset); + // Switch to non-blocking for futher usage + changeBlockingMode(newsocket, 1); + INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u)", ptpId, i + 1, internal->data.ptp.lport, inet_ntoa(peeraddr.sin_addr), internal->data.ptp.pport); // Return Socket @@ -2864,16 +2876,22 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int sockaddr_in peeraddr; memset(&peeraddr, 0, sizeof(peeraddr)); socklen_t peeraddrlen = sizeof(peeraddr); + int error; - // Accept Connection - int newsocket = accept(ptpsocket.id, (sockaddr*)&peeraddr, &peeraddrlen); - int error = errno; + // Check if listening socket is ready to accept + int newsocket = IsSocketReady(ptpsocket.id, true, false, &error); + if (newsocket > 0) { + // Accept Connection + newsocket = accept(ptpsocket.id, (sockaddr*)&peeraddr, &peeraddrlen); + error = errno; + } - if (newsocket == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK)) { + if (newsocket == 0 || (newsocket == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ETIMEDOUT))) { + socket->attemptCount++; if (flag == 0) { // Simulate blocking behaviour with non-blocking socket u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id; - return WaitBlockingAdhocSocket(threadSocketId, PTP_ACCEPT, id, nullptr, nullptr, timeout, addr, port, "ptp accept"); + return WaitBlockingAdhocSocket(threadSocketId, PTP_ACCEPT, id, nullptr, nullptr, (flag) ? socket->recv_timeout : timeout, addr, port, "ptp accept"); } // Prevent spamming Debug Log with retries of non-bocking socket else { @@ -2883,6 +2901,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Accepted New Connection if (newsocket > 0) { + socket->attemptCount++; int newid = AcceptPtpSocket(id, newsocket, peeraddr, addr, port); if (newid >= 0) return newid; @@ -2971,7 +2990,7 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Instant Connection (Lucky!) if (connectresult != SOCKET_ERROR || errorcode == EISCONN) { - socket->connectCount++; + socket->attemptCount++; // Set Connected State ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; @@ -2982,9 +3001,9 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Connection in Progress else if (connectresult == SOCKET_ERROR && connectInProgress(errorcode)) { - socket->connectCount++; + socket->attemptCount++; // Nonblocking Mode. First attempt need to be blocking for GvG Next Plus to work, even though it used non-blocking flag but only try to connect once per socket, which mean treating it just like blocking socket instead of non-blocking :( - if (flag && socket->connectCount > 1) { + if (flag && socket->attemptCount > 1) { //if (errorcode == EALREADY) return ERROR_NET_ADHOC_BUSY; return ERROR_NET_ADHOC_WOULD_BLOCK; } @@ -3166,6 +3185,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Socket Type internal->type = SOCK_PTP; internal->recv_timeout = rexmt_int; + internal->retry_count = rexmt_cnt; // Copy Infrastructure Socket ID internal->data.ptp.id = tcpsocket;