MCPcopy Create free account
hub / github.com/bitcoin/bitcoin / RecvUntilTerminator

Method RecvUntilTerminator

src/util/sock.cpp:297–378  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

295}
296
297std::string Sock::RecvUntilTerminator(uint8_t terminator,
298 std::chrono::milliseconds timeout,
299 CThreadInterrupt& interrupt,
300 size_t max_data) const
301{
302 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
303 std::string data;
304 bool terminator_found{false};
305
306 // We must not consume any bytes past the terminator from the socket.
307 // One option is to read one byte at a time and check if we have read a terminator.
308 // However that is very slow. Instead, we peek at what is in the socket and only read
309 // as many bytes as possible without crossing the terminator.
310 // Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
311 // one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
312 // at a time is about 50 times slower.
313
314 for (;;) {
315 if (data.size() >= max_data) {
316 throw std::runtime_error(
317 strprintf("Received too many bytes without a terminator (%u)", data.size()));
318 }
319
320 char buf[512];
321
322 const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
323
324 switch (peek_ret) {
325 case -1: {
326 const int err{WSAGetLastError()};
327 if (IOErrorIsPermanent(err)) {
328 throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
329 }
330 break;
331 }
332 case 0:
333 throw std::runtime_error("Connection unexpectedly closed by peer");
334 default:
335 auto end = buf + peek_ret;
336 auto terminator_pos = std::find(buf, end, terminator);
337 terminator_found = terminator_pos != end;
338
339 const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
340 static_cast<size_t>(peek_ret)};
341
342 const ssize_t read_ret{Recv(buf, try_len, 0)};
343
344 if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
345 throw std::runtime_error(
346 strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
347 "peek claimed %u bytes are available",
348 read_ret, try_len, peek_ret));
349 }
350
351 // Don't include the terminator in the output.
352 const size_t append_len{terminator_found ? try_len - 1 : try_len};
353
354 data.append(buf, buf + append_len);

Callers 4

AcceptMethod · 0.80
BOOST_AUTO_TEST_CASEFunction · 0.80
i2p.cppFile · 0.80

Calls 3

IOErrorIsPermanentFunction · 0.85
NetworkErrorStringFunction · 0.85
sizeMethod · 0.45

Tested by 1

BOOST_AUTO_TEST_CASEFunction · 0.64