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).
This commit is contained in:
parent
1c621e70be
commit
a9d9f0f5f7
1 changed files with 30 additions and 15 deletions
43
src/net.cpp
43
src/net.cpp
|
@ -841,19 +841,39 @@ void ThreadSocketHandler()
|
||||||
{
|
{
|
||||||
if (pnode->hSocket == INVALID_SOCKET)
|
if (pnode->hSocket == INVALID_SOCKET)
|
||||||
continue;
|
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);
|
FD_SET(pnode->hSocket, &fdsetError);
|
||||||
hSocketMax = max(hSocketMax, pnode->hSocket);
|
hSocketMax = max(hSocketMax, pnode->hSocket);
|
||||||
have_fds = true;
|
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 && !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);
|
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
|
||||||
if (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
|
// typical socket buffer is 8K-64K
|
||||||
char pchBuf[0x10000];
|
char pchBuf[0x10000];
|
||||||
int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
||||||
|
|
Loading…
Reference in a new issue