以太坊底层P2P代码细节很多,深入进去,会丢失整体框架,所以按照自己的习惯,先明白框架结构,了然于胸,然后再深入细节,所以这一节就先用白话总结下整体的流程。
入口:
1. P2P层分为两个部分,一层是基于UDP的节点Node发现,二是基于TCP的节点Peer的消息交互。注意这里的两个英文单词,Node,是网络上的节点,Peer是已经连接的节点
2. 两个过程依次涉及两个大类NodeTable 和 Host,其中NodeTable又是Host的成员变量, Host是WebThreeDirect的成员变量,
WebThreeDirect又继承了接口类NetworkFace
class NetworkFace
{
public:
virtual ~NetworkFace() = default;
/// Get information concerning this node.
virtual p2p::NodeInfo nodeInfo() const = 0;
/// Get information on the current peer set.
virtual std::vector<p2p::PeerSessionInfo> peers() = 0;
/// Same as peers().size(), but more efficient.
virtual size_t peerCount() const = 0;
/// Generalised peer addition.
virtual void addPeer(p2p::NodeSpec const& _node, p2p::PeerType _t) = 0;
/// Add node to connect to.
virtual void addNode(p2p::NodeID const& _node, bi::tcp::endpoint const& _hostEndpoint) = 0;
/// Require connection to peer.
virtual void requirePeer(p2p::NodeID const& _node, bi::tcp::endpoint const& _endpoint) = 0;
/// Save peers
virtual dev::bytes saveNetwork() = 0;
/// Sets the ideal number of peers.
virtual void setIdealPeerCount(size_t _n) = 0;
/// Get network id
virtual u256 networkId() const = 0;
/// Start the network subsystem.
virtual void startNetwork() = 0;
/// Stop the network subsystem.
virtual void stopNetwork() = 0;
/// Is network working? there may not be any peers yet.
virtual bool isNetworkStarted() const = 0;
/// Get enode string.
virtual std::string enode() const = 0;
};
NetworkFace 提供接口,可以看出主要有3大块,node、peer、network
看下WebThreeDirect的定义, 继承NetworkFace后,增加了主要的成员变量,主要的是p2p::Host m_net; 实现虚函数的过程也是封装了Host的实际实现函数,例如 addPeer --> m_net.addPeer(_s, _t);
/**
* @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one
* running on any given machine for the provided DB path.
*
* Keeps a libp2p Host going (administering the work thread with m_workNet).
*
* Encapsulates a bunch of P2P protocols (interfaces), each using the same underlying libp2p Host.
*
* Provides a baseline for the multiplexed multi-protocol session class, WebThree.
*/
class WebThreeDirect: public NetworkFace
{
private:
std::string m_clientVersion; ///< Our end-application client's name/version.
std::unique_ptr<eth::Client> m_ethereum; ///< Client for Ethereum ("eth") protocol.
p2p::Host m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required.
}
启动:
在aleth\main.cpp的main函数中,建立WebThreeDirect对象
dev::WebThreeDirect web3(WebThreeDirect::composeClientVersion("aleth"), db::databasePath(),
snapshotPath, chainParams, withExisting, netPrefs, &nodesState, testingMode);
开始设置Host的环境,启动:
// 设置peer的个数,如果是主动连接的有11个,如果是被动连接,可以有11* 7个
web3.setIdealPeerCount(peers); // 11
web3.setPeerStretch(peerStretch); // 7
// 还是启动host,后续再具体分析
if (bootstrap || !remoteHost.empty() || enableDiscovery || listenSet || !preferredNodes.empty())
{
web3.startNetwork(); // --- host->start()
cout << "Node ID: " << web3.enode() << "\n";
}
// 为发现node 增加源,也就是从这些固定节点开始进行节点发现
if (bootstrap && privateChain.empty())
for (auto const& i: Host::pocHosts())
web3.requirePeer(i.first, i.second);
if (!remoteHost.empty())
web3.addNode(p2p::NodeID(), remoteHost + ":" + toString(remotePort));
host->start() 里面调用startedWorking(), 再这里面完成了置位启动标记位,设置tcp的监听,创建nodeTabel(nodeTable 再构造中开启upd连接,发现节点)
void Host::startedWorking()
{
asserts(!m_timer);
{ ......
m_run = true;
}
......
// try to open acceptor (todo: ipv6)
int port = Network::tcp4Listen(m_tcp4Acceptor, m_netConfig);
if (port > 0)
{
m_listenPort = port;
determinePublic();
runAcceptor(); // 这里面开始异步接受节点信息,进行握手,建立连接
}
......
auto nodeTable = make_shared<NodeTable>(
m_ioService,
m_alias,
NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()),
m_netConfig.discovery
); // 里面建立UDP监听,开始发现节点
nodeTable->setEventHandler(new HostNodeTableHandler(*this));
DEV_GUARDED(x_nodeTable)
m_nodeTable = nodeTable;
restoreNetwork(&m_restoreNetwork);
LOG(m_logger) << "p2p.started id: " << id();
run(boost::system::error_code());
}
Host::pocHosts() 里面写死了6个(主网)Node的信息(NodeID, IP, Port)。再上线的nodeTable发现节点时,开始为空,先添加固定的节点,然后基于这些固定节点节点发现
NodeTable::NodeTable(
ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled)
: m_hostNodeID(_alias.pub()),
m_hostNodeEndpoint(_endpoint),
m_secret(_alias.secret()),
m_socket(make_shared<NodeSocket>(
_io, static_cast<UDPSocketEvents&>(*this), (bi::udp::endpoint)m_hostNodeEndpoint)),
m_timers(_io)
{
......
try
{
m_socket->connect(); //UDP 配置
doDiscovery(); // 节点发现
doHandleTimeouts();
}
......
}
节点发现过程参见 https://blog.csdn.net/laorenmen/article/details/85014195 以太坊P2P流程 1
握手流程,参见 https://blog.csdn.net/laorenmen/article/details/85043601 以太坊P2P流程2
https://blog.csdn.net/laorenmen/article/details/85059348 以太坊P2P流程3
上面的文章分别介绍了Node发现和Peer握手流程,最后在握手成功(收到hello报文后)进入 host-> startPeerSession参见下一篇学习笔记