比特币开发学习:P2P网络建立流程之生成地址对并连接到指定地址
本节继续讲解比特币P2P网络建立流程。本节中解释的线程是“ThreadOpenAddedConnections”,它用于生成地址对并连接到指定的地址。本文可以结合比特币系统启动第12步的讲解,让我们对比特币系统启动的过程有一个更系统的了解。
P2P网络的建立是在比特币系统启动的第12步最后时刻调用CConnman:Start方法开始的。
下面开始解释每个线程的具体处理。
1、ThreadSockETHandler
参见文章从零开始学习比特币(5)——P2P网络建立过程中套接字的读取和发送
2、ThreadnSaddressSeed
参见文章从零开始学习比特币(6)——P2P网络建立过程中查询DNS节点[x ThreadAddedConnections
该线程的主要作用是生成一个address对象,调用OpenNetworkConnection方法连接到指定地址。
Thread是在net.cpp文件的第1959行定义的。先来具体解读一下。
线程体是一个while循环。在循环中执行以下处理。
调用GetAddedNodeInfo方法获取所有节点信息。这个方法返回所有的节点信息,包括连接的和未连接的地址。首先,生成用于存储节点信息的容器变量ret和用于存储地址串的列表对象address。然后将vAddedNodes集合中的所有地址复制到lAddresses中。STD:vector ret;STD:list ladders(0);{ LOCK(cs _ vAddedNodes);ret . reserve(vaddednodes . size());std:copy(vAddedNodes.cbegin()、vAddedNodes.cend()、STD:back _ inserter(lAddresses));}遍历所有节点(vNodes节点容器)进行后续处理。如果当前节点的地址有效,它将被添加到mapConnectedmap。Key是当前节点的地址,值指示当前节点是否是入站节点。获取当前节点的地址名称。如果名称不为空,请将其放入mapConnectedByNamemap。Key是当前节点的地址名,它的值是一个std:pair对象,其中第一个值表示当前节点是否是入站节点,第二个值是节点的地址。STD:map map connected;STD:mapmapConnectedByName;{ LOCK(cs _ vNodes);for(const CNode * pnode:vNodes){ if(pnode-addr。is valid()){ map connected[pnode-addr]=pnode-fin bound;} STD:string addrName=pnode-get addr name();如果(!addrName . empty()){ mapConnectedByName[STD:move(addrName)]=STD:make _ pair(pnode-fInbound,static _ cast(pnode-addr));}}}遍历lAddresses变量并执行以下处理。根据当前地址和当前网络类型,生成CService类型的服务对象和节点信息对象。如果当前地址是IP:Port的形式,则查找对应于mapConnected集合的地址。如果可以找到,设置节点信息对象的相关属性。
如果当前地址是名称的形式,则查找对应于mapConnectedByName集合的地址。如果可以找到,设置节点信息对象的相关属性。将当前地址信息对象添加到ret集合。
for(const STD:string stradd node:lAddresses){ CService service(lookup numeric(stradd node . c _ str(),Params()。GetDefaultPort()));AddedNodeInfo added node { stradd node,CService(),false,false };如果(服务。IsValid()) { //strAddNode是一个IP:port auto it=map connected . find(service);如果(它!=map connected . end()){ added node . resolved address=service;added node . fc connected=true;added node . fin bound=it-second;} } else { //strAddNode是一个名称auto it=mapconnectedbyname . find(stradd node);如果(它!=mapconnectedbyname . end()){ added node . resolved address=it-second . second;added node . fc 蓑衣网小编2022 connected=true;added node . fin bound=it-second . first;} } ret . launte _ back(STD:move(added node));}返回ret集合。 遍历所有节点信息,如果当前节点未连接,则进行如下处理:生成地址对象addr,其类型为CAD address。调用OpenNetworkConnection方法连接到当前节点。for(const AddedNodeInfo info:vInfo){ if(!info.fConnected) { if(!格兰特。TryAcquire()) { //如果我们已经用完了我们的信号量并需要一个新的信号量,我们不要在这里等待,因为当我们等待的时候addednodeinfo状态可能会改变。打破;} tried=trueCAD address addr(CService(),NODE _ NONE);OpenNetworkConnection(addr,false,grant,info.strAddedNode.c_str(),false,false,true);如果(!interrupt net . sleep _ for(STD:chrono:毫秒(500)))返回;}}我们来详细看看OpenNetworkConnection函数的处理过程。
如果interruptNet为真,则返回该值。如果网络不活动(fNetworkActive为false),则返回。if(interrupt net){ return;}如果(!fNetworkActive){ return;}如果参数pszDest为空(当前节点信息的地址),进一步,如果要连接的节点是本地的、连接的或禁止的,则返回。如果参数pszDest不为空,而且,如果节点是连接的,它将返回。如果(!pszDest){ if(is local(addr connect)| | FindNode(static 蓑衣网小编2022 _ cast(addr connect))| | is banned(addr connect)| | FindNode(addr connect。ToStringIPPort()))返回;} else if(FindNode(STD:string(pszDest)))返回;调用ConnectNode方法,连接到指定的地址,并返回对等节点CNode对象。如果连接失败,它将返回。如果参数grantOutbound对象存在,则调用其MoveTo方法进行处理。如果参数fOneShot为true,则将对等节点的fOneShot属性设置为true。如果是临时探测节点(参数fFeeler为true),则将对等节点的fFeeler属性设置为true。如果是手动连接,则将对等节点的m_manual_connection属性设置为true。调用网络事件处理程序的InitializeNode方法来初始化对等节点。具体代码在net_processing.cpp文件的第611行,如下图:void peer logic completion:initialize node(cnode * pnode){ c address addr=pnode-addr;STD:string addrName=pnode-get addr name();NodeId NodeId=pnode-GetId();{ LOCK(cs _ main);mapnodestate . launte _ hint(mapnodestate . end()、STD:piece _ construct、std:forward_as_tuple(nodeid)、std:forward_as_tuple(addr,STD:move(addrName)));}如果(!pnode-fin bound)pushnodevision(pnode,connman,GetTime());}代码的主要动作是检查节点是否是出站节点,即是否连接到其他对等节点,如果是,则调用PushNodeVersion方法发送版本信息。特定消息处理部分。将生成的对等节点保存到vNodes向量中。3.1,ConnectNode