From a9d9f0f5f72ab1f9ebc2f76bfe3b7921fa2826d7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 30 Apr 2013 18:42:01 +0200 Subject: [PATCH] Do not kill connections on recv buffer overflow Instead of killing a connection when the receive buffer overflows, just temporarily halt receiving before that happens. Also, no matter what, always allow at least one full message in the receive buffer (otherwise blocks larger than the configured buffer size would pause indefinitely). --- src/net.cpp | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 3fa48ae484..54ed1d9b51 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -841,19 +841,39 @@ void ThreadSocketHandler() { if (pnode->hSocket == INVALID_SOCKET) continue; + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + have_fds = true; + + // Implement the following logic: + // * If there is data to send, select() for sending data. As this only + // happens when optimistic write failed, we choose to first drain the + // write buffer in this case before receiving more. This avoids + // needlessly queueing received data, if the remote peer is not themselves + // receiving data. This means properly utilizing TCP flow control signalling. + // * Otherwise, if there is no (complete) message in the receive buffer, + // or there is space left in the buffer, select() for receiving data. + // * (if neither of the above applies, there is certainly one message + // in the receiver buffer ready to be processed). + // Together, that means that at least one of the following is always possible, + // so we don't deadlock: + // * We send some data. + // * We wait for data to be received (and disconnect after timeout). + // * We process a message in the buffer (message handler thread). { 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; + if (lockSend && !pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; } } + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv && ( + pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || + pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + FD_SET(pnode->hSocket, &fdsetRecv); + } } } @@ -959,12 +979,7 @@ void ThreadSocketHandler() TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) { - if (pnode->GetTotalRecvSize() > ReceiveFloodSize()) { - if (!pnode->fDisconnect) - printf("socket recv flood control disconnect (%u bytes)\n", pnode->GetTotalRecvSize()); - pnode->CloseSocketDisconnect(); - } - else { + { // typical socket buffer is 8K-64K char pchBuf[0x10000]; int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);