比特币网络部分主要包含在net.cpp和netbase.cpp文件中,netbase.cpp内主要是一些辅助函数,因此我们主要阅读net.cpp文件中的几个重要函数
01:ThreadDNSAddressSeed
// src/net.cpp
void CConnman::ThreadDNSAddressSeed()
{
// goal: only query DNS seeds if address need is acute
// Avoiding DNS seeds when we don't need them improves user privacy by
// creating fewer identifying DNS requests, reduces trust by giving seeds
// less influence on the network topology, and reduces traffic to the seeds.
/*该方法提示,只有在急需接入网络的时候再使用DNS seed。这样能避免DNS seed连接数过多,也能提高用户的隐私。所以尽量不要直接连接DNS seed。*/
if ((addrman.size() > 0) &&
(!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {
if (!interruptNet.sleep_for(std::chrono::seconds(11)))
return;
LOCK(cs_vNodes);
int nRelevant = 0;
for (auto pnode : vNodes) {
nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices);
}
if (nRelevant >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
return;
}
}
const std::vector &vSeeds = Params().DNSSeeds();
int found = 0;
LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
for (const CDNSSeedData &seed : vSeeds) {
if (interruptNet) {
return;
}
if (HaveNameProxy()) {
AddOneShot(seed.host);
} else {
std::vector vIPs;
std::vector vAdd;
ServiceFlags requiredServiceBits = nRelevantServices;
std::string host = GetDNSHost(seed, &requiredServiceBits);
CNetAddr resolveSource;
if (!resolveSource.SetInternal(host)) {
continue;
}
if (LookupHost(host.c_str(), vIPs, 0, true))
{
for (const CNetAddr& ip : vIPs)
{
int nOneDay = 24*3600;
CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits);
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
vAdd.push_back(addr);
found++;
}
addrman.Add(vAdd, resolveSource);
}
}
}
LogPrintf("%d addresses found from DNS seeds\n", found);
}
将这个函数拆分成多个段落来分析。
第一段代码:
if ((addrman.size() > 0) &&
(!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {
if (!interruptNet.sleep_for(std::chrono::seconds(11)))
return;
LOCK(cs_vNodes);
int nRelevant = 0;
for (auto pnode : vNodes) {
nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices);
}
if (nRelevant >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
return;
}
}
addrman.size()
函数在/src/addrman.h
中声明。返回IP地址管理器CAddrMan中的IP地址个数。
-forcednsseed
参数指强制通过DNSseed来接入网络。DEFAULT_FORCEDNSSEED
默认是false。
interruptNet.sleep_for(std::chrono::seconds(11)
线程阻塞11秒。
cs_vNodes
声明在/src/net.h
文件中
// src/net.h
mutable CCriticalSection cs_vNodes;
CCriticalSection
类CCriticalSection的对象表示一个"临界区",它是一个用于同步的对象,同一时刻只允许一个线程存取资源或代码区。类似于互斥锁功能。
vNodes
声明在/src/net.h
中
// src/net.h
std::vector vNodes;
系统定义了节点数组( vector
这段代码的意思是:如果IP地址管理器CAddrMan中IP地址大于0,且没有指定-forcednsseed
参数,线程阻塞11秒。然后锁定cs_vNodes
,遍历vNodes
节点数组,如果成功连接的节点个数大于等于2。则打印"P2P节点是有效的,跳过DNS seeding阶段"
剩余部分代码:
const std::vector &vSeeds = Params().DNSSeeds();
int found = 0;
LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
for (const CDNSSeedData &seed : vSeeds) {
if (interruptNet) {
return;
}
if (HaveNameProxy()) {
AddOneShot(seed.host);
} else {
std::vector vIPs;
std::vector vAdd;
ServiceFlags requiredServiceBits = nRelevantServices;
std::string host = GetDNSHost(seed, &requiredServiceBits);
CNetAddr resolveSource;
if (!resolveSource.SetInternal(host)) {
continue;
}
if (LookupHost(host.c_str(), vIPs, 0, true))
{
for (const CNetAddr& ip : vIPs)
{
int nOneDay = 24*3600;
CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits);
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
vAdd.push_back(addr);
found++;
}
addrman.Add(vAdd, resolveSource);
}
}
}
LogPrintf("%d addresses found from DNS seeds\n", found);
这段代码的意思是通过DNS seed列表来获取对应的IP地址、端口号存放在IP管理器CAddrMan中。