比特币开发学习:P2P网络建立流程之生成地址对并连接到指定地址

当前位置:首页 > 币圈百科 > 比特币开发学习:P2P网络建立流程之生成地址对并连接到指定地址

比特币开发学习:P2P网络建立流程之生成地址对并连接到指定地址

2022-11-25币圈百科771

比特币开发学习: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:map

mapConnectedByName;{ 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

在上面对OpenNetworkConnection函数的解释“3 .调用ConnectNode方法,连接到指定地址,返回对等CNode对象”,我提到了‘connect node’方法,它负责连接到特定的对等节点。我们来看看具体的治疗方法。

如果参数pszDest是空指针,处理如下:如果要连接的地址是本地地址,则直接返回空指针。调用FindNode方法查看指定的节点是否存在。如果它存在,即已经连接,则返回一个空指针。if(pszDest==null ptr){ if(is local(addr connect))返回null ptr;//查找现有连接CNode * pnode=FindNode(static _ cast(addr connect));if (pnode) { LogPrintf('未能打开新连接,已连接\ n ');返回nullptr}}如果参数pszDest不是空指针,那么调用Lookup方法查找/生成地址字符串对应的地址对象。如果找到,则执行以下处理:生成要连接的地址对象。如果地址address对象无效,则返回一个空指针。调用FindNode方法来查找相应的地址对象。如果它存在,即已经连接,则返回一个空指针。 这个地方解析要连接的地址字符串生成要连接的地址对象const int default_port=Params().GetDefaultPort();if(PSZ目的地){ STD:vector已解析;if (Lookup(pszDest,resolved,default_port,fNameLookup!HaveNameProxy(),256)!已解决。empty()){ addr connect=CAD address(resolved[GetRand(resolved。size())],NODE _ NONE);如果(!addrConnect .IsValid()) { LogPrint(BCLog:NET,'解析程序返回了无效的地址%s(对于%s ',添加连接.ToString()、PSZ dest);返回nullptr}锁(cs _ vNodes);CNode * pnode=FindNode(static _ cast(addr connect));if(pnode){ pnode-MaybeSetAddrName(STD:string(pszDest));LogPrintf('未能打开新连接,已连接\ n’);返回nullptr} }}如果要连接的地址对象是有效的,进行下面的处理。调用GetProxy方法,返回代理类型。如果方法返回为真,即存在代理,那么调用贷方(即信用)ateSocket方法,创建代理套接字。如果成功创建,调用ConnectThroughProxy方法,通过代理连接到对等节点。如果不存在代理,那么调用CreateSocket方法,创建对等节点的套接字。如果成功创建,调用ConnectSocketDirectly方法,直接连接到对等节点。bool proxyConnectionFailed = false;if (GetProxy(addrConnect.GetNetwork(), proxy)) { hSocket = CreateSocket(proxy.proxy); if (hSocket == INVALID_SOCKET) { return nullptr; } connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed);} else { // no proxy needed (none set for target network) hSocket = CreateSocket(addrConnect); if (hSocket == INVALID_SOCKET) { return nullptr; } connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, manual_connection);}if (!proxyConnectionFailed) { // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to // the proxy, mark this as an attempt. addrman.Attempt(addrConnect, fCountFailure);}如果要连接的字符串不空,且存在代理,那么:调用CreateSocket方法,生成代理的套接字。然后,调用ConnectThroughProxy方法,通过代理连接到指定的对等节点。hSocket = CreateSocket(proxy.proxy);if (hSocket == INVALID_SOCKET) { return nullptr;}std::string host;int port = default_port;SplitHostPort(std::string(pszDest), port, host);connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr);如果以上都没有连接到主节点,则关闭套接字并返回空指针。 if (!connected) { CloseSocket(hSocket); return nullptr; }最后,生成并返回主节点对象。 NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false); pnode->AddRef(); return pnode;
比特币开发学习:P2P网络建立流程之生成地址对并连接到指定地址 | 分享给朋友: