Merge pull request #2937
971bb3e
Added ping time measurement. New RPC "ping" command to request ping. Implemented "pong" message handler. New "pingtime" field in getpeerinfo, to provide results to user. New "pingwait" field, to show pings still in flight, to better see newly lagging peers. (Josh Lehan)
This commit is contained in:
commit
9269d0e96e
6 changed files with 131 additions and 4 deletions
|
@ -229,6 +229,7 @@ static const CRPCCommand vRPCCommands[] =
|
||||||
{ "getbestblockhash", &getbestblockhash, true, false, false },
|
{ "getbestblockhash", &getbestblockhash, true, false, false },
|
||||||
{ "getconnectioncount", &getconnectioncount, true, false, false },
|
{ "getconnectioncount", &getconnectioncount, true, false, false },
|
||||||
{ "getpeerinfo", &getpeerinfo, true, false, false },
|
{ "getpeerinfo", &getpeerinfo, true, false, false },
|
||||||
|
{ "ping", &ping, true, false, false },
|
||||||
{ "addnode", &addnode, true, true, false },
|
{ "addnode", &addnode, true, true, false },
|
||||||
{ "getaddednodeinfo", &getaddednodeinfo, true, true, false },
|
{ "getaddednodeinfo", &getaddednodeinfo, true, true, false },
|
||||||
{ "getdifficulty", &getdifficulty, true, false, false },
|
{ "getdifficulty", &getdifficulty, true, false, false },
|
||||||
|
|
|
@ -153,6 +153,7 @@ extern void EnsureWalletIsUnlocked();
|
||||||
|
|
||||||
extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp
|
extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp
|
||||||
extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp);
|
extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp);
|
||||||
|
extern json_spirit::Value ping(const json_spirit::Array& params, bool fHelp);
|
||||||
extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp);
|
extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp);
|
||||||
extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp);
|
extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp);
|
||||||
|
|
||||||
|
|
85
src/main.cpp
85
src/main.cpp
|
@ -3863,6 +3863,63 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else if (strCommand == "pong")
|
||||||
|
{
|
||||||
|
int64 pingUsecEnd = GetTimeMicros();
|
||||||
|
uint64 nonce = 0;
|
||||||
|
size_t nAvail = vRecv.in_avail();
|
||||||
|
bool bPingFinished = false;
|
||||||
|
std::string sProblem;
|
||||||
|
|
||||||
|
if (nAvail >= sizeof(nonce)) {
|
||||||
|
vRecv >> nonce;
|
||||||
|
|
||||||
|
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
|
||||||
|
if (pfrom->nPingNonceSent != 0) {
|
||||||
|
if (nonce == pfrom->nPingNonceSent) {
|
||||||
|
// Matching pong received, this ping is no longer outstanding
|
||||||
|
bPingFinished = true;
|
||||||
|
int64 pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
|
||||||
|
if (pingUsecTime > 0) {
|
||||||
|
// Successful ping time measurement, replace previous
|
||||||
|
pfrom->nPingUsecTime = pingUsecTime;
|
||||||
|
} else {
|
||||||
|
// This should never happen
|
||||||
|
sProblem = "Timing mishap";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Nonce mismatches are normal when pings are overlapping
|
||||||
|
sProblem = "Nonce mismatch";
|
||||||
|
if (nonce == 0) {
|
||||||
|
// This is most likely a bug in another implementation somewhere, cancel this ping
|
||||||
|
bPingFinished = true;
|
||||||
|
sProblem = "Nonce zero";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sProblem = "Unsolicited pong without ping";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is most likely a bug in another implementation somewhere, cancel this ping
|
||||||
|
bPingFinished = true;
|
||||||
|
sProblem = "Short payload";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sProblem.empty())) {
|
||||||
|
LogPrint("net", "pong %s %s: %s, %"PRI64x" expected, %"PRI64x" received, %zu bytes\n"
|
||||||
|
, pfrom->addr.ToString().c_str()
|
||||||
|
, pfrom->strSubVer.c_str()
|
||||||
|
, sProblem.c_str()
|
||||||
|
, pfrom->nPingNonceSent
|
||||||
|
, nonce
|
||||||
|
, nAvail);
|
||||||
|
}
|
||||||
|
if (bPingFinished) {
|
||||||
|
pfrom->nPingNonceSent = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
else if (strCommand == "alert")
|
else if (strCommand == "alert")
|
||||||
{
|
{
|
||||||
CAlert alert;
|
CAlert alert;
|
||||||
|
@ -4084,15 +4141,35 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||||
if (pto->nVersion == 0)
|
if (pto->nVersion == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Keep-alive ping. We send a nonce of zero because we don't use it anywhere
|
//
|
||||||
// right now.
|
// Message: ping
|
||||||
|
//
|
||||||
|
bool pingSend = false;
|
||||||
|
if (pto->fPingQueued) {
|
||||||
|
// RPC ping request by user
|
||||||
|
pingSend = true;
|
||||||
|
}
|
||||||
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) {
|
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) {
|
||||||
|
// Ping automatically sent as a keepalive
|
||||||
|
pingSend = true;
|
||||||
|
}
|
||||||
|
if (pingSend) {
|
||||||
uint64 nonce = 0;
|
uint64 nonce = 0;
|
||||||
if (pto->nVersion > BIP0031_VERSION)
|
while (nonce == 0) {
|
||||||
|
RAND_bytes((unsigned char*)&nonce, sizeof(nonce));
|
||||||
|
}
|
||||||
|
pto->nPingNonceSent = nonce;
|
||||||
|
pto->fPingQueued = false;
|
||||||
|
if (pto->nVersion > BIP0031_VERSION) {
|
||||||
|
// Take timestamp as close as possible before transmitting ping
|
||||||
|
pto->nPingUsecStart = GetTimeMicros();
|
||||||
pto->PushMessage("ping", nonce);
|
pto->PushMessage("ping", nonce);
|
||||||
else
|
} else {
|
||||||
|
// Peer is too old to support ping command with nonce, pong will never arrive, disable timing
|
||||||
|
pto->nPingUsecStart = 0;
|
||||||
pto->PushMessage("ping");
|
pto->PushMessage("ping");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start block sync
|
// Start block sync
|
||||||
if (pto->fStartSync && !fImporting && !fReindex) {
|
if (pto->fStartSync && !fImporting && !fReindex) {
|
||||||
|
|
15
src/net.cpp
15
src/net.cpp
|
@ -620,6 +620,21 @@ void CNode::copyStats(CNodeStats &stats)
|
||||||
X(nSendBytes);
|
X(nSendBytes);
|
||||||
X(nRecvBytes);
|
X(nRecvBytes);
|
||||||
stats.fSyncNode = (this == pnodeSync);
|
stats.fSyncNode = (this == pnodeSync);
|
||||||
|
|
||||||
|
// 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 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.dPingWait = (((double)nPingUsecWait) / 1e6);
|
||||||
}
|
}
|
||||||
#undef X
|
#undef X
|
||||||
|
|
||||||
|
|
12
src/net.h
12
src/net.h
|
@ -119,6 +119,8 @@ public:
|
||||||
uint64 nSendBytes;
|
uint64 nSendBytes;
|
||||||
uint64 nRecvBytes;
|
uint64 nRecvBytes;
|
||||||
bool fSyncNode;
|
bool fSyncNode;
|
||||||
|
double dPingTime;
|
||||||
|
double dPingWait;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,6 +236,12 @@ public:
|
||||||
CCriticalSection cs_inventory;
|
CCriticalSection cs_inventory;
|
||||||
std::multimap<int64, CInv> mapAskFor;
|
std::multimap<int64, CInv> mapAskFor;
|
||||||
|
|
||||||
|
// Ping time measurement
|
||||||
|
uint64 nPingNonceSent;
|
||||||
|
int64 nPingUsecStart;
|
||||||
|
int64 nPingUsecTime;
|
||||||
|
bool fPingQueued;
|
||||||
|
|
||||||
CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION)
|
CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION)
|
||||||
{
|
{
|
||||||
nServices = 0;
|
nServices = 0;
|
||||||
|
@ -268,6 +276,10 @@ public:
|
||||||
fRelayTxes = false;
|
fRelayTxes = false;
|
||||||
setInventoryKnown.max_size(SendBufferSize() / 1000);
|
setInventoryKnown.max_size(SendBufferSize() / 1000);
|
||||||
pfilter = new CBloomFilter();
|
pfilter = new CBloomFilter();
|
||||||
|
nPingNonceSent = 0;
|
||||||
|
nPingUsecStart = 0;
|
||||||
|
nPingUsecTime = 0;
|
||||||
|
fPingQueued = false;
|
||||||
|
|
||||||
// Be shy and don't send version until we hear
|
// Be shy and don't send version until we hear
|
||||||
if (hSocket != INVALID_SOCKET && !fInbound)
|
if (hSocket != INVALID_SOCKET && !fInbound)
|
||||||
|
|
|
@ -19,6 +19,24 @@ Value getconnectioncount(const Array& params, bool fHelp)
|
||||||
return (int)vNodes.size();
|
return (int)vNodes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value ping(const Array& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 0)
|
||||||
|
throw runtime_error(
|
||||||
|
"ping\n"
|
||||||
|
"Requests that a ping be sent to all other nodes, to measure ping time.\n"
|
||||||
|
"Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
|
||||||
|
"Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.");
|
||||||
|
|
||||||
|
// Request that each node send a ping during next message processing pass
|
||||||
|
LOCK(cs_vNodes);
|
||||||
|
BOOST_FOREACH(CNode* pNode, vNodes) {
|
||||||
|
pNode->fPingQueued = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Value::null;
|
||||||
|
}
|
||||||
|
|
||||||
static void CopyNodeStats(std::vector<CNodeStats>& vstats)
|
static void CopyNodeStats(std::vector<CNodeStats>& vstats)
|
||||||
{
|
{
|
||||||
vstats.clear();
|
vstats.clear();
|
||||||
|
@ -54,6 +72,9 @@ Value getpeerinfo(const Array& params, bool fHelp)
|
||||||
obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes));
|
obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes));
|
||||||
obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes));
|
obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes));
|
||||||
obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected));
|
obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected));
|
||||||
|
obj.push_back(Pair("pingtime", stats.dPingTime));
|
||||||
|
if (stats.dPingWait > 0.0)
|
||||||
|
obj.push_back(Pair("pingwait", stats.dPingWait));
|
||||||
obj.push_back(Pair("version", stats.nVersion));
|
obj.push_back(Pair("version", stats.nVersion));
|
||||||
obj.push_back(Pair("subver", stats.strSubVer));
|
obj.push_back(Pair("subver", stats.strSubVer));
|
||||||
obj.push_back(Pair("inbound", stats.fInbound));
|
obj.push_back(Pair("inbound", stats.fInbound));
|
||||||
|
|
Loading…
Reference in a new issue