From 76d660ebd336d3dd47dd555ebbaa721a4cc978b2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 6 Jun 2011 20:35:01 +0200 Subject: [PATCH] Faster timeout when connecting Use non-blocking connects, and a select() call to wait a predefined time (5s by default, but configurable with -timeout) for either success or failure. This allows much more connections to be tried per time unit. Based on a patch by phantomcircuit. --- src/init.cpp | 8 +++++ src/main.h | 1 - src/net.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/net.h | 3 +- src/util.h | 2 ++ 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index a5104601..0e2113e7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -151,6 +151,7 @@ bool AppInit2(int argc, char* argv[]) " -gen=0 \t\t " + _("Don't generate coins\n") + " -min \t\t " + _("Start minimized\n") + " -datadir= \t\t " + _("Specify data directory\n") + + " -timeout= \t " + _("Specify connection timeout (in milliseconds)\n") + " -proxy= \t " + _("Connect through socks4 proxy\n") + " -dns \t " + _("Allow DNS lookups for addnode and connect\n") + " -addnode= \t " + _("Add a node to connect to\n") + @@ -414,6 +415,13 @@ bool AppInit2(int argc, char* argv[]) return false; } + if (mapArgs.count("-timeout")) + { + int nNewTimeout = GetArg("-timeout", 5000); + if (nNewTimeout > 0 && nNewTimeout < 600000) + nConnectTimeout = nNewTimeout; + } + if (mapArgs.count("-printblock")) { string strMatch = mapArgs["-printblock"]; diff --git a/src/main.h b/src/main.h index 8087df33..436ffbec 100644 --- a/src/main.h +++ b/src/main.h @@ -79,7 +79,6 @@ extern int fUseUPnP; - bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); diff --git a/src/net.cpp b/src/net.cpp index c884e8d5..ca6380fc 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -51,6 +51,7 @@ map mapAlreadyAskedFor; // Settings int fUseProxy = false; +int nConnectTimeout = 5000; CAddress addrProxy("127.0.0.1",9050); @@ -76,7 +77,7 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout) { hSocketRet = INVALID_SOCKET; @@ -91,7 +92,86 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) bool fProxy = (fUseProxy && addrConnect.IsRoutable()); struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); +#ifdef __WXMSW__ + u_long fNonblock = 1; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + int fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1) +#endif + { + closesocket(hSocket); + return false; + } + + if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + // WSAEINVAL is here because some legacy version of winsock uses it + if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) + { + struct timeval timeout; + timeout.tv_sec = nTimeout / 1000; + timeout.tv_usec = (nTimeout % 1000) * 1000; + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(hSocket, &fdset); + int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); + if (nRet == 0) + { + printf("connection timeout\n"); + closesocket(hSocket); + return false; + } + if (nRet == SOCKET_ERROR) + { + printf("select() for connection failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + socklen_t nRetSize = sizeof(nRet); +#ifdef __WXMSW__ + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR) +#else + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) +#endif + { + printf("getsockopt() for connection failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + if (nRet != 0) + { + printf("connect() failed after select(): %i\n",nRet); + closesocket(hSocket); + return false; + } + } +#ifdef __WXMSW__ + else if (WSAGetLastError() != WSAEISCONN) +#else + else +#endif + { + printf("connect() failed: %s\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + } + + /* + this isn't even strictly necessary + CNode::ConnectNode immediately turns the socket back to non-blocking + but we'll turn it back to blocking just in case + */ +#ifdef __WXMSW__ + fNonblock = 0; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags & !O_NONBLOCK) == SOCKET_ERROR) +#endif { closesocket(hSocket); return false; diff --git a/src/net.h b/src/net.h index d1ded872..8a55856e 100644 --- a/src/net.h +++ b/src/net.h @@ -19,6 +19,7 @@ class CRequestTracker; class CNode; class CBlockIndex; extern int nBestHeight; +extern int nConnectTimeout; @@ -32,7 +33,7 @@ enum -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet); +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout=nConnectTimeout); bool Lookup(const char *pszName, std::vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); bool GetMyExternalIP(unsigned int& ipRet); diff --git a/src/util.h b/src/util.h index e4bf0fb1..cd0761ee 100644 --- a/src/util.h +++ b/src/util.h @@ -105,6 +105,8 @@ T* alignup(T* p) typedef int socklen_t; #else #define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY #define WSAEWOULDBLOCK EWOULDBLOCK #define WSAEMSGSIZE EMSGSIZE #define WSAEINTR EINTR