Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
252return true;253#endif /* USE_POLL */254}256void Sock::SendComplete(std::span<const unsigned char> data,257std::chrono::milliseconds timeout,258CThreadInterrupt& interrupt) const259{260const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;261size_t sent{0};263for (;;) {264const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};266if (ret > 0) {267sent += static_cast<size_t>(ret);268if (sent == data.size()) {269break;270}271} else {272const int err{WSAGetLastError()};273if (IOErrorIsPermanent(err)) {274throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));275}276}278const auto now = GetTime<std::chrono::milliseconds>();280if (now >= deadline) {281throw std::runtime_error(strprintf(282"Send timeout (sent only %u of %u bytes before that)", sent, data.size()));283}285if (interrupt) {286throw std::runtime_error(strprintf(287"Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));288}290// Wait for a short while (or the socket to become ready for sending) before retrying291// if nothing was sent.292const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});293(void)Wait(wait_time, SendEvent);294}295}297void Sock::SendComplete(std::span<const char> data,298std::chrono::milliseconds timeout,299CThreadInterrupt& interrupt) const300{301SendComplete(MakeUCharSpan(data), timeout, interrupt);302}304std::string Sock::RecvUntilTerminator(uint8_t terminator,305std::chrono::milliseconds timeout,306CThreadInterrupt& interrupt,307size_t max_data) const308{309const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;310std::string data;311bool terminator_found{false};313// We must not consume any bytes past the terminator from the socket.314// One option is to read one byte at a time and check if we have read a terminator.315// However that is very slow. Instead, we peek at what is in the socket and only read316// as many bytes as possible without crossing the terminator.317// Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read318// one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte319// at a time is about 50 times slower.321for (;;) {322if (data.size() >= max_data) {323throw std::runtime_error(324strprintf("Received too many bytes without a terminator (%u)", data.size()));325}327char buf[512];329const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};331switch (peek_ret) {332case -1: {333const int err{WSAGetLastError()};334if (IOErrorIsPermanent(err)) {335throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));336}337break;338}339case 0:340throw std::runtime_error("Connection unexpectedly closed by peer");341default:342auto end = buf + peek_ret;343auto terminator_pos = std::find(buf, end, terminator);344terminator_found = terminator_pos != end;346const size_t try_len{terminator_found ? terminator_pos - buf + 1 :347static_cast<size_t>(peek_ret)};349const ssize_t read_ret{Recv(buf, try_len, 0)};351if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {352throw std::runtime_error(353strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "354"peek claimed %u bytes are available",355read_ret, try_len, peek_ret));356}358// Don't include the terminator in the output.359const size_t append_len{terminator_found ? try_len - 1 : try_len};361data.append(buf, buf + append_len);363if (terminator_found) {364return data;365}366}368const auto now = GetTime<std::chrono::milliseconds>();370if (now >= deadline) {371throw std::runtime_error(strprintf(372"Receive timeout (received %u bytes without terminator before that)", data.size()));373}375if (interrupt) {376throw std::runtime_error(strprintf(377"Receive interrupted (received %u bytes without terminator before that)",378data.size()));379}381// Wait for a short while (or the socket to become ready for reading) before retrying.382const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});383(void)Wait(wait_time, RecvEvent);384}385}387bool Sock::IsConnected(std::string& errmsg) const388{389if (m_socket == INVALID_SOCKET) {390errmsg = "not connected";391return false;392}394char c;395switch (Recv(&c, sizeof(c), MSG_PEEK)) {396case -1: {397const int err = WSAGetLastError();398if (IOErrorIsPermanent(err)) {399errmsg = NetworkErrorString(err);400return false;401}402return true;403}404case 0:405errmsg = "closed";406return false;407default:408return true;409}410}412void Sock::Close()413{414if (m_socket == INVALID_SOCKET) {415return;416}417#ifdef WIN32418int ret = closesocket(m_socket);419#else420int ret = close(m_socket);421#endif422if (ret) {423LogWarning("Error closing socket %d: %s", m_socket, NetworkErrorString(WSAGetLastError()));424}425m_socket = INVALID_SOCKET;426}428bool Sock::operator==(SOCKET s) const429{430return m_socket == s;431};433std::string NetworkErrorString(int err)434{435#if defined(WIN32)436return Win32ErrorString(err);437#else438// On BSD sockets implementations, NetworkErrorString is the same as SysErrorString.439return SysErrorString(err);440#endif441}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.