diff options
Diffstat (limited to 'src/btfrv.cpp')
| -rw-r--r-- | src/btfrv.cpp | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp index 63e092e..7efd38b 100644 --- a/src/btfrv.cpp +++ b/src/btfrv.cpp | |||
| @@ -258,10 +258,57 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port, | |||
| 258 | return (RvSocket)s; | 258 | return (RvSocket)s; |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | bool RvServiceWaitPaired(RvSocket s) | 261 | bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs) |
| 262 | { | 262 | { |
| 263 | // A registration socket is idle on purpose -- we announce ourselves and | ||
| 264 | // wait, possibly for a long time, for someone to dial. ConnectTo() clears | ||
| 265 | // the socket timeout for exactly that reason, and with no timeout at all | ||
| 266 | // this recv() has no way to end when the path dies without either side | ||
| 267 | // saying so: a NAT drops the idle mapping, a relay restarts, a route | ||
| 268 | // changes. No FIN arrives, nothing is readable, and the call never | ||
| 269 | // returns. The thread stays alive and asleep, so ThreadBtfAccept never | ||
| 270 | // reaches the `continue` that would register again, and the node goes deaf | ||
| 271 | // while every outward sign says it is healthy. | ||
| 272 | // | ||
| 273 | // Seen in production on the bootstrap seed: 27 minutes without a block, | ||
| 274 | // eight behind the network, relay reachable throughout, fixed instantly by | ||
| 275 | // a restart. | ||
| 276 | // | ||
| 277 | // TCP keepalive is the obvious answer and it does not work here -- I | ||
| 278 | // measured it. With probes every 5s and a 2-probe limit, a socket whose | ||
| 279 | // replies were being dropped was still ESTAB after four minutes, the probe | ||
| 280 | // counter stuck at zero. So the timeout is ours to enforce, at the one | ||
| 281 | // place that knows what waiting means. | ||
| 282 | // | ||
| 283 | // Expiring is not a failure. It costs one reconnect per interval and | ||
| 284 | // returns the loop to a known state. | ||
| 285 | if (nTimeoutSecs > 0) | ||
| 286 | { | ||
| 287 | #ifdef _WIN32 | ||
| 288 | DWORD tv = (DWORD)nTimeoutSecs * 1000; | ||
| 289 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); | ||
| 290 | #else | ||
| 291 | struct timeval tv; tv.tv_sec = nTimeoutSecs; tv.tv_usec = 0; | ||
| 292 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); | ||
| 293 | #endif | ||
| 294 | } | ||
| 295 | |||
| 263 | unsigned char paired = 0; | 296 | unsigned char paired = 0; |
| 264 | return ReadN((SOCKET)s, &paired, 1) && paired == 0x01; | 297 | bool fOk = ReadN((SOCKET)s, &paired, 1) && paired == 0x01; |
| 298 | |||
| 299 | // Hand back a socket that blocks again: from here it is a data tunnel, and | ||
| 300 | // a tunnel legitimately sits quiet between messages. | ||
| 301 | if (fOk && nTimeoutSecs > 0) | ||
| 302 | { | ||
| 303 | #ifdef _WIN32 | ||
| 304 | DWORD tvOff = 0; | ||
| 305 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tvOff, sizeof(tvOff)); | ||
| 306 | #else | ||
| 307 | struct timeval tvOff; tvOff.tv_sec = 0; tvOff.tv_usec = 0; | ||
| 308 | setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, &tvOff, sizeof(tvOff)); | ||
| 309 | #endif | ||
| 310 | } | ||
| 311 | return fOk; | ||
| 265 | } | 312 | } |
| 266 | 313 | ||
| 267 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, | 314 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, |