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.
2001}2002}2003}2004{2005// Delete disconnected nodes2006std::list<CNode*> nodes_disconnected_copy = m_nodes_disconnected;2007for (CNode* pnode : nodes_disconnected_copy)2008{2009// Destroy the object only after other threads have stopped using it.2010if (pnode->GetRefCount() <= 0) {2011m_nodes_disconnected.remove(pnode);2012DeleteNode(pnode);2013}2014}2015}2016{2017// Move entries from reconnections_to_add to m_reconnections.2018LOCK(m_reconnections_mutex);2019m_reconnections.splice(m_reconnections.end(), std::move(reconnections_to_add));2020}2021}2023void CConnman::NotifyNumConnectionsChanged()2024{2025AssertLockNotHeld(m_nodes_mutex);2027size_t nodes_size;2028{2029LOCK(m_nodes_mutex);2030nodes_size = m_nodes.size();2031}2032if(nodes_size != nPrevNodeCount) {2033nPrevNodeCount = nodes_size;2034if (m_client_interface) {2035m_client_interface->NotifyNumConnectionsChanged(nodes_size);2036}2037}2038}2040bool CConnman::ShouldRunInactivityChecks(const CNode& node, NodeClock::time_point now) const2041{2042return node.m_connected + m_peer_connect_timeout < now;2043}2045bool CConnman::InactivityCheck(const CNode& node, NodeClock::time_point now) const2046{2047// Tests that see disconnects after using mocktime can start nodes with a2048// large timeout. For example, -peertimeout=999999999.2049const auto last_send{node.m_last_send.load()};2050const auto last_recv{node.m_last_recv.load()};2052if (!ShouldRunInactivityChecks(node, now)) return false;2054bool has_received{last_recv > NodeClock::epoch};2055bool has_sent{last_send > NodeClock::epoch};2057if (!has_received || !has_sent) {2058std::string has_never;2059if (!has_received) has_never += ", never received from peer";2060if (!has_sent) has_never += ", never sent to peer";2061LogDebug(BCLog::NET,2062"socket no message in first %i seconds%s, %s",2063count_seconds(m_peer_connect_timeout),2064has_never,2065node.DisconnectMsg()2066);2067return true;2068}2070if (now > last_send + TIMEOUT_INTERVAL) {2071LogDebug(BCLog::NET,2072"socket sending timeout: %is, %s", Ticks<std::chrono::seconds>(now - last_send),2073node.DisconnectMsg()2074);2075return true;2076}2078if (now > last_recv + TIMEOUT_INTERVAL) {2079LogDebug(BCLog::NET,2080"socket receive timeout: %is, %s", Ticks<std::chrono::seconds>(now - last_recv),2081node.DisconnectMsg()2082);2083return true;2084}2086if (node.fPauseSend) {2087const auto last_pause{node.m_pause_send_since.load()};2088if (now > last_pause + TIMEOUT_INTERVAL) {2089LogDebug(BCLog::NET,2090"send buffer full for %is, %s", Ticks<std::chrono::seconds>(now - last_pause),2091node.DisconnectMsg()2092);2093return true;2094}2095}2097if (!node.fSuccessfullyConnected) {2098if (node.m_transport->GetInfo().transport_type == TransportProtocolType::DETECTING) {2099LogDebug(BCLog::NET, "V2 handshake timeout, %s", node.DisconnectMsg());2100} else {2101LogDebug(BCLog::NET, "version handshake timeout, %s", node.DisconnectMsg());2102}2103return true;2104}2106return false;2107}2109Sock::EventsPerSock CConnman::GenerateWaitSockets(std::span<CNode* const> nodes)2110{2111Sock::EventsPerSock events_per_sock;2113for (const ListenSocket& hListenSocket : vhListenSocket) {2114events_per_sock.emplace(hListenSocket.sock, Sock::Events{Sock::RecvEvent});2115}2117for (CNode* pnode : nodes) {2118bool select_recv = !pnode->fPauseRecv;2119bool select_send;2120{2121LOCK(pnode->cs_vSend);2122// Sending is possible if either there are bytes to send right now, or if there will be2123// once a potential message from vSendMsg is handed to the transport. GetBytesToSend2124// determines both of these in a single call.2125const auto& [to_send, more, _msg_type] = pnode->m_transport->GetBytesToSend(!pnode->vSendMsg.empty());2126select_send = !to_send.empty() || more;2127}2128if (!select_recv && !select_send) continue;2130LOCK(pnode->m_sock_mutex);2131if (pnode->m_sock) {2132Sock::Event event = (select_send ? Sock::SendEvent : 0) | (select_recv ? Sock::RecvEvent : 0);2133events_per_sock.emplace(pnode->m_sock, Sock::Events{event});2134}2135}2137return events_per_sock;2138}2140void CConnman::SocketHandler()2141{2142AssertLockNotHeld(m_nodes_mutex);2143AssertLockNotHeld(m_total_bytes_sent_mutex);2145Sock::EventsPerSock events_per_sock;2147{2148const NodesSnapshot snap{*this, /*shuffle=*/false};2150const auto timeout = std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS);2152// Check for the readiness of the already connected sockets and the2153// listening sockets in one call ("readiness" as in poll(2) or2154// select(2)). If none are ready, wait for a short while and return2155// empty sets.2156events_per_sock = GenerateWaitSockets(snap.Nodes());2157if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {2158m_interrupt_net->sleep_for(timeout);2159}2161// Service (send/receive) each of the already connected nodes.2162SocketHandlerConnected(snap.Nodes(), events_per_sock);2163}2165// Accept new connections from listening sockets.2166SocketHandlerListening(events_per_sock);2167}2169void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,2170const Sock::EventsPerSock& events_per_sock)2171{2172AssertLockNotHeld(m_total_bytes_sent_mutex);2174const auto now{NodeClock::now()};2176for (CNode* pnode : nodes) {2177if (m_interrupt_net->interrupted()) {2178return;2179}2181//2182// Receive2183//2184bool recvSet = false;2185bool sendSet = false;2186bool errorSet = false;2187{2188LOCK(pnode->m_sock_mutex);2189if (!pnode->m_sock) {2190continue;2191}2192const auto it = events_per_sock.find(pnode->m_sock);2193if (it != events_per_sock.end()) {2194recvSet = it->second.occurred & Sock::RecvEvent;2195sendSet = it->second.occurred & Sock::SendEvent;2196errorSet = it->second.occurred & Sock::ErrorEvent;2197}2198}2200if (sendSet) {2201// Send data2202auto [bytes_sent, data_left] = WITH_LOCK(pnode->cs_vSend, return SocketSendData(*pnode));2203if (bytes_sent) {2204RecordBytesSent(bytes_sent);2206// If both receiving and (non-optimistic) sending were possible, we first attempt2207// sending. If that succeeds, but does not fully drain the send queue, do not2208// attempt to receive. This avoids needlessly queueing data if the remote peer2209// is slow at receiving data, by means of TCP flow control. We only do this when2210// sending actually succeeded to make sure progress is always made; otherwise a2211// deadlock would be possible when both sides have data to send, but neither is2212// receiving.2213if (data_left) recvSet = false;2214}2215}2217if (recvSet || errorSet)2218{2219// typical socket buffer is 8K-64K2220uint8_t pchBuf[0x10000];2221int nBytes = 0;2222{2223LOCK(pnode->m_sock_mutex);2224if (!pnode->m_sock) {2225continue;2226}2227nBytes = pnode->m_sock->Recv(pchBuf, sizeof(pchBuf), MSG_DONTWAIT);2228}2229if (nBytes > 0)2230{2231bool notify = false;2232if (!pnode->ReceiveMsgBytes({pchBuf, (size_t)nBytes}, notify)) {2233LogDebug(BCLog::NET,2234"receiving message bytes failed, %s",2235pnode->DisconnectMsg()2236);2237pnode->CloseSocketDisconnect();2238}2239RecordBytesRecv(nBytes);2240if (notify) {2241pnode->MarkReceivedMsgsForProcessing();2242WakeMessageHandler();2243}2244}2245else if (nBytes == 0)2246{2247// socket closed gracefully2248if (!pnode->fDisconnect) {2249LogDebug(BCLog::NET, "socket closed, %s", pnode->DisconnectMsg());2250}