比特币源码研读7-程序入口函数解析(6)

96
Jacky_2c9f
2017.11.26 18:18* 字数 2110

上一节提到了一个问题:比特币默认的日志输出文件是哪个? 不知道大家找到了没,现在答案公布如下:

如果有看过我的第一篇文章“比特币源码研读1——下载与编译” 就知道,我们安装的路径为个人的目录下,那么比特币默认的日志文件就在:

~/.bitcoin/debug.log

tips: 这里说明下,有一种情况是看不到debug.log这个日志文件的——你从未运行过程序,解决方案就是跑以下的命令:

bitcoind

或是

bitcoind -daemon

如果不想下载一百多G的数据文件,对于第一个命令bitcoind,直接ctrl+c即可;第二个命令的话杀掉后台进程就可以了。


好了,我们接下来继续讲解参数的初始化设置。

InitParameterInteraction(): 该函数具体实现于init.cpp文件中,主要分为7部分,先看第一部分:

(1) 绑定并监听地址

// when specifying an explicit binding address, you want to listen on it

// even when -connect or -proxy is specified

if (IsArgSet("-bind")) {

if (SoftSetBoolArg("-listen", true))

LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);

}

if (IsArgSet("-whitebind")) {

if (SoftSetBoolArg("-listen", true))

LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);

}

就是当你显式指定一个绑定地址,就监控该地址,即使你指定了“-connect”或者“-proxy”参数。

从代码可以看出,有两种方式来绑定地址:"-bind" 和 "-whitebind",这两种的处理方式也一样,都是通过函数SoftSetBoolArg设置 参数“-listen”为 true,表示对地址进行监听。

另外,关于记录日志的函数 LogPrintf,以后会经常碰面,这里介绍一下,它是以预编译方式定义于util.h文件中。从定义中看出,真正实现打印功能的函数是 LogPrintStr, 其定义于util.h文件中。

其中,“\” 表示换行, 实现代码在util.cpp文件中:

int LogPrintStr(const std::string &str)

{

int ret = 0; // Returns total number of characters written

static std::atomic_bool fStartedNewLine(true);

std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);

if (fPrintToConsole)

{

// print to console

ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);

fflush(stdout);

}

else if (fPrintToDebugLog)

{

boost::call_once(&DebugPrintInit, debugPrintInitFlag);

boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);

// buffer if we haven't opened the log yet

if (fileout == NULL) {

assert(vMsgsBeforeOpenLog);

ret = strTimestamped.length();

vMsgsBeforeOpenLog->push_back(strTimestamped);

}

else

{

// reopen the log file, if requested

if (fReopenDebugLog) {

fReopenDebugLog = false;

fs::path pathDebug = GetDataDir() / "debug.log";

if (fsbridge::freopen(pathDebug,"a",fileout) != NULL)

setbuf(fileout, NULL); // unbuffered

}

ret = FileWriteStr(strTimestamped, fileout);

}

}

return ret;

}

大概逻辑如下:

1. 通过调用函数LogTimestampStr为日志加上时间戳;

2. 根据全局变量fPrintToConsole判断是否打印到控制台,默认值为false, 即不打印;

3. 根据全局变量fPrintToDebugLog判断是否打印到日志文件debug.log,默认是true,即打印;

好了,继续InitParameterInteraction函数讲解。

(2) 连接可信任节点

if (gArgs.IsArgSet("-connect")) {

// when only connecting to trusted nodes, do not seed via DNS, or listen by default

if (SoftSetBoolArg("-dnsseed", false))

LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);

if (SoftSetBoolArg("-listen", false))

LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);

}

首先判断gArgs是否有"-connect"参数,如果有,将“dnsseed”和"-listen"参数设置为false,即只有当网络连接到可信任节点时,才取消通过DNS广播方式查找节点地址,不监听默认的地址。

这里需要注意的是,全局参数对象mapArgs中如果有“-listen” 参数,即在此之前已经设置了“-listen” 参数,则不会再重新设置了,即“-listen” 参数的值就不会变了。

这也就是当你在(1)中设置了”-bind”和”-whitebind”参数,那么即使你指定了“-connect”或者“-proxy”参数也无效。

(3)代理模式

if (IsArgSet("-proxy")) {

// to protect privacy, do not listen by default if a default proxy server is specified

if (SoftSetBoolArg("-listen", false))

LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);

// to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1

// to listen locally, so don't rely on this happening through -listen below.

if (SoftSetBoolArg("-upnp", false))

LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);

// to protect privacy, do not discover addresses by default

if (SoftSetBoolArg("-discover", false))

LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);

}

如注释,设置代理是为了保护隐私,假如已经指定了代理服务器,将"-listen"、"-upnp"以及"-discover"均设为false,表示只使用指定的代理服务器,不使用UPNP监听端口,不去查找默认的地址。换言之,只使用代理参数提供的监听地址及端口。

以下是微软官方网站对UPnP的解释:

通用即插即用 (UPnP) 是一种用于 PC 机和智能设备(或仪器)的常见对等网络连接的体系结构,尤其是在家庭中。UPnP 以Internet标准和技术(例如 TCP/IP、HTTP 和 XML)为基础,使这样的设备彼此可自动连接和协同工作,从而使网络(尤其是家庭网络)对更多的人成为可能。

(4)监听设置

全局变量DEFAULT_LISTEN 默认值为true, 即不设置监听,将 "-upnp"、"-discover"以及"-listenonion"(匿名地址监听)均设为false

if (!GetBoolArg("-listen", DEFAULT_LISTEN)) {

// do not map ports or try to retrieve public IP when not listening (pointless)

if (SoftSetBoolArg("-upnp", false))

LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);

if (SoftSetBoolArg("-discover", false))

LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);

if (SoftSetBoolArg("-listenonion", false))

LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);

}

tips: listenonion,即匿名地址监听,这里涉及到通信机制的一个概念:第二代洋葱路由(onion routing)。

Tor(The Onion Router)是第二代洋葱路由(onion routing)的一种实现,用户通过Tor可以在因特网上进行匿名交流。Tor专门防范流量过滤、嗅探分析,让用户免受其害。最初该项目由美国海军研究实验室赞助。2004年后期,Tor成为电子前哨基金会的一个项目。2005年后期,EFF不再赞助Tor项目,但他们继续维持Tor的官方网站。

(5)外部IP地址设置

if (IsArgSet("-externalip")) {

// if an explicit public IP is specified, do not try to find others

if (SoftSetBoolArg("-discover", false))

LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);

}

如果显式指定了公共IP地址,就不用查找其他的监听地址了。

(6)区块模式设置

// disable whitelistrelay in blocksonly mode

if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {

if (SoftSetBoolArg("-whitelistrelay", false))

LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);

}

全局变量DEFAULT_BLOCKSONLY默认值为False,只有DEFAULT_BLOCKSONLY的值为true,GetBoolArg才为true,这时才会将"-whitelistrelay"设置为false,即区块模式下白名单列表失效。

tips: 这里的blocksonly参数是比特币客户端以调试状态启动时才会使用的,我们可以从src/init.cpp里面的帮助信息函数HelpMessage中得到相关信息:

if (showDebug)

strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));

关于DEFAULT_BLOCKSONLY, 我们已经知道其默认值为False,即默认情况下不会只以区块模式运行。假定只在该模式下运行,那么此时全网的交易都不会被打包,钱包的交易广播功能将失效,也就是我们看到的walletbroadcast参数此时需要设置为false,否则互斥。

(7)强制白名单节点连接参数设置

// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.

if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {

if (SoftSetBoolArg("-whitelistrelay", true))

LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);

}

DEFAULT_WHITELISTFORCERELAY默认为true,则参数"-whitelistrelay"设置为true,即比特币网络中的信息将优先在白名单节点间传递。

至此InitParameterInteraction函数已经解读完毕。下一节将讲解初始化基本环境搭建,敬请期待!


作者:区块链研习社比特币源码研读班 Jacky

比特币源码研读
Web note ad 1