text:
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#if defined(HAVE_CONFIG_H)
#include "config/gridcoin-config.h"
#endif

#include "wallet/db.h"
#include "banman.h"
#include "net.h"
#include "init.h"
#include "ui_interface.h"
#include "util.h"

#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/thread.hpp>
#include <inttypes.h>

#if !defined(HAVE_MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif

#ifdef WIN32
  #include <string.h>
#endif

#ifdef USE_UPNP
 #include <miniupnpc/miniwget.h>
 #include <miniupnpc/miniupnpc.h>
 #include <miniupnpc/upnpcommands.h>
 #include <miniupnpc/upnperrors.h>
#endif

using namespace std;

extern int nMaxConnections;
int MAX_OUTBOUND_CONNECTIONS = 8;
int PEER_TIMEOUT = 45;

void ThreadMessageHandler2(void* parg);
void ThreadSocketHandler2(void* parg);
void ThreadOpenConnections2(void* parg);
void ThreadOpenAddedConnections2(void* parg);
#ifdef USE_UPNP
void ThreadMapPort2(void* parg);
#endif
void ThreadDNSAddressSeed2(void* parg);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);

extern void Scraper(bool bSingleShot = false);
extern void NeuralNetwork();

extern bool fScraperActive;

struct LocalServiceInfo {
    int nScore;
    int nPort;
};

//
// Global state variables
//
bool fDiscover = true;
bool fUseUPnP = false;
ServiceFlags nLocalServices = NODE_NETWORK;
static CCriticalSection cs_mapLocalHost;
static map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {};
static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices);
uint64_t nLocalHostNonce = 0;

ThreadHandler* netThreads = new ThreadHandler;
static std::vector<SOCKET> vhListenSocket;
CAddrMan addrman;

// Initialization of static class variable.
std::atomic<NodeId> CNode::nLastNodeId {-1};

vector<CNode*> vNodes;
CCriticalSection cs_vNodes;
vector<std::string> vAddedNodes;
CCriticalSection cs_vAddedNodes;

map<CInv, CDataStream> mapRelay;
deque<pair<int64_t, CInv> > vRelayExpiration;
CCriticalSection cs_mapRelay;
map<CInv, int64_t> mapAlreadyAskedFor;

static deque<string> vOneShots;
CCriticalSection cs_vOneShots;

set<CNetAddr> setservAddNodeAddresses;
CCriticalSection cs_setservAddNodeAddresses;

std::map<CAddress, std::pair<int, int64_t>> CNode::mapMisbehavior;
CCriticalSection CNode::cs_mapMisbehavior;

static CSemaphore *semOutbound = NULL;

void AddOneShot(string strDest)
{
    LOCK(cs_vOneShots);
    vOneShots.push_back(strDest);
}

unsigned short GetListenPort()
{
    return (unsigned short)(GetArg("-port", GetDefaultPort()));
}

void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd, bool fForce)
{
    // The line of code below is the line of code that kept us from syncing to the best block (fForce forces the sync to continue).
    if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) return;  // Filter out duplicate requests
    pindexLastGetBlocksBegin = pindexBegin;
    hashLastGetBlocksEnd = hashEnd;
    PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd);
}

// find 'best' local address for a particular peer
bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
{
    if (fNoListen)
        return false;

    int nBestScore = -1;
    int nBestReachability = -1;
    {
        LOCK(cs_mapLocalHost);
        for (map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++)
        {
            int nScore = (*it).second.nScore;
            int nReachability = (*it).first.GetReachabilityFrom(paddrPeer);
            if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
            {
                addr = CService((*it).first, (*it).second.nPort);
                nBestReachability = nReachability;
                nBestScore = nScore;
            }
        }
    }
    return nBestScore >= 0;
}

// get best local address for a particular peer as a CAddress
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
{
    ServiceFlags nLocalServices = NODE_NETWORK;
    CAddress ret(CService("0.0.0.0",0), nLocalServices);
    CService addr;
    if (GetLocal(addr, paddrPeer))
    {
        ret = CAddress(addr);
        ret.nServices = nLocalServices;
        ret.nTime = GetAdjustedTime();
    }
    return ret;
}

bool RecvLine2(SOCKET hSocket, string& strLine)
{

    try
    {
    strLine = "";
    clock_t begin = clock();

    while (true)
    {
        char c;
        int nBytes = recv(hSocket, &c, 1,  0);

        clock_t end = clock();
        double elapsed_secs = double(end - begin) / (CLOCKS_PER_SEC+.01);
        if (elapsed_secs > 5) return true;

        if (nBytes > 0)
        {
            strLine += c;
            if (c == '\n')      return true;
            if (c == '\r')      return true;
            //12-19-2015
            if (strLine.find("</users>") != string::npos) return true;
            if (strLine.find("</html>") != string::npos) return true;
            if (strLine.find("<EOF>") != string::npos) return true;

            if (strLine.size() >= 39000)
                return true;
        }
        else if (nBytes <= 0)
        {

            boost::this_thread::interruption_point();
            if (nBytes < 0)
            {

                int nErr = WSAGetLastError();
                if (nErr == WSAEMSGSIZE)
                    continue;
                if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
                {
                    MilliSleep(1);
                    clock_t end = clock();
                    double elapsed_secs = double(end - begin) / (CLOCKS_PER_SEC+.01);
                    if (elapsed_secs > 3) return true;
                    continue;
                }
            }
            if (!strLine.empty())
                return true;
            if (nBytes == 0)
            {
                // socket closed
                return false;
            }
            else
            {
                // socket error
                int nErr = WSAGetLastError();
                LogPrint(BCLog::LogFlags::NET, "recv socket err: %d", nErr);
                return false;
            }
        }
    }

    }
    catch (std::exception &e)
    {
        return false;
    }
    catch (...)
    {
        return false;
    }

}

bool RecvLine(SOCKET hSocket, string& strLine)
{
    strLine = "";
    while (true)
    {
        char c;
        int nBytes = recv(hSocket, &c, 1, 0);
        if (nBytes > 0)
        {
            if (c == '\n')
                continue;

            if (c == '\r')
                return true;
            strLine += c;
            if (strLine.size() >= 9000)
                return true;
        }
        else if (nBytes <= 0)
        {
            if (fShutdown)
                return false;
            if (nBytes < 0)
            {
                int nErr = WSAGetLastError();
                if (nErr == WSAEMSGSIZE)
                    continue;
                if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
                {
                    MilliSleep(10);
                    continue;
                }
            }
            if (!strLine.empty())
                return true;
            if (nBytes == 0)
            {
                // socket closed
                LogPrint(BCLog::LogFlags::NOISY, "socket closed");
                return false;
            }
            else
            {
                // socket error
                int nErr = WSAGetLastError();
                LogPrint(BCLog::LogFlags::NOISY, "recv err: %d", nErr);
                return false;
            }
        }
    }
}

// used when scores of local addresses may have changed
// pushes better local address to peers
void static AdvertizeLocal()
{
    LOCK(cs_vNodes);
    for (auto const& pnode : vNodes)
    {
        if (pnode->fSuccessfullyConnected)
        {
            CAddress addrLocal = GetLocalAddress(&pnode->addr);
            if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal)
            {
                pnode->PushAddress(addrLocal);
                pnode->addrLocal = addrLocal;
            }
        }
    }
}

void SetReachable(enum Network net, bool fFlag)
{
    LOCK(cs_mapLocalHost);
    vfReachable[net] = fFlag;
    if (net == NET_IPV6 && fFlag)
        vfReachable[NET_IPV4] = true;
}

// learn a new local address
bool AddLocal(const CService& addr, int nScore)
{
    if (!addr.IsRoutable())
        return false;

    if (!fDiscover && nScore < LOCAL_MANUAL)
        return false;

    if (IsLimited(addr))
        return false;

    try
    {
    LogPrint(BCLog::LogFlags::NOISY, "AddLocal(%s,%i)", addr.ToString(), nScore);

    {
        LOCK(cs_mapLocalHost);
        bool fAlready = mapLocalHost.count(addr) > 0;
        LocalServiceInfo &info = mapLocalHost[addr];
        if (!fAlready || nScore >= info.nScore) {
            info.nScore = nScore + (fAlready ? 1 : 0);
            info.nPort = addr.GetPort();
        }
        SetReachable(addr.GetNetwork());
    }

    AdvertizeLocal();
    }
    catch(...)
    {

    }
    LogPrintf("7..");
    return true;
}

bool AddLocal(const CNetAddr &addr, int nScore)
{
    return AddLocal(CService(addr, GetListenPort()), nScore);
}

/** Make a particular network entirely off-limits (no automatic connects to it) */
void SetLimited(enum Network net, bool fLimited)
{
    if (net == NET_UNROUTABLE)
        return;
    LOCK(cs_mapLocalHost);
    vfLimited[net] = fLimited;
}

bool IsLimited(enum Network net)
{
    LOCK(cs_mapLocalHost);
    return vfLimited[net];
}

bool IsLimited(const CNetAddr &addr)
{
    return IsLimited(addr.GetNetwork());
}

/** vote for a local address */
bool SeenLocal(const CService& addr)
{
    {
        LOCK(cs_mapLocalHost);
        if (mapLocalHost.count(addr) == 0)
            return false;
        mapLocalHost[addr].nScore++;
    }

    AdvertizeLocal();

    return true;
}

/** check whether a given address is potentially local */
bool IsLocal(const CService& addr)
{
    LOCK(cs_mapLocalHost);
    return mapLocalHost.count(addr) > 0;
}

/** check whether a given address is in a network we can probably connect to */
bool IsReachable(const CNetAddr& addr)
{
    LOCK(cs_mapLocalHost);
    enum Network net = addr.GetNetwork();
    return vfReachable[net] && !vfLimited[net];
}

bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet)
{
    SOCKET hSocket;
    if (!ConnectSocket(addrConnect, hSocket))
    {
        LogPrint(BCLog::LogFlags::NOISY, "GetMyExternalIP() : unable to connect to %s ", addrConnect.ToString());
        return false;
    }

    send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL);

    string strLine;
    while (RecvLine(hSocket, strLine))
    {
        if (strLine.empty()) // HTTP response is separated from headers by blank line
        {
            while (true)
            {
                if (!RecvLine(hSocket, strLine))
                {
                    closesocket(hSocket);
                    return false;
                }
                if (pszKeyword == NULL)
                    break;
                if (strLine.find(pszKeyword) != string::npos)
                {
                    strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword));
                    break;
                }
            }
            closesocket(hSocket);
            if (strLine.find("<") != string::npos)
                strLine = strLine.substr(0, strLine.find("<"));
            strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
            while (strLine.size() > 0 && isspace(strLine[strLine.size()-1]))
                strLine.resize(strLine.size()-1);
            CService addr(strLine,0,true);
            LogPrint(BCLog::LogFlags::NOISY, "GetMyExternalIP() received [%s] %s", strLine, addr.ToString());
            if (!addr.IsValid() || !addr.IsRoutable())
                return false;
            ipRet.SetIP(addr);
            return true;
        }
    }
    closesocket(hSocket);
    return error("GetMyExternalIP() : connection closed");
}

bool GetMyExternalIP(CNetAddr& ipRet)
{
    CService addrConnect;
    const char* pszGet;
    const char* pszKeyword;

    for (int nLookup = 0; nLookup <= 1; nLookup++)
    for (int nHost = 1; nHost <= 2; nHost++)
    {
        // We should be phasing out our use of sites like these.  If we need
        // replacements, we should ask for volunteers to put this simple
        // php file on their web server that prints the client IP:
        //  <?php echo $_SERVER["REMOTE_ADDR"]; ?>
        if (nHost == 1)
        {
            addrConnect = CService("91.198.22.70",80); // checkip.dyndns.org

            if (nLookup == 1)
            {
                CService addrIP("checkip.dyndns.org", 80, true);
                if (addrIP.IsValid())
                    addrConnect = addrIP;
            }

            pszGet = "GET / HTTP/1.1\r\n"
                     "Host: checkip.dyndns.org\r\n"
                     "User-Agent: Gridcoin\r\n"
                     "Connection: close\r\n"
                     "\r\n";

            pszKeyword = "Address:";
        }
        else if (nHost == 2)
        {
            addrConnect = CService("74.208.43.192", 80); // www.showmyip.com

            if (nLookup == 1)
            {
                CService addrIP("www.showmyip.com", 80, true);
                if (addrIP.IsValid())
                    addrConnect = addrIP;
            }

            pszGet = "GET /simple/ HTTP/1.1\r\n"
                     "Host: www.showmyip.com\r\n"
                     "User-Agent: Gridcoin\r\n"
                     "Connection: close\r\n"
                     "\r\n";

            pszKeyword = NULL; // Returns just IP address
        }

        if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet))
            return true;
    }

    return false;
}

void ThreadGetMyExternalIP(void* parg)
{
    // Make this thread recognisable as the external IP detection thread
    RenameThread("grc-ext-ip");
    try
    {
        CNetAddr addrLocalHost;
        if (GetMyExternalIP(addrLocalHost))
        {
            LogPrintf("GetMyExternalIP() returned %s", addrLocalHost.ToStringIP());
            AddLocal(addrLocalHost, LOCAL_HTTP);
        }
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadMyExternalIP()");
    }
    catch(boost::thread_interrupted&)
    {
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadGetMyExternalIP()");
    }
}

void AddressCurrentlyConnected(const CService& addr)
{
    addrman.Connected(addr);
}

std::atomic<uint64_t> CNode::nTotalBytesRecv{ 0 };
std::atomic<uint64_t> CNode::nTotalBytesSent{ 0 };

CNode* FindNode(const CNetAddr& ip)
{
    {
        LOCK(cs_vNodes);
        for (auto const& pnode : vNodes)
            if ((CNetAddr)pnode->addr == ip)
                return (pnode);
    }
    return NULL;
}

CNode* FindNode(std::string addrName)
{
    LOCK(cs_vNodes);
    for (auto const& pnode : vNodes)
        if (pnode->addrName == addrName)
            return (pnode);
    return NULL;
}

CNode* FindNode(const CService& addr)
{
    {
        LOCK(cs_vNodes);
        for (auto const& pnode : vNodes)
            if ((CService)pnode->addr == addr)
                return (pnode);
    }
    return NULL;
}

CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
{
    if (pszDest == NULL) {
        if (IsLocal(addrConnect))
            return NULL;

        // Look for an existing connection
        CNode* pnode = FindNode((CService)addrConnect);
        if (pnode)
        {
            pnode->AddRef();
            return pnode;
        }
    }

    // Connect
    SOCKET hSocket;
    if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket))
    {
        addrman.Attempt(addrConnect);
        /// debug print
        LogPrint(BCLog::LogFlags::NOISY, "connected %s", pszDest ? pszDest : addrConnect.ToString());
        // Set to non-blocking
#ifdef WIN32
        u_long nOne = 1;
        if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
            LogPrintf("ConnectSocket() : ioctlsocket non-blocking setting error %d", WSAGetLastError());
#else
        if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
            LogPrintf("ConnectSocket() : fcntl non-blocking setting error %d", errno);
#endif

        // Add node
        CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
        pnode->AddRef();

        {
            LOCK(cs_vNodes);
            vNodes.push_back(pnode);
        }

        pnode->nTimeConnected = GetAdjustedTime();
        return pnode;
    }
    else
    {
        return NULL;
    }
}

NodeId CNode::GetNewNodeId()
{
    return nLastNodeId.fetch_add(1, std::memory_order_relaxed);
}

void CNode::CloseSocketDisconnect()
{
    fDisconnect = true;
    if (hSocket != INVALID_SOCKET)
    {
        LogPrint(BCLog::LogFlags::NOISY, "disconnecting node %s", addrName);
        closesocket(hSocket);
        hSocket = INVALID_SOCKET;

        // in case this fails, we'll empty the recv buffer when the CNode is deleted
        TRY_LOCK(cs_vRecvMsg, lockRecv);
        if (lockRecv)
            vRecvMsg.clear();
    }
}

bool CNode::DisconnectNode(const std::string& strNode)
{
    LOCK(cs_vNodes);
    if (CNode* pnode = FindNode(strNode)) {
        pnode->fDisconnect = true;
        return true;
    }
    return false;
}

bool CNode::DisconnectNode(const CSubNet& subnet)
{
    bool disconnected = false;
    LOCK(cs_vNodes);
    for (CNode* pnode : vNodes) {
        if (subnet.Match(pnode->addr)) {
            pnode->fDisconnect = true;
            disconnected = true;
        }
    }
    return disconnected;
}

bool CNode::DisconnectNode(const CNetAddr& addr)
{
    return CNode::DisconnectNode(CSubNet(addr));
}

bool CNode::DisconnectNode(NodeId id)
{
    LOCK(cs_vNodes);
    for(CNode* pnode : vNodes) {
        if (id == pnode->GetId()) {
            pnode->fDisconnect = true;
            return true;
        }
    }
    return false;
}

void CNode::PushVersion()
{
    int64_t nTime = GetAdjustedTime();
    CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
    CAddress addrMe = GetLocalAddress(&addr);
    RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
    LogPrint(BCLog::LogFlags::NOISY, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s",
        PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString());

    // In the version following 180324 (mandatory v5.0.0 - Fern), we can finally
    // drop the garbage legacy fields added to the version message:
    //
    if (PROTOCOL_VERSION > 180324) {
        //TODO: change `PushMessage()` to use ServiceFlags so we don't need to cast nLocalServices
        PushMessage(
            "aries",
            PROTOCOL_VERSION,
            (uint64_t)nLocalServices,
            nTime,
            addrYou,
            addrMe,
            nLocalHostNonce,
            FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()),
            nBestHeight);
    } else {
        const std::string legacy_dummy;
        PushMessage(
            "aries",
            PROTOCOL_VERSION,
            legacy_dummy, // nonce
            legacy_dummy, // pw1
            legacy_dummy, // mycpid
            legacy_dummy, // enccpid
            legacy_dummy, // acid
            (uint64_t)nLocalServices,
            nTime,
            addrYou,
            addrMe,
            nLocalHostNonce,
            FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()),
            nBestHeight);
    }
}

bool CNode::Misbehaving(int howmuch)
{
    if (addr.IsLocal())
    {
        LogPrintf("Warning: Local node %s misbehaving (delta: %d)!", addrName, howmuch);
        return false;
    }

    {
        int nMisbehavior = 0;

        LOCK(cs_mapMisbehavior);

        nMisbehavior = GetMisbehavior() + howmuch;

        mapMisbehavior[addr] = std::make_pair(nMisbehavior, GetAdjustedTime());

        if (nMisbehavior >= GetArg("-banscore", 100))
        {
            LogPrint(BCLog::LogFlags::VERBOSE, "Misbehaving: %s (%d -> %d) DISCONNECTING", addr.ToString(), nMisbehavior-howmuch, nMisbehavior);

            g_banman->Ban(addr, BanReasonNodeMisbehaving);
            CloseSocketDisconnect();
            return true;
        } else
            LogPrint(BCLog::LogFlags::VERBOSE, "Misbehaving: %s (%d -> %d)", addr.ToString(), nMisbehavior-howmuch, nMisbehavior);
        return false;
    }
}

int CNode::GetMisbehavior() const
{
    int nMisbehavior = 0;

    LOCK(cs_mapMisbehavior);

    const auto& iMisbehavior = mapMisbehavior.find(addr);

    if (iMisbehavior != mapMisbehavior.end())
    {
        // This expression results in the misbehavior decaying linearly over a 24 hour period at a rate equal to the default banscore.
        // The default banscore is normally 100, but can be changed by specifying -banscore on the command line. At the default setting,
        // This results in a decay of roughly 100/24 = 4 points per hour.
        int time_based_decay_correction = std::round(
                    (double) GetArg("-banscore", 100)
                    * (double) std::max((int64_t) 0, GetAdjustedTime() - iMisbehavior->second.second)
                    / (double) GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)
                    );

        // Make sure nMisbehavior doesn't go below zero.
        nMisbehavior = std::max(0, iMisbehavior->second.first - time_based_decay_correction);

        // Delete entry if nMisbehavior is zero.
        if (!nMisbehavior) mapMisbehavior.erase(iMisbehavior);
    }

    return nMisbehavior;
}

void CNode::copyStats(CNodeStats &stats)
{
    stats.id = id;
    stats.nServices = nServices;
    stats.addr = addr;
    stats.nLastSend = nLastSend;
    stats.nLastRecv = nLastRecv;
    stats.nTimeConnected = nTimeConnected;
    stats.nTimeOffset = nTimeOffset;
    stats.addrName = addrName;
    stats.nVersion = nVersion;
    stats.strSubVer = strSubVer;
    stats.fInbound = fInbound;
    stats.nStartingHeight = nStartingHeight;
    stats.nTrust = nTrust;
    stats.nMisbehavior = GetMisbehavior();

    // No lock for these two... using atomics.
    stats.nSendBytes = nSendBytes;
    stats.nRecvBytes = nRecvBytes;

    // It is common for nodes with good ping times to suddenly become lagged,
    // due to a new block arriving or other large transfer.
    // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
    // since pingtime does not update until the ping is complete, which might take a while.
    // So, if a ping is taking an unusually long time in flight,
    // the caller can immediately detect that this is happening.
    int64_t nPingUsecWait = 0;
    if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) {
        nPingUsecWait = GetTimeMicros() - nPingUsecStart;
    }

    // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
    stats.dPingTime = (((double)nPingUsecTime) / 1e6);
    stats.dMinPing  = (((double)nMinPingUsecTime) / 1e6);
    stats.dPingWait = (((double)nPingUsecWait) / 1e6);
    stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";

}

void CNode::CopyNodeStats(std::vector<CNodeStats>& vstats)
{
    vstats.clear();

    LOCK(cs_vNodes);
    vstats.reserve(vNodes.size());
    for (auto const& pnode : vNodes) {
        CNodeStats stats;
        pnode->copyStats(stats);
        vstats.push_back(stats);
    }
}

// requires LOCK(cs_vRecvMsg)
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
{
    nRecvBytes += nBytes;

    while (nBytes > 0) {

        // get current incomplete message, or create a new one
        if (vRecvMsg.empty() ||
            vRecvMsg.back().complete())
            vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion));

        CNetMessage& msg = vRecvMsg.back();

        // absorb network data
        int handled;
        if (!msg.in_data)
            handled = msg.readHeader(pch, nBytes);
        else
            handled = msg.readData(pch, nBytes);

        if (handled < 0)
                return false;

        pch += handled;
        nBytes -= handled;

        if (msg.complete())
            msg.nTime = GetTimeMicros();
    }

    return true;
}

int CNetMessage::readHeader(const char *pch, unsigned int nBytes)
{
    // copy data to temporary parsing buffer
    unsigned int nRemaining = 24 - nHdrPos;
    unsigned int nCopy = std::min(nRemaining, nBytes);

    memcpy(&hdrbuf[nHdrPos], pch, nCopy);
    nHdrPos += nCopy;

    // if header incomplete, exit
    if (nHdrPos < 24)
        return nCopy;

    // deserialize to CMessageHeader
    try {
        hdrbuf >> hdr;
    }
    catch (std::exception &e) {
        return -1;
    }

    // reject messages larger than MAX_SIZE
    if (hdr.nMessageSize > MAX_SIZE)
            return -1;

    // switch state to reading message data
    in_data = true;

    return nCopy;
}

int CNetMessage::readData(const char *pch, unsigned int nBytes)
{
    unsigned int nRemaining = hdr.nMessageSize - nDataPos;
    unsigned int nCopy = std::min(nRemaining, nBytes);

    if (vRecv.size() < nDataPos + nCopy) {
        // Allocate up to 256 KiB ahead, but never more than the total message size.
        vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024));
    }

    memcpy(&vRecv[nDataPos], pch, nCopy);
    nDataPos += nCopy;

    return nCopy;
}

// requires LOCK(cs_vSend)
void SocketSendData(CNode *pnode)
{
    std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin();

    while (it != pnode->vSendMsg.end())
    {
        const CSerializeData &data = *it;
        assert(data.size() > pnode->nSendOffset);
        int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
        if (nBytes > 0) {
            pnode->nLastSend = GetAdjustedTime();
            pnode->nSendBytes += nBytes;
            pnode->nSendOffset += nBytes;
            pnode->RecordBytesSent(nBytes);
            if (pnode->nSendOffset == data.size()) {
                pnode->nSendOffset = 0;
                pnode->nSendSize -= data.size();
                it++;
            } else {
                // could not send full message; stop sending more
                break;
            }
        }
        else
        {
            if (nBytes < 0) {
                // error
                int nErr = WSAGetLastError();
                if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
                {
                    LogPrint(BCLog::LogFlags::NOISY, "socket send error %d", nErr);
                    pnode->CloseSocketDisconnect();
                }
            }
            // couldn't send anything at all
            break;
        }
    }

    if (it == pnode->vSendMsg.end()) {
        assert(pnode->nSendOffset == 0);
        assert(pnode->nSendSize == 0);
    }
    pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it);
}

void ThreadSocketHandler(void* parg)
{
    // Make this thread recognisable as the networking thread
    RenameThread("grc-net");

    try
    {
        ThreadSocketHandler2(parg);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadSocketHandler()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadSocketHandler exited (interrupt)");
        return;
    }
    catch (...)
    {
        throw; // support pthread_cancel()
    }
    LogPrintf("ThreadSocketHandler exited");
}

void ThreadSocketHandler2(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadSocketHandler started");
    list<CNode*> vNodesDisconnected;
    unsigned int nPrevNodeCount = 0;

    while (true)
    {
        //
        // Disconnect nodes
        //
        {
            LOCK(cs_vNodes);
            // Disconnect unused nodes
            vector<CNode*> vNodesCopy = vNodes;
            for (auto const& pnode : vNodesCopy)
            {
                if (pnode->fDisconnect ||
                    (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty()))
                {
                    // remove from vNodes
                    vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());

                    // release outbound grant (if any)
                    pnode->grantOutbound.Release();

                    // close socket and cleanup
                    pnode->CloseSocketDisconnect();

                    // hold in disconnected pool until all refs are released
                    if (pnode->fNetworkNode || pnode->fInbound)
                        pnode->Release();
                    vNodesDisconnected.push_back(pnode);
                }
            }

            // Delete disconnected nodes
            list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
            for (auto const& pnode : vNodesDisconnectedCopy)
            {
                // wait until threads are done using it
                if (pnode->GetRefCount() <= 0)
                {
                    bool fDelete = false;
                    {
                        TRY_LOCK(pnode->cs_vSend, lockSend);
                        if (lockSend)
                        {
                            TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                            if (lockRecv)
                            {
                                TRY_LOCK(pnode->cs_mapRequests, lockReq);
                                if (lockReq)
                                {
                                    TRY_LOCK(pnode->cs_inventory, lockInv);
                                    if (lockInv)
                                        fDelete = true;
                                }
                            }
                        }
                    }
                    if (fDelete)
                    {
                        vNodesDisconnected.remove(pnode);
                        delete pnode;
                    }
                }
            }
        }

        size_t vNodesSize;
        {
            LOCK(cs_vNodes);
            vNodesSize = vNodes.size();
        }

        if(vNodesSize != nPrevNodeCount)
        {
            nPrevNodeCount = vNodesSize;
            uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
        }

        //
        // Find which sockets have data to receive
        //
        struct timeval timeout;
        timeout.tv_sec  = 0;
        timeout.tv_usec = 50000; // frequency to poll pnode->vSend

        fd_set fdsetRecv;
        fd_set fdsetSend;
        fd_set fdsetError;
        FD_ZERO(&fdsetRecv);
        FD_ZERO(&fdsetSend);
        FD_ZERO(&fdsetError);
        SOCKET hSocketMax = 0;
        bool have_fds = false;

        for (auto const& hListenSocket : vhListenSocket) {
            FD_SET(hListenSocket, &fdsetRecv);
            hSocketMax = max(hSocketMax, hListenSocket);
            have_fds = true;
        }
        {
            LOCK(cs_vNodes);
            for (auto const& pnode : vNodes)
            {
                if (pnode->hSocket == INVALID_SOCKET)
                    continue;
                {
                    TRY_LOCK(pnode->cs_vSend, lockSend);
                    if (lockSend) {
                        // do not read, if draining write queue
                        if (!pnode->vSendMsg.empty())
                            FD_SET(pnode->hSocket, &fdsetSend);
                        else
                            FD_SET(pnode->hSocket, &fdsetRecv);
                        FD_SET(pnode->hSocket, &fdsetError);
                        hSocketMax = max(hSocketMax, pnode->hSocket);
                        have_fds = true;
                    }
                }
            }
        }

        int nSelect = select(have_fds ? hSocketMax + 1 : 0,
                             &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
        if (fShutdown)
            return;
        if (nSelect == SOCKET_ERROR)
        {
            if (have_fds)
            {
                int nErr = WSAGetLastError();
                LogPrint(BCLog::LogFlags::NOISY, "socket select error %d", nErr);
                for (unsigned int i = 0; i <= hSocketMax; i++)
                    FD_SET(i, &fdsetRecv);
            }
            FD_ZERO(&fdsetSend);
            FD_ZERO(&fdsetError);
            MilliSleep(timeout.tv_usec/1000);
        }

        //
        // Accept new connections
        //
        for (auto const& hListenSocket : vhListenSocket)
        if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
        {
            struct sockaddr_storage sockaddr;
            socklen_t len = sizeof(sockaddr);
            SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
            CAddress addr;
            int nInbound = 0;

            if (hSocket != INVALID_SOCKET)
                if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
                    LogPrintf("Warning: Unknown socket family");

            {
                LOCK(cs_vNodes);
                for (auto const& pnode : vNodes)
                    if (pnode->fInbound)
                        nInbound++;
            }

            if (hSocket == INVALID_SOCKET)
            {
                int nErr = WSAGetLastError();
                if (nErr != WSAEWOULDBLOCK)
                    LogPrintf("socket error accept INVALID_SOCKET: %d", nErr);
            }
            else if (nInbound >= GetArg("-maxconnections", 250) - MAX_OUTBOUND_CONNECTIONS)
            {
                LogPrint(BCLog::LogFlags::NOISY,
                         "Surpassed max inbound connections maxconnections:%" PRId64 " minus max_outbound:%i",
                         GetArg("-maxconnections",250),
                         MAX_OUTBOUND_CONNECTIONS);

                closesocket(hSocket);
            }
            else if (g_banman->IsBanned(addr))
            {
                LogPrint(BCLog::LogFlags::NOISY, "connection from %s dropped (banned)", addr.ToString());
                closesocket(hSocket);
            }
            else
            {
                LogPrint(BCLog::LogFlags::NOISY, "accepted connection %s", addr.ToString());
                CNode* pnode = new CNode(hSocket, addr, "", true);
                pnode->AddRef();
                {
                    LOCK(cs_vNodes);
                    vNodes.push_back(pnode);
                }
            }
        }

        //
        // Service each socket
        //
        vector<CNode*> vNodesCopy;
        {
            LOCK(cs_vNodes);
            vNodesCopy = vNodes;
            for (auto const& pnode : vNodesCopy)
                pnode->AddRef();
        }
        for (auto const& pnode : vNodesCopy)
        {
            if (fShutdown)
                return;

            //
            // Receive
            //
            if (pnode->hSocket == INVALID_SOCKET)
                continue;
            if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
            {
                TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                if (lockRecv)
                {
                    if (pnode->GetTotalRecvSize() > ReceiveFloodSize()) {
                        if (!pnode->fDisconnect)
                            LogPrintf("socket recv flood control disconnect (%u bytes)", pnode->GetTotalRecvSize());
                        pnode->CloseSocketDisconnect();
                    }
                    else {
                        // typical socket buffer is 8K-64K
                        char pchBuf[0x10000];
                        int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
                        if (nBytes > 0)
                        {
                            if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
                                pnode->CloseSocketDisconnect();
                            pnode->nLastRecv = GetAdjustedTime();
                            pnode->RecordBytesRecv(nBytes);
                        }
                        else if (nBytes == 0)
                        {
                            // socket closed gracefully
                            if (!pnode->fDisconnect)
                            {
                              LogPrint(BCLog::LogFlags::NOISY, "socket closed");
                            }
                            pnode->CloseSocketDisconnect();
                        }
                        else if (nBytes < 0)
                        {
                            // error
                            int nErr = WSAGetLastError();
                            if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
                            {
                                if (!pnode->fDisconnect)
                                {
                                   LogPrint(BCLog::LogFlags::NOISY, "socket recv error %d", nErr);
                                }
                                pnode->CloseSocketDisconnect();
                            }
                        }
                    }
                }
            }

            //
            // Send
            //
            if (pnode->hSocket == INVALID_SOCKET)
                continue;
            if (FD_ISSET(pnode->hSocket, &fdsetSend))
            {
                TRY_LOCK(pnode->cs_vSend, lockSend);
                if (lockSend)
                    SocketSendData(pnode);
            }

            //
            // Inactivity checking
            //
            // Consider this for future removal as this really is not beneficial nor harmful.
            if ((GetAdjustedTime() - pnode->nTimeConnected) > (60*60*2) && (vNodes.size() > (MAX_OUTBOUND_CONNECTIONS*.75)))
            {
                    LogPrint(BCLog::LogFlags::NOISY, "Node %s connected longer than 2 hours with connection count of %zd, disconnecting. ",
                             pnode->addr.ToString(), vNodes.size());

                    pnode->fDisconnect = true;

                    continue;
            }

            int64_t nTime = GetAdjustedTime();

            if (nTime - pnode->nTimeConnected > PEER_TIMEOUT)
            {
                if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
                {
                    LogPrint(BCLog::LogFlags::NOISY, "socket no message in first %d seconds, %d %d",
                             PEER_TIMEOUT, pnode->nLastRecv != 0, pnode->nLastSend != 0);

                    pnode->fDisconnect = true;

                    continue;
                }

                else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
                {
                    LogPrintf("socket sending timeout: %" PRId64 "s", nTime - pnode->nLastSend);

                    pnode->fDisconnect = true;

                    continue;
                }

                else if (nTime - pnode->nLastRecv > TIMEOUT_INTERVAL)
                {
                    LogPrintf("socket receive timeout: %" PRId64 "s", nTime - pnode->nLastRecv);

                    pnode->fDisconnect = true;

                    continue;
                }

                else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
                {
                    LogPrintf("ping timeout: %fs", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));

                    pnode->fDisconnect = true;

                    continue;
                }
            }
        }
        {
            LOCK(cs_vNodes);
            for (auto const& pnode : vNodesCopy)
                pnode->Release();
        }

        MilliSleep(10);
    }
}

#ifdef USE_UPNP
void ThreadMapPort(void* parg)
{
    // Make this thread recognisable as the UPnP thread
    RenameThread("grc-UPnP");

    try
    {
        ThreadMapPort2(parg);
    }
    catch (std::exception& e) {
        PrintException(&e, "ThreadMapPort()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadMapPort exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadMapPort()");
    }
    LogPrintf("ThreadMapPort exited");
}

void ThreadMapPort2(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadMapPort started");

    std::string port = strprintf("%u", GetListenPort());
    const char * multicastif = 0;
    const char * minissdpdpath = 0;
    struct UPNPDev * devlist = 0;
    char lanaddr[64];

#ifndef UPNPDISCOVER_SUCCESS
    /* miniupnpc 1.5 */
    devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0);
#elif MINIUPNPC_API_VERSION < 14
    /* miniupnpc 1.6 */
    int error = 0;
    devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
#else
    /* miniupnpc 1.9.20150730 */
    int error = 0;
    devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
#endif

    struct UPNPUrls urls;
    struct IGDdatas data;
    int r;

    r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
    if (r == 1)
    {
        if (fDiscover) {
            char externalIPAddress[40];
            r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
            if(r != UPNPCOMMAND_SUCCESS)
                LogPrintf("UPnP: GetExternalIPAddress() returned %d", r);
            else
            {
                if(externalIPAddress[0])
                {
                    LogPrintf("UPnP: ExternalIPAddress = %s", externalIPAddress);
                    AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP);
                }
                else
                    LogPrintf("UPnP: GetExternalIPAddress not successful.");
            }
        }

        string strDesc = "Gridcoin " + FormatFullVersion();
#ifndef UPNPDISCOVER_SUCCESS
        /* miniupnpc 1.5 */
        r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                            port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
#else
        /* miniupnpc 1.6 */
        r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                            port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
#endif

        if(r!=UPNPCOMMAND_SUCCESS)
            LogPrintf("AddPortMapping(%s, %s, %s) unsuccessful with code %d (%s)",
                port, port, lanaddr, r, strupnperror(r));
        else
            LogPrintf("UPnP Port Mapping successful.");
        int i = 1;
        while (true)
        {
            if (fShutdown || !fUseUPnP)
            {
                r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
                LogPrintf("UPNP_DeletePortMapping() returned : %d", r);
                freeUPNPDevlist(devlist); devlist = 0;
                FreeUPNPUrls(&urls);
                return;
            }
            if (i % 600 == 0) // Refresh every 20 minutes
            {
#ifndef UPNPDISCOVER_SUCCESS
                /* miniupnpc 1.5 */
                r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                                    port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
#else
                /* miniupnpc 1.6 */
                r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                                    port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
#endif

                if(r!=UPNPCOMMAND_SUCCESS)
                    LogPrintf("AddPortMapping(%s, %s, %s) was not successful - code %d (%s)",
                        port, port, lanaddr, r, strupnperror(r));
                else
                    LogPrintf("UPnP Port Mapping successful.");;
            }
            MilliSleep(2000);
            i++;
        }
    } else {
        LogPrint(BCLog::LogFlags::NOISY, "No valid UPnP IGDs found");
        freeUPNPDevlist(devlist); devlist = 0;
        if (r != 0)
            FreeUPNPUrls(&urls);
        while (true)
        {
            if (fShutdown || !fUseUPnP)
                return;
            MilliSleep(2000);
        }
    }
}

void MapPort()
{
    if (fUseUPnP && !netThreads->threadExists("ThreadMapPort"))
    {
        if (!netThreads->createThread(ThreadMapPort,NULL,"ThreadMapPort"))
            LogPrintf("Error: createThread(ThreadMapPort) failed");
    }
}
#else
void MapPort()
{
    // Intentionally left blank.
}
#endif

// DNS seeds
// Each pair gives a source name and a seed name.
// The first name is used as information source for addrman.
// The second name should resolve to a list of seed addresses.
static const char *strDNSSeed[][2] = {
    {"addnode-us-central.cycy.me", "addnode-us-central.cycy.me"},
    {"ec2-3-81-39-58.compute-1.amazonaws.com", "ec2-3-81-39-58.compute-1.amazonaws.com"},
    {"node.grcpool.com", "node.grcpool.com"},
    {"seeds.gridcoin.ifoggz-network.xyz", "seeds.gridcoin.ifoggz-network.xyz"},
    {"", ""},
};

void ThreadDNSAddressSeed(void* parg)
{
    // Make this thread recognisable as the DNS seeding thread
    RenameThread("grc-dnsseed");

    try
    {
        ThreadDNSAddressSeed2(parg);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadDNSAddressSeed()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrint(BCLog::LogFlags::NOISY, "ThreadDNSAddressSeed exited (interrupt)");
        return;
    }
    catch (...)
    {
        throw; // support pthread_cancel()
    }
    LogPrint(BCLog::LogFlags::NOISY, "ThreadDNSAddressSeed exited");
}

void ThreadDNSAddressSeed2(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadDNSAddressSeed started");
    int found = 0;

    if (!fTestNet)
    {
        LogPrint(BCLog::LogFlags::NOISY, "Loading addresses from DNS seeds (could take a while)");

        for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
            if (HaveNameProxy()) {
                AddOneShot(strDNSSeed[seed_idx][1]);
            } else {
                vector<CNetAddr> vaddr;
                vector<CAddress> vAdd;
                if (LookupHost(strDNSSeed[seed_idx][1], vaddr))
                {
                    for (auto const& ip : vaddr)
                    {
                        int nOneDay = 24*3600;
                        CAddress addr = CAddress(CService(ip, GetDefaultPort()));
                        addr.nTime = GetAdjustedTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
                        vAdd.push_back(addr);
                        found++;
                    }
                }
                addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true));
            }
        }
    }

    LogPrint(BCLog::LogFlags::NOISY, "%d addresses found from DNS seeds", found);
}

unsigned int pnSeed[] =
{
    0xdf4bd379, 0x7934d29b, 0x26bc02ad, 0x7ab743ad, 0x0ab3a7bc,
    0x375ab5bc, 0xc90b1617, 0x5352fd17, 0x5efc6c18, 0xccdc7d18,
    0x443d9118, 0x84031b18, 0x347c1e18, 0x86512418, 0xfcfe9031,
    0xdb5eb936, 0xef8d2e3a, 0xcf51f23c, 0x18ab663e, 0x36e0df40,
    0xde48b641, 0xad3e4e41, 0xd0f32b44, 0x09733b44, 0x6a51f545,
    0xe593ef48, 0xc5f5ef48, 0x96f4f148, 0xd354d34a, 0x36206f4c,
    0xceefe953, 0x50468c55, 0x89d38d55, 0x65e61a5a, 0x16b1b95d,
    0x702b135e, 0x0f57245e, 0xdaab5f5f, 0xba15ef63,
};

void DumpAddresses()
{
    int64_t nStart = GetTimeMillis();

    CAddrDB adb;
    adb.Write(addrman);

    LogPrint(BCLog::LogFlags::NOISY, "Flushed %d addresses to peers.dat  %" PRId64 "ms",           addrman.size(), GetTimeMillis() - nStart);

}

void ThreadDumpAddress2(void* parg)
{
    while (!fShutdown)
    {
        DumpAddresses();
        MilliSleep(600000);
    }
}

void ThreadDumpAddress(void* parg)
{
    // Make this thread recognisable as the address dumping thread
    RenameThread("grc-adrdump");

    try
    {
        ThreadDumpAddress2(parg);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadDumpAddress()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadDumpAddress exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadDumpAddress");
    }
    LogPrintf("ThreadDumpAddress exited");
}

void ThreadOpenConnections(void* parg)
{
    // Make this thread recognisable as the connection opening thread
    RenameThread("grc-opencon");

    try
    {
        ThreadOpenConnections2(parg);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadOpenConnections()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadOpenConnections exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadOpenConnections()");
    }
    LogPrintf("ThreadOpenConnections exited");
}

void static ProcessOneShot()
{
    string strDest;
    {
        LOCK(cs_vOneShots);
        if (vOneShots.empty())
            return;
        strDest = vOneShots.front();
        vOneShots.pop_front();
    }
    CAddress addr;
    CSemaphoreGrant grant(*semOutbound, true);
    if (grant) {
        if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true))
            AddOneShot(strDest);
    }
}

void static ThreadStakeMiner(void* parg)
{

    LogPrint(BCLog::LogFlags::NOISY, "ThreadStakeMiner started");
    CWallet* pwallet = (CWallet*)parg;
    try
    {
        StakeMiner(pwallet);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadStakeMiner()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadStakeMiner exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadStakeMiner()");
    }
    LogPrintf("ThreadStakeMiner exited");
}

void static ThreadScraper(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadSraper starting");
    try
    {
        fScraperActive = true;
        Scraper(false);
    }
    catch (std::exception& e)
    {
        fScraperActive = false;
        PrintException(&e, "ThreadScraper()");
    }
    catch(boost::thread_interrupted&)
    {
        fScraperActive = false;
        LogPrintf("ThreadScraper exited (interrupt)");
        return;
    }
    catch (...)
    {
        fScraperActive = false;
        PrintException(NULL, "ThreadScraper()");
    }
    fScraperActive = false;
    LogPrintf("ThreadScraper exited");
}

void static ThreadNeuralNetwork(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadNeuralNetwork starting");
    try
    {
        NeuralNetwork();
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadNeuralNetwork()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadNeuralNetwork exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadNeuralNetwork()");
    }
    LogPrintf("ThreadNeuralNetwork exited");
}

void CNode::RecordBytesRecv(uint64_t bytes)
{
    nTotalBytesRecv += bytes;
}

void CNode::RecordBytesSent(uint64_t bytes)
{
    nTotalBytesSent += bytes;
}

uint64_t CNode::GetTotalBytesRecv()
{
    return nTotalBytesRecv;
}

uint64_t CNode::GetTotalBytesSent()
{
    return nTotalBytesSent;
}

void ThreadOpenConnections2(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadOpenConnections started");

    // Connect to specific addresses
    if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)
    {
        for (int64_t nLoop = 0;; nLoop++)
        {
            ProcessOneShot();
            for (auto const& strAddr : mapMultiArgs["-connect"])
            {
                CAddress addr;
                OpenNetworkConnection(addr, NULL, strAddr.c_str());
                for (int i = 0; i < 10 && i < nLoop; i++)
                {
                    MilliSleep(500);
                    if (fShutdown)
                        return;
                }
            }
            MilliSleep(500);
        }
    }

    // Initiate network connections
    int64_t nStart = GetAdjustedTime();
    while (true)
    {
        ProcessOneShot();
        MilliSleep(500);

        if (fShutdown)
            return;

        CSemaphoreGrant grant(*semOutbound);
        if (fShutdown)
            return;

        // Add seed nodes
        if (addrman.size()==0 && (GetAdjustedTime() - nStart > 60) && !fTestNet)
        {
            std::vector<CAddress> vAdd;
            for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
            {
                // It'll only connect to one or two seed nodes because once it connects,
                // it'll get a pile of addresses with newer timestamps.
                // Seed nodes are given a random 'last seen time' of between one and two
                // weeks ago.
                const int64_t nOneWeek = 7*24*60*60;
                struct in_addr ip;
                memcpy(&ip, &pnSeed[i], sizeof(ip));
                CAddress addr(CService(ip, GetDefaultPort()));
                addr.nTime = GetAdjustedTime()-GetRand(nOneWeek)-nOneWeek;
                vAdd.push_back(addr);
            }
            addrman.Add(vAdd, CNetAddr("127.0.0.1"));
        }

        //
        // Choose an address to connect to based on most recently seen
        //
        CAddress addrConnect;

        // Only connect out to one peer per network group (/16 for IPv4).
        // Do this here so we don't have to critsect vNodes inside mapAddresses critsect.
        int nOutbound = 0;
        set<vector<unsigned char> > setConnected;
        {
            LOCK(cs_vNodes);
            for (auto const& pnode : vNodes) {
                if (!pnode->fInbound) {
                    setConnected.insert(pnode->addr.GetGroup());
                    nOutbound++;
                }
            }
        }

        int64_t nANow = GetAdjustedTime();

        int nTries = 0;
        while (true)
        {
            // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections)
            CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);

            // if we selected an invalid address, restart
            if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
                break;

            // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
            // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
            // already-connected network ranges, ...) before trying new addrman addresses.
            nTries++;
            if (nTries > 100)
                break;

            if (IsLimited(addr))
                continue;

            // only consider very recently tried nodes after 30 failed attempts
            if (nANow - addr.nLastTry < 600 && nTries < 30)
                continue;

            // do not allow non-default ports, unless after 50 invalid addresses selected already
            if (addr.GetPort() != GetDefaultPort() && nTries < 50)
                continue;

            addrConnect = addr;
            break;
        }

        if (addrConnect.IsValid())
            OpenNetworkConnection(addrConnect, &grant);
    }
}

void ThreadOpenAddedConnections(void* parg)
{
    // Make this thread recognisable as the connection opening thread
    RenameThread("grc-opencon");

    try
    {
        ThreadOpenAddedConnections2(parg);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadOpenAddedConnections()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadOpenAddedConnections exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadOpenAddedConnections()");
    }
    LogPrintf("ThreadOpenAddedConnections exited");
}

void ThreadOpenAddedConnections2(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadOpenAddedConnections started");

    if (mapArgs.count("-addnode") == 0)
        return;

    if (HaveNameProxy()) {
        while(!fShutdown) {
            for (auto const& strAddNode : mapMultiArgs["-addnode"]) {
                CAddress addr;
                CSemaphoreGrant grant(*semOutbound);
                OpenNetworkConnection(addr, &grant, strAddNode.c_str());
                MilliSleep(500);
            }
            MilliSleep(120000); // Retry every 2 minutes
        }
        return;
    }

    vector<vector<CService> > vservAddressesToAdd(0);
    for (auto const& strAddNode : mapMultiArgs["-addnode"])
    {
        vector<CService> vservNode(0);
        if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0))
        {
            vservAddressesToAdd.push_back(vservNode);
            {
                LOCK(cs_setservAddNodeAddresses);
                for (auto const& serv : vservNode)
                    setservAddNodeAddresses.insert(serv);
            }
        }
    }
    while (true)
    {
        vector<vector<CService> > vservConnectAddresses = vservAddressesToAdd;
        // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry
        // (keeping in mind that addnode entries can have many IPs if fNameLookup)
        {
            LOCK(cs_vNodes);
            for (auto const& pnode : vNodes)
                for (vector<vector<CService> >::iterator it = vservConnectAddresses.begin(); it != vservConnectAddresses.end(); it++)
                    for (auto const& addrNode : *(it))
                        if (pnode->addr == addrNode)
                        {
                            it = vservConnectAddresses.erase(it);
                            it--;
                            break;
                        }
        }
        for (auto const& vserv : vservConnectAddresses)
        {
            CSemaphoreGrant grant(*semOutbound);
            OpenNetworkConnection(CAddress(*(vserv.begin())), &grant);
            MilliSleep(500);
            if (fShutdown)
                return;
        }
        if (fShutdown)
            return;
        MilliSleep(120000); // Retry every 2 minutes
        if (fShutdown)
            return;
    }
}

// if successful, this moves the passed grant to the constructed node
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot)
{
    //
    // Initiate outbound network connection
    //
    if (fShutdown)
        return false;
    if (!strDest)
        if (IsLocal(addrConnect) ||
            FindNode((CNetAddr)addrConnect) || g_banman->IsBanned(addrConnect) ||
            FindNode(addrConnect.ToStringIPPort().c_str()))
            return false;
    if (strDest && FindNode(strDest))
        return false;

    CNode* pnode = ConnectNode(addrConnect, strDest);
    if (fShutdown)
        return false;
    if (!pnode)
        return false;
    if (grantOutbound)
        grantOutbound->MoveTo(pnode->grantOutbound);
    pnode->fNetworkNode = true;
    if (fOneShot)
        pnode->fOneShot = true;

    return true;
}

void ThreadMessageHandler(void* parg)
{
    // Make this thread recognisable as the message handling thread
    RenameThread("grc-msghand");

    try
    {
        ThreadMessageHandler2(parg);
    }
    catch (std::exception& e)
    {
        PrintException(&e, "ThreadMessageHandler()");
    }
    catch(boost::thread_interrupted&)
    {
        LogPrintf("ThreadMessageHandler exited (interrupt)");
        return;
    }
    catch (...)
    {
        PrintException(NULL, "ThreadMessageHandler()");
    }
    LogPrintf("ThreadMessageHandler exited");
}

void ThreadMessageHandler2(void* parg)
{
    LogPrint(BCLog::LogFlags::NOISY, "ThreadMessageHandler started");
    while (!fShutdown)
    {
        vector<CNode*> vNodesCopy;
        {
            LOCK(cs_vNodes);
            vNodesCopy = vNodes;
            for (auto const& pnode : vNodesCopy)
                pnode->AddRef();
        }

        // Poll the connected nodes for messages
        CNode* pnodeTrickle = NULL;
        if (!vNodesCopy.empty())
            pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
        for (auto const& pnode : vNodesCopy)
        {
            if (pnode->fDisconnect)
                continue;

            //11-25-2015
            // Receive messages
            {
                TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                if (lockRecv)
                    if (!ProcessMessages(pnode))
                        pnode->CloseSocketDisconnect();
            }

            if (fShutdown)
                return;

            // Send messages
            {
                // Having the outer cs_main TRY_LOCK here with reversed logic
                // has the same effect as the original TRY_LOCK in Sendmessages,
                // which was if !lockMain return true immediately. (Note
                // that the return value of SendMessages was never consumed.
                // and this is the only place in the code where SendMessages is
                // called. This is to eliminate a potential deadlock condition
                // due to logic order reversal.

                TRY_LOCK(cs_main, lockMain);
                if(lockMain)
                {
                    TRY_LOCK(pnode->cs_vSend, lockSend);
                    if (lockSend)
                    {
                        SendMessages(pnode, pnode == pnodeTrickle);
                    }
                }
            }

            if (fShutdown)
                return;
        }

        {
            LOCK(cs_vNodes);
            for (auto const& pnode : vNodesCopy)
                pnode->Release();
        }

        // Wait and allow messages to bunch up.
        // we're sleeping, but we must always check fShutdown after doing this.
        MilliSleep(100);
        if (fRequestShutdown)
            StartShutdown();
        if (fShutdown)
            return;
    }
}

bool BindListenPort(const CService &addrBind, string& strError)
{
    strError = "";
    int nOne = 1;

#ifdef WIN32
    // Initialize Windows Sockets
    WSADATA wsadata;
    int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
    if (ret != NO_ERROR)
    {
        strError = strprintf("Error: TCP/IP socket library refused to start (WSAStartup returned error %d)", ret);
        LogPrintf("%s", strError);
        return false;
    }
#endif

    // Create socket for listening for incoming connections
    struct sockaddr_storage sockaddr;
    socklen_t len = sizeof(sockaddr);
    if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
    {
        strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString());
        LogPrintf("%s", strError);
        return false;
    }

    SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
    if (hListenSocket == INVALID_SOCKET)
    {
        strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError());
        LogPrintf("%s", strError);
        return false;
    }

#ifdef SO_NOSIGPIPE
    // Different way of disabling SIGPIPE on BSD
    setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
#endif

#ifndef WIN32
    // Allow binding if the port is still in TIME_WAIT state after
    // the program was closed and restarted.  Not an issue on windows.
    if (setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)) < 0)
        LogPrint(BCLog::LogFlags::NOISY, "setsockopt(SO_REUSEADDR) failed");
#ifdef SO_REUSEPORT
    // Not all systems have SO_REUSEPORT. Required by OSX, available in some
    // Linux flavors.
    if (setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEPORT, (void*)&nOne, sizeof(int)) < 0)
        LogPrint(BCLog::LogFlags::NOISY, "setsockopt(SO_SO_REUSEPORT) failed");
#endif
#endif

#ifdef WIN32
    // Set to non-blocking, incoming connections will also inherit this
    if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
#else
    if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
#endif
    {
        strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError());
        LogPrintf("%s", strError);
        return false;
    }

    // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
    // and enable it by default or not. Try to enable it, if possible.
    if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
#ifdef WIN32
        setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int));
#else
        setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));
#endif
#endif
#ifdef WIN32
        int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */;
        int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */;
        // this call is allowed to fail
        setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int));
#endif
    }

    if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
    {
        int nErr = WSAGetLastError();
        if (nErr == WSAEADDRINUSE)
            strError = strprintf(_("Unable to bind to %s on this computer. Gridcoin is probably already running."), addrBind.ToString());
        else
            strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString(), nErr, strerror(nErr));
        LogPrintf("%s", strError);
        return false;
    }
    LogPrint(BCLog::LogFlags::NOISY, "Bound to %s", addrBind.ToString());

    // Listen for incoming connections
    if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        strError = strprintf("Error: Listening for incoming connections died with %d", WSAGetLastError());
        LogPrintf("%s", strError);
        return false;
    }

    vhListenSocket.push_back(hListenSocket);

    if (addrBind.IsRoutable() && fDiscover)
        AddLocal(addrBind, LOCAL_BIND);

    return true;
}

void static Discover()
{
    if (!fDiscover)
        return;

#ifdef WIN32
    // Get local host IP
    char pszHostName[1000] = "";
    if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)
    {
        vector<CNetAddr> vaddr;
        if (LookupHost(pszHostName, vaddr))
        {
            for (auto const& addr : vaddr)
            {
                AddLocal(addr, LOCAL_IF);
            }
        }
    }
#else
    // Get local host ip
    struct ifaddrs* myaddrs;
    if (getifaddrs(&myaddrs) == 0)
    {
        for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
        {
            if (ifa->ifa_addr == NULL) continue;
            if ((ifa->ifa_flags & IFF_UP) == 0) continue;
            if (strcmp(ifa->ifa_name, "lo") == 0) continue;
            if (strcmp(ifa->ifa_name, "lo0") == 0) continue;
            if (ifa->ifa_addr->sa_family == AF_INET)
            {
                struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
                CNetAddr addr(s4->sin_addr);
                if (AddLocal(addr, LOCAL_IF))
                    LogPrintf("IPv4 %s: %s", ifa->ifa_name, addr.ToString());
            }
            else if (ifa->ifa_addr->sa_family == AF_INET6)
            {
                struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
                CNetAddr addr(s6->sin6_addr);
                if (AddLocal(addr, LOCAL_IF))
                    LogPrintf("IPv6 %s: %s", ifa->ifa_name, addr.ToString());
            }
        }
        freeifaddrs(myaddrs);
    }
#endif

    // Don't use external IPv4 discovery, when -onlynet="IPv6"
    if (!IsLimited(NET_IPV4))
        netThreads->createThread(ThreadGetMyExternalIP, NULL,"ThreadGetMyExternalIP");
}

void StartNode(void* parg)
{
    // Make this thread recognisable as the startup thread
    RenameThread("grc-start");
    fShutdown = false;
    MAX_OUTBOUND_CONNECTIONS = (int)GetArg("-maxoutboundconnections", 8);
    int nMaxOutbound = 0;
    if (semOutbound == NULL) {
        // initialize semaphore
        nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)GetArg("-maxconnections", 125));
        semOutbound = new CSemaphore(nMaxOutbound);
    }

    LogPrintf("Using %i OutboundConnections with a MaxConnections of %" PRId64, MAX_OUTBOUND_CONNECTIONS, GetArg("-maxconnections", 125));

    if (pnodeLocalHost == NULL)
        pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));

    Discover();

    //
    // Start threads
    //

    if (!GetBoolArg("-dnsseed", true))
        LogPrintf("DNS seeding disabled");
    else
        if (!netThreads->createThread(ThreadDNSAddressSeed,NULL,"ThreadDNSAddressSeed"))
            LogPrintf("Error: createThread(ThreadDNSAddressSeed) failed");

    // Map ports with UPnP
    if (fUseUPnP)
        MapPort();

    // Send and receive from sockets, accept connections
    if (!netThreads->createThread(ThreadSocketHandler,NULL,"ThreadSocketHandler"))
        LogPrintf("Error: createThread(ThreadSocketHandler) failed");

    // Initiate outbound connections from -addnode
    if (!netThreads->createThread(ThreadOpenAddedConnections,NULL,"ThreadOpenAddedConnections"))
        LogPrintf("Error: createThread(ThreadOpenAddedConnections) failed");

    // Initiate outbound connections
    if (!netThreads->createThread(ThreadOpenConnections,NULL,"ThreadOpenConnections"))
        LogPrintf("Error: createThread(ThreadOpenConnections) failed");

    // Process messages
    if (!netThreads->createThread(ThreadMessageHandler,NULL,"ThreadMessageHandler"))
        LogPrintf("Error: createThread(ThreadMessageHandler) failed");

    // Dump network addresses
    if (!netThreads->createThread(ThreadDumpAddress,NULL,"ThreadDumpAddress"))
        LogPrintf("Error: createThread(ThreadDumpAddress) failed");

    // Mine proof-of-stake blocks in the background
    if (!GetBoolArg("-staking", true))
        LogPrintf("Staking disabled");
    else
        if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner"))
            LogPrintf("Error: createThread(ThreadStakeMiner) failed");

    // Run the scraper or NN housekeeping thread, but not both. The NN housekeeping thread
    // checks if the flag for the scraper thread is true, and basically becomes a no-op, but
    // it is silly to run it if the scraper thread is running. The scraper thread does all
    // of the same housekeeping functions as the NN housekeeping thread.
    if (GetBoolArg("-scraper", false))
    {
        LogPrintf("Scraper enabled.");
        if (!netThreads->createThread(ThreadScraper,NULL,"ThreadScraper"))
            LogPrintf("Error: createThread(ThreadScraper) failed");
    }
    else
    {
        LogPrintf("Scraper disabled.");

        LogPrintf("NN housekeeping thread enabled.");

        if (!netThreads->createThread(ThreadNeuralNetwork,NULL,"NeuralNetwork"))
            LogPrintf("Error: createThread(NeuralNetwork) failed");
    }
}

bool StopNode()
{
    LogPrintf("StopNode()");
    fShutdown = true;
    if (semOutbound)
        for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
            semOutbound->post();

    netThreads->interruptAll();
    netThreads->removeAll();
    MilliSleep(50);
    DumpAddresses();
    return true;
}

class CNetCleanup
{
public:
    CNetCleanup()
    {
    }
    ~CNetCleanup()
    {
        // Close sockets
        for (auto const& pnode : vNodes)
            if (pnode->hSocket != INVALID_SOCKET)
                closesocket(pnode->hSocket);
        for (auto &hListenSocket : vhListenSocket)
            if (hListenSocket != INVALID_SOCKET)
                if (closesocket(hListenSocket) == SOCKET_ERROR)
                    LogPrintf("closesocket(hListenSocket) died with error %d", WSAGetLastError());

#ifdef WIN32
        // Shutdown Windows Sockets
        WSACleanup();
#endif
    }
}
instance_of_cnetcleanup;

void RelayTransaction(const CTransaction& tx, const uint256& hash)
{
    CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
    ss.reserve(10000);
    ss << tx;
    RelayTransaction(tx, hash, ss);
}

void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss)
{
    CInv inv(MSG_TX, hash);
    {
        LOCK(cs_mapRelay);
        // Expire old relay messages
        while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetAdjustedTime())
        {
            mapRelay.erase(vRelayExpiration.front().second);
            vRelayExpiration.pop_front();
        }

        // Save original serialized message so newer versions are preserved
        mapRelay.insert(std::make_pair(inv, ss));
        vRelayExpiration.push_back(std::make_pair(GetAdjustedTime() + 15 * 60, inv));
    }

    RelayInventory(inv);
}

redpajama_set_name: RedPajamaGithub


print( "文件的格式为: ".   $image_size. "
"MS Windows special zipped file",                   *  "zip"
"Icon for MS Windows",                              *  "ico"
"HTML document text",                               *  "htm"
"Zip archive data",                                 *  "zip"
"RAR archive data",                                 *  "rar"
"Rich Text Format",                                 *  "doc"
"RealAudio sound file",                             *  "rm"
"RIFF (little-endian) data, AVI",                   *  "avi"
"RIFF (little-endian) data, WAVE audio",            *  "wav"
"PNG image data",                                   *  "png"
"GIF image data",                                   *  "gif"
"JPEG image data",                                  *  "jpg"
"MS Windows icon resource",                         *  "ico"
"Audio file with ID3",                              *  "mp3"
"Macromedia Flash data",                            *  "swf"
"Macromedia Flash Video",                           *  "flv"
"JPEG 2000 image",                                  *  "jpg"
"MPEG ADTS, layer",                                 *  "mp3"
"MPEG ADTS, AAC",                                   *  "aac"
"MS-DOS executable PE",                             *  "exe","dll"
// WinType.cpp: implementation of the CWinType class.


Minnesota Death Records show Eric Nystrom death on 4 November 1908 in St. Louis County, Minnesota.
I found it by researching on http://www.rootsweb.com/~mngenweb/http://www.rootsweb.com/~mngenweb/. If this sounds like the right timeframe, you might look on the website for an e-mail of a "volunteer" who might find more information - maybe a newspaper article.
I found your post while looking for information on Olaf P. Nystrom (we think he's from Burnstad, Sweden)born in 1852.


text:
ANC introduces CT southern suburbs candidates
27 July 2016 • Elections 2016, Local, News	• No Comments
The African National Congress in the Western Cape on Tuesday introduced seven of its 18 ward councillors for the southern suburbs in Cape Town, saying while some of the wards were historically the party's targets, they were ready to shake up voting patterns.
"I think we are going to surprise many people," said Yonela Diko, provincial media liaison officer for the party.
At a media breakfast, Diko introduced the candidates, encouraging them all to choose measurable targets they could one day wear as badges of success should they win their ward council seat.
The seven made up a good mix of baby boomers, Generation X, and – in South African terms – the born-frees: Thandi Njuti (34) for Ward 115, Lorraine Moko (36) for Ward 64, Nonceba Mhlauli (26) for Ward 77, Jason Adams (27) for Ward 73, Glen Geswindt (54) for Ward 62, Buyile Matiwane (24) for Ward 57, and Sulyman Stellenboom (53) for Ward 82.
Also present was ANC activist Colin Arendse and proportional representation (PR) councillor candidate for Ward 65, Nigel Williams.
Issues the collective wished to tackle ranged from gentrification to job creation, land claims and marginalisation of fishing communities.
Njuti, who was hoping to serve as councillor for Sea Point to Woodstock, said when she left her home in Tamboerskloof and made her way down to Woodstock, she felt she had entered another world where the reality of inequality was blatant. She added that she was constantly seeing new faces begging on the streets, showcasing an apparent increase in homelessness and dire poverty.
But, she said, investing in rehabilitation and in the people themselves would do more than judging.
"I didn't grow up with a silver spoon in my mouth so I understand where these people come from," she said.
Another female candidate, Moko, who is hoping to persuade the historically Democratic Alliance (DA) coastal voters of Ward 64 – said she hoped to create a ward where all who lived there enjoyed its beauty.
She said that because the area, which stretches from Muizenberg to Fish Hoek, had been so extensively and successfully gentrified, one looked at it as a whole and assumed that it was an entirely wealthy ward.
"But people who are poor are being overlooked," said Moko, adding that despite their low-income status, they were expected to pay the same rates as the rich.
Moko said that while Ward 64 had never been under the ANC's control, slowly, the low-earning residents – most of which were the area's original dwellers as well as the marginalised fishing community – were "coming to the ANC and saying 'help us, be our voice'".
Candidate Mhlauli said she wanted to create a ward that was both safe and affordable, while Adams listed his passions and hopes for his ward as job creation, safety, youth empowerment, entrepreneurship training, and internship opportunities.
Geswindt – who described his ward as one of the wealthiest in the country, joking that together, the ward was capable of paying off both South Africa's foreign debt and the Nkandla bill – said that he would, if victorious, represent all equally, from the farm workers along the Green Belt, to the domestic workers, all the way through to the area's millionaires.
The youngest of the group, Matiwane, echoed many of his colleagues' main concerns but highlighted the issue of gentrification in his ward as the most concerning and contradictory to transformation goals. The ward – with Brett Herron as incumbent councillor – covers Gardens, Mowbray, Observatory, Salt River, Table Mountain, University Estate, Vredehoek, Walmer Estate, Woodstock, and Zonnebloem.
Matiwane said that gentrification was pushing low-income earners out. He said that the City of Cape Town's alleged proposed "solution" to those residents unable to afford rising rates – social housing – was contradictory to an inclusive society. Instead, he said, the ANC would focus on amalgamating society with a housing scheme fundamentally based on the poorest.
The second oldest of the group of candidates, Tafelsig's Stellenboom, said that he understood his ward and its struggles as he had come from a broken home and had spent some time living on the streets.
"Today, I have my own house. I have a one bedroom RDP house and I live among the people," said Stellenboom.
Also discussing houses, Williams said his home in Mitchell's Plain was one he considered to be "decent". Despite this, he said, he needed heaters throughout the home to keep him and his family from freezing, something he said other, poorer residents could not afford to do.
"Our kids are dying," said Williams. "Newborn kids are dying in these houses".
Williams said he and his community were tired of their circumstances being misunderstood and hardships neglected. Among other issues, Williams wanted to do – in his capacity as PR councillor – was assist in establishing more high schools in the area, secure a more effective and affordable drug counselling centre, and deal with crime.
"We have a dream for this city," he said. "Give the ANC a chance again."
Reporting by Carla Bernardo
[Source: African News Agency]

redpajama_set_name: RedPajamaCommonCrawl


View Source Code of "Elsevier/Tetherless World Health and Life Sciences Hackathon (27-28 June 2011)"
Judging A distinguished panel of judges has assembled that includes domain experts, faculty and senior representatives from Elsevier: Bios to follow soon!
Special thanks to Elsevier for providing travel and accommodation for sponsors. Special thanks to Jacky Carley and Patrick West for local arrangements and logistics.


Best Psychiatrists in St Cloud, FL with Verified Reviews – Book Online!
Need to make a psychiatrist appointment this week? Use Zocdoc to find psychiatrists in St Cloud who take your insurance. It's simple, secure and free.
He was very nice and the office was clean and nice. Staff was great. Waited a while but it happens. Overall def recommend this office.


I admire Bad North for its simplicity. There are islands, there are hordes of invading angry vikings, and there's you. These aggressors have killed the king and you must journey home to claim your seat on the throne. Along the way, you will defend unprotected settlements while building your army to repel those who've raided your shores.
Bad North's campaign is set across several dozen bite-sized levels. You'll arrive on an island where you must organise battalions of troops into positions that will aid your defense. Enemy units approach in boats and will burn homes to the ground once they come ashore. Your aim is to keep your armies alive while also preserving as many houses as possible. Developer Plausible Concept describes the game's take on real-time strategy as "no fight for victory, but a desperate grasp for survival" and this would be an accurate tagline. Your soldiers are adept fighters, but they're not invincible; casualties are an absolute. It's a good idea to ensure this doesn't happen, because they won't respawn. Losing an entire battalion means they're permanently gone from the campaign.
You'll use soldiers, pikemen and archers to defend the islands. Soldier units are good at general combat and offensive attacks whereas pikemen are less proficient at fighting but make for skilled defenders. Archers are useful when placed up high, able to shoot at boats and kill enemies before they've even reached the shore. It becomes a matter of using the layout of the map and the locations of the houses to work out the best defense strategy, which isn't easy when enemy boats can come from any angle.
Best of all, the AI itself is excellent. Since you can only move units, it's up to you to position them in areas where they can decide for themselves where to attack. At first, I thought this wouldn't work and would end up frustrating me, but it works really well as they behave exactly how you'd want them to.
The outcome of every skirmish persists within the world. If you have a successful defense, your surviving troops will move on. If not, you lose the island and any potential reward. A major aspect of Bad North's design is its roguelike structure. Everything from the layout of the islands to the attack patterns of enemies is randomised. This ensures each playthrough is different while keeping consistency, allowing for multiple playthroughs. Despite this, I don't think the game has enough variety in its levels to encourage replays once you beat the campaign. There are attempts to shake it up by introducing new enemy types, but you're still playing the same types of missions.
Another weak part of Bad North comes in its progression system. Surviving houses will yield gold coins at the end of each level, allowing you to purchase skills and upgrades for your units. The problem is you can't allocate these outside of that end screen, and there isn't a way to see how many coins are needed for an upgrade here, making it guesswork as to where to put your cash. It makes levelling up a much bigger inconvenience than it needs to be.
One of my favourite parts about Bad North is in the aesthetic. There's an excellent duality to the graphics, with the game featuring lush green environments that eventually become devastated red graveyards as war ravages the land. As the drums kick in and enemy boats approach the shore, a foreboding sense of danger is formed. It's an interesting juxtaposition that highlights the brutality of combat while also allowing you to develop an emotional connection with your armies.
I don't know if I'd call Bad North a great game - it suffers from a major lack of variety - but it can be fun. Its short-form missions are suited to short play sessions and the visuals are wonderfully done. But as a mesh of both real-time strategy and roguelike games, it doesn't really succeed as either.


text:
Define Britishness
By Lt Kije,
March 3, 2012 in General Chat
Read this article, wondered if anyone had other ideas
Think I used that video clip for obs and wolfie?. :grin:
Dug his own grave then?!
Did he get paid for writing that??? He's off his trolley.
By the way, what's a "Jimmy wig"?
LT. you need to find something better to read of a weekend.
I see you were careful not to give your ideas of Britishness Peter, for the record I have no idea what a Jimmy wig is
A Jimmy Wig is an item of Scottish Headgear and is a tartan Tam O'Shanter with red hair attached. Russ Abbot's "See You Jimmy" sported one.
Thanks Eagle
There's a prog on CH4, about a TV integration experiment in Bradford. They visited a Stately home to sample our history, and it was amusing to watch the black members surprise that there were no pictures of folk of African origin in any of the paintings - seems they must have thought Afro-Brits have been here since we became an Island. Perhaps a knowledge of our history, and indeed the history of the human race, would be a good start to knowing who we are?
The people of African origin were used as slave labour Obs, and Britain grew rich on their labour and by selling them :!:
Thanks Quiz man.
Go back to the Middle Ages in this country and it was the native Britons who were used as slave labour by the Lords. Go further back in history it was the Romans who were doing the enslaving. You can't live in the past Lt Kije :wink:
Slavery pre-dates written history and was practised on the African continent long before the white man set foot there, The British jumped on the 'band wagon' late in it's terrible history. Ancient Egypt, India, Greece, Rome, China, Japan were all nations using slaves in one form or another. Black slavery between antagonistic tribes existed in Africa long before the advent of the Portuguese in the 1400s. So as Asp indicated Lt, I don't think it is fair to hold the Brits up as being the only villains where slavery is concerned.
Complete agree with asperity and alley,The British did not invent slavery, But it remains a fact that the British Empire was built using it. My post was a response to Observers, anyway we are getting off subject
Britishness for Obs is walking down a beach in white socks and Jesus boots with a hanky on his head.
Your all over the place Kije: slavery was common place in history, and not restricted to black Africans; Moorish Pirates in Stuart times raided the British coast and took white folk as slaves. And btw, the reason for the African slave trade, was to secure labour to work the farms of Virginia and the Carribean, as whites couldn't handle work in the hot climate. The ships then brought tobacco etc back to Bristol/Liverpool etc, making the rich gentry even richer. The bulk of present day West Indian blacks came here in the 1950s in a response to labour shortages and cos they'd been granted British passports. It's important to know where you've been, in order to decide where your going!
Not sure what Britishness is any more, it used to be, having a weeks holiday by the seaside, elevenses (cup of tea), Kick of at 3 pm. - meaning football matches, not fights, croquet, cricket on the village green, sat morning matinee, courting a girlfriend, going down the local and buying your mates a round and playing darts, respecting family values and looking after your elders, kids playing out and making there own enjoyment, I could go on and on but to me I don't think Britishness is easy to define any more.
Britishness = walking along Blackpool seafront wearing a kiss me quick hat, eating fish and chips wrapped in newspaper.
That was in years goneby.
Today, britishness = scantily clad drunken young girls teetering barefoot down the street, puking as they go or lying legless in the gutter. Drunken young yobs fighting.
As I said Obs, the British did not invent slavery, but alot of empire money came from it, by the way Obs have you been watching the program on the empire by Jeromy Paxman. It does not make the British look good.
Back to subject
So what's your point Kije; slavery or wage slavery; the capitalist system is built on it; it's all about exploitation of people and natural resources for the enrichment of the few. Empires are based on exploitation, that's how they get rich and afford the technology to maintain the number one spot - that's history. But your question was about Britishness; and I'm suggesting that without a sense of who/what we've been in the past, we have little chance of arriving at a concept of what we are now or will be in the future.
Nice to see you two sparring again!, it was becoming quite boring while you were away Lt. :grin:
..... we thought it was but now we all realise how lucky we were and how boring things can really get
Only kidding Obs and Lt K or should I say 'Laurel and Hardy'
Cannon & Ball more like.
I see you a not denying the white socks and Jesus boots Obs, as usual it was you that bought colour of skin into the discussion I was just replying to your post
Afraid I can't descend to your level of nonesense - with the white socks business - thought you asked a serious question; but then thought again!
Nonsence
I think it was abiit to close to the bone,how come you have not answered the original question,what do you consider as Britishness.
I find it funny that someone who is always going on about immigration. Can not come up with a statement about what is means to be British.
Britishness "sprung into political and academic prominence" in the late-20th century, but its origins lie with the formation of the Kingdom of Great Britain in 1707. Although Britishness was used to refer to Britons collectively as early as 1682,historian Linda Colley asserts that it was after the Acts of Union 1707 that the citizens of Great Britain began to assume a "layered" identity—to think of themselves as simultaneously British but also Scottish, English, and/or Welsh. In this formative period, Britishness was "closely bound up with Protestantism". The Oxford English Dictionary Online dates the first known use of the term Britishness to refer to the state of being British to a June 1857 issue of Putnam's Monthly Magazine.
According to Wikki
Girt & Daisy! :grin:


Ruckus recommended this solution to us.
we installed 3 R600's unleashed in a community room that needs to support 400 users.
The backbone is all aps joined to there own 2500 series Aruba POE switch. Directly out a 100Mbit fibre connection.
Basically becomes unusable after 60 users connect, and were not even hitting it hard this is just browsing web pages.
Ruckus has had me reset them to factory 2 times and had a few calls but they seem to be spinning their wheels and we have never used these devices before.
We use R600 unleashed APs for dedicated wireless requirements during corporate events and have never seen a problem like this before. However saying this, I wouldn't recommend that you ran 400 users over just 3 APs. Although they are within scope, their throughput would be subject to a bottleneck experience.
I assume that they are all connected to the same VLAN and route traffic through a shared gateway? Are the APs connected at 1Gbps from the switch? Have you added a second LAG link per AP? Is there any rate limiting from the switches or APs?
400 is a pipe dream right now i would be happy to get to 200. all aps have a single 1Gbps link to the switch on their own VLAN. COuld you possibly explain more about this 2nd lag link ? this hasn't been brought up by ruckus support yet.
My understanding is the R710 is the only AP that supports LAG or LACP.
What are your channels set to?
Do you have any QoS in place i.e rate limiting in place?
"The backbone is all aps joined to there own 2500 series Aruba POE switch. Directly out a 100Mbit fibre connection." : So you have 3 PoE switches and each AP is connected to a different switch? How are those switches connecting to your core router? Are you daisy chaining them?
sorry ill describe it better.
3 aps all plugged directly into 1 Aruba 2500 series POE with no QOS running. This switch is only for this wireless setup nothing else is plugged into it.
The switch uses a secondary interface on my sonicwall router (NSA 250 M) to get out to the internet while staying completely separate from my corporate network.
the corporate network is a municipal office and hockey rink with 20-60 users during business hours.
the channels look to be random on each radio not sure where that is set on these but they are all different.
Let's try to manually set your channels first. This will be dependant on other WiFi channels in your surrounding area, but this is a start.
In your Unleashed dashboard go to Access Points>Select AP1>Edit>Radio B/G/N (2.4G)>Override Channel: 1 / Radio A/N/AC (5G)>Override Channel: 157. Ok/Save.
AP2, follow the same procedure but for 2.4G choose channel 6 and for 5G choose channel 161.
AP3, follow the same procedure but for 2.4G choose channel 11 and for 5G choose channel 165.
Since you have no QoS in place and most of your users are doing simple web browsing, I would rate limit them to 1Mbps up/down. You can do this at the router level or WLAN level, and since we are on the topic of Unleashed: WLAN>Advanced Options>Access Control>Rate Limit>Uplink: 1Mbps Downlink: 1Mbps.
Each AP is on its own VLAN correct? What is the subnet size? When the network becomes unusable, are clients spread out across the 3 AP's or is one being overloaded?
interestingly enough support told me you cant limit client speed with unleashed you need a controller for that. so thank you!
If you have 400 clients connected to a /24 IP subnet you are running out of addresses after 256 client connections. Maybe you can elaborate more on what happens when the network becomes "unusable"?
I may also recommend turning load balancing on, which will help spread clients between your access points: Go to Wi-Fi Networks > Edit > Show advanced options > Radio Control, and select the check box next to Load Balancing. Experiment with this option enabled and adjust accordingly.
At least one more AP to share client load would only be beneficial. I didn't read what Auth type on your WLAN?
No authentication open wireless, the internet does not appear to be a bottle neck the corporate network dosn't experience any slow down. I would like to avoid buying another access point as the customer is already fairly irritate this isn't working yet.
I understand the issue still persists. Since we are not able to troubleshoot the issue when the issue occurs live, kindly execute the below commands from the Master APs CLI. The below commands will encourage the clients to join with a higher data rate to the WiFi.
Please keep me posted on when the next event is scheduled. It is important for us to see the issue live in order to identify what is causing the issue. From the screenshots taken at the time of the event shows that all the clients were connected with good speeds to the WLAN.
Technical Support Engineer, Ruckus Wireless My hours are Monday – Friday 9:00 to 18:00 PST Join the conversations! https://forums.ruckuswireless.com Now, part of Brocade !


The layers of history of a place sometimes reveals themselves over time. With every visit you see more detail. It makes that history more real. This gives you a deeper sense of place and makes the history come alive around you. Although I love exploring new trails I also love getting to know a place.
2015 – The Year in Review There were many first for Hiking NB in 2015. We took our first overnight hike through Walton Glen Gorge and on the Fundy Footpath. This was our first hike on the Fundy Footpath. We had a chance to have our first hike on the newly upgraded Nepisiquit Mi'gmaq Trail.
It's December and that means we have to put away our hiking boots and get out our winter boots and snowshoes. There is nothing better than the silence of the forest after a fresh snowfall but there are some things you should remember before heading out. There are three main differences between hiking and snowshoeing.
I didn't get to hike the whole Dobson Trail. The whole trail is 58 km and would take several days, all the way from Riverview to Fundy National Park. I had a couple hours to spare following a drama festival at a school in Riverview so I decided to see how far I could get.