Développement de base Bitcoin : Une question sur les membres de données de la classe CNode


La question date de 2015 ; certaines choses ont changé depuis.

CNetMessage est un conteneur de messages indépendant du protocole de transport. Il contient les données du message reçu (DataStream), l'heure de réception du message, la taille de la charge utile et d'autres informations. Il est utilisé pour décomposer les messages du réseau. Voir le code suivant :

bool CNode::ReceiveMsgBytes(Span msg_bytes, bool& complete) { complete = false; const auto time = GetTime(); VERROUILLER(cs_vRecv); m_last_recv = std::chrono::duration_cast(time); nRecvBytes += msg_bytes.size(); while (msg_bytes.size() > 0) { // absorbe les données réseau if ( ! m_transport->ReceivedBytes(msg_bytes)) { // Grave problème de transport, déconnectez-vous du homologue. renvoie faux ; } if (m_transport->ReceivedMessageComplete()) { // décompose un CNetMessage indépendant du transport à partir du désérialiseur bool rejet_message{false} ; CNetMessage msg = m_transport->GetReceivedMessage (heure, rejet_message); if (reject_message) { // La désérialisation du message a échoué. Supprimez le message mais ne déconnectez pas le homologue. // stocke la taille du message corrompu mapRecvBytesPerMsgType.at(NET_MESSAGE_TYPE_OTHER) += msg.m_raw_message_size; continuer; } // Stocke les octets reçus par type de message. // Pour empêcher un DOS mémoire, autorisez uniquement les types de messages connus. auto i = mapRecvBytesPerMsgType.find(msg.m_type); if (i == mapRecvBytesPerMsgType.end()) { i = mapRecvBytesPerMsgType.find(NET_MESSAGE_TYPE_OTHER); } assert(i ! = mapRecvBytesPerMsgType.end()); i->seconde += msg.m_raw_message_size; // envoie le message à la file d'attente du processus, vRecvMsg.push_back(std::move(msg)); complet = vrai ; } } renvoie vrai ; }

Développement de base Bitcoin : Une question sur les membres de données de la classe CNode

Après avoir reçu le message, nous le transmettons à vRecvMsg, puis le mettons dans une file d'attente pour être traité. Dans ProcessMessage, nous obtenons les données du message (DataStream) et les traitons en fonction du type de message.

bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic& interrompuMsgProc) { AssertLockHeld(g_msgproc_mutex); PeerRef homologue = GetPeerRef(pfrom->GetId()); if (peer == nullptr) renvoie false ; { LOCK(peer->m_getdata_requests_mutex); if ( ! peer->m_getdata_requests.empty()) { ProcessGetData(*pfrom, *peer, interrompuMsgProc); } } const boolprocessed_orphan = ProcessOrphanTx(*peer); if (pfrom->fDisconnect) renvoie false ; if (processed_orphan) renvoie vrai ; // ceci maintient l'ordre des réponses // et empêche m_getdata_requests de croître de manière illimitée { LOCK(peer->m_getdata_requests_mutex); if ( ! peer->m_getdata_requests.empty()) renvoie true ; } // Ne vous embêtez pas si le tampon d'envoi est trop plein pour répondre de toute façon if (pfrom->fPauseSend) return false; auto poll_result{pfrom->PollMessage()} ; if ( ! poll_result) { // Aucun message à traiter return false ; } CNetMessage& msg{poll_result->first}; bool fMoreWork = poll_result->second; TRACE6(net, inbound_message, pfrom->GetId(), pfrom->m_addr_name.c_str(), pfrom->ConnectionTypeAsString().c_str(), msg.m_type.c_str(), msg.m_recv.size(), msg .m_recv.data() ); if (m_opts.capture_messages) { CaptureMessage(pfrom->addr, msg.m_type, MakeUCharSpan(msg.m_recv), /*is_incoming=*/true); } essayez { ProcessMessage(*pfrom, msg.m_type, msg.m_recv, msg.m_time, interrompuMsgProc); if (interruptMsgProc) renvoie false ; { LOCK(peer->m_getdata_requests_mutex); if ( ! peer->m_getdata_requests.empty()) fMoreWork = true; } // Ce pair a-t-il un orphelin prêt à reconsidérer sa décision ? // (Remarque : nous pouvons avoir fourni un parent pour un orphelin fourni // par un autre homologue qui a déjà été traité ; dans ce cas, // le travail supplémentaire peut ne pas être remarqué, ce qui peut entraîner // un délai inutile de 100 ms) si (m_orphanage.HaveTxToReconsider(peer->m_id)) fMoreWork = true; } catch (const std::exception& e) { LogPrint(BCLog::NET, « %s(%s, %u octets) : Exception '%s' (%s) capturée\n », __func__, SanitizeString(msg. m_type), msg.m_message_size, e.what(), typeid(e).name()); } catch (…) { LogPrint(BCLog::NET, « %s(%s, %u bytes) : exception inconnue interceptée\n », __func__, SanitizeString(msg.m_type), msg.m_message_size); } return fMoreWork; }

Dans le cas de vSendMsg, il s'agit d'un vecteur de CSerializedNetMsg. La structure CSerializedNetMsg est simple. Il contient les données du message (un vecteur de caractères non signés) et son type. Comme son nom l'indique, il représente le message sérialisé. Vous pouvez remarquer dans la base de code que NetMsg::Make est couramment utilisé pour le construire. Cette fonction accepte un paramètre de chaîne représentant le type de message et tout autre paramètre sauf celui qui sera utilisé pour composer les données du message.

espace de noms NetMsg { template CSerializedNetMsg Make(std::string msg_type, Args&&… args) { CSerializedNetMsg msg; msg.m_type = std::move(msg_type); VectorWriter{msg.data, 0, std::forward(args)…} ; renvoyer un message ; } } // espace de noms NetMsg

Maintenant, ce que je comprends de la différence entre CSerializedNetMsg et CNetMessage est la suivante :

  • CSerializedNetMsg semble plus léger
  • CNetMessage a des membres supplémentaires (m_time, m_message_size, m_raw_message_size)
  • Un seul objet CNetMessage pour le même message existera. CSerializedNetMsg a une méthode spécifique pour les copies
  • Bien qu’ils semblent similaires, ils présentent des caractéristiques spécifiques à leurs objectifs. Par exemple, il est important de traiter les messages reçus avec CNetMessage, entre autres raisons, pour connaître exactement la taille des données que nous recevons afin de suivre la taille de la file d'attente du processus. La copie depuis CSerializedNetMsg peut être utile lors de l'envoi du même message pour plusieurs nœuds.

    m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](pnode CNode*) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); if (pnode->GetCommonVersion() < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect) return ; ProcessBlockAvailability(pnode->GetId()); CNodeState &state = *State(pnode->GetId()); // Si l'homologue l'a déjà fait, ou si nous lui avons déjà annoncé le bloc précédent, // mais nous ne pensons pas qu'il ait celui-ci, allez-y et annoncez-le if (state.m_requested_hb_cmpctblocks && ! PeerHasHeader(&state, pindex) && PeerHasHeader (&state, pindex->pprev)) { LogPrint(BCLog::NET, « %s envoyant l'en-tête et les identifiants %s au peer=%d\n », « PeerManager::NewPoWValidBlock », hashBlock.ToString(), pnode->GetId()); const CSerializedNetMsg& ser_cmpctblock{lazy_ser.get()}; PushMessage(*pnode, ser_cmpctblock.Copy()); state.pindexBestHeaderSent = pindex; } });

    À propos de vRecvGetData, il a été supprimé dans #19911. Nous avons maintenant m_getdata_requests en tant que membre de Peer, lorsqu'un nœud reçoit un message GETDATA, il stocke les INV dans m_getdata_requests. Il est utilisé pour contrôler ce qu'un nœud nous a demandé (par exemple, une transaction).