以太坊C++源码解析(三)p2p(4)

从Host类到Session类

上一节我们跟踪到了Host::startPeerSession()函数里,现在我们来深入这个函数看个究竟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Host::startPeerSession(Public const& _id, RLP const& _rlp, unique_ptr<RLPXFrameCoder>&& _io, std::shared_ptr<RLPXSocket> const& _s)
{
// session maybe ingress or egress so m_peers and node table entries may not exist
shared_ptr<Peer> p;

// ...
// create session so disconnects are managed
shared_ptr<SessionFace> ps = make_shared<Session>(this, move(_io), _s, p, PeerSessionInfo({_id, clientVersion, p->endpoint.address.to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>(), protocolVersion}));
// ...

{
RecursiveGuard l(x_sessions);
// ...

unsigned offset = (unsigned)UserPacket;

// todo: mutex Session::m_capabilities and move for(:caps) out of mutex.
for (auto const& i: caps)
{
auto pcap = m_capabilities[i];
if (!pcap)
return ps->disconnect(IncompatibleProtocol);

pcap->newPeerCapability(ps, offset, i); // 重要!
offset += pcap->messageCount();
}

ps->start();
m_sessions[_id] = ps;
}

LOG(m_logger) << "p2p.host.peer.register " << _id;
}

可以看到这个函数里先是创建了一个Session类,然后对m_capabilities的成员调用newPeerCapability()来为每一个session创建一个capability,也就是消息处理器,最后调用session类的start()函数。
看到这里可能读者会一头雾水,不知道这里是做了什么处理,不用急,我们还是先从m_capabilities谈起。
m_capabilities定义在Host类中:

1
std::map<CapDesc, std::shared_ptr<HostCapabilityFace>> m_capabilities;

CapDesc定义为:

1
using CapDesc = std::pair<std::string, u256>;

HostCapabilityFace是一个虚基类,最重要的成员有两个:

1
2
3
4
5
6
7
8
9
10
class HostCapabilityFace
{
public:
// ...
virtual unsigned messageCount() const = 0;

virtual std::shared_ptr<Capability> newPeerCapability(
std::shared_ptr<SessionFace> const& _s, unsigned _idOffset, CapDesc const& _cap) = 0;
// ...
};

这两个函数正是在Host::startPeerSession()函数中被调用的那两个!
目前我们还不清楚这两个函数的具体功能,我们需要去找HostCapabilityFace类的子类,看看它们的实现。
为了找子类,我们需要找m_capabilities在哪里插入数据,发现是在Host::registerCapability()函数中

1
2
3
4
5
6
7
8
9
10
void Host::registerCapability(std::shared_ptr<HostCapabilityFace> const& _cap)
{
registerCapability(_cap, _cap->name(), _cap->version());
}

void Host::registerCapability(
std::shared_ptr<HostCapabilityFace> const& _cap, std::string const& _name, u256 const& _version)
{
m_capabilities[std::make_pair(_name, _version)] = _cap;
}

再找Host::registerCapability()在哪里被调用,找到libethereum\client.cpp里的Client::init(),里面有一段代码:

1
2
3
auto ethHostCapability =
make_shared<EthereumHost>(_extNet, bc(), m_stateDB, m_tq, m_bq, _networkId);
_extNet.registerCapability(ethHostCapability);

_extNet就是Host对象,原来HostCapabilityFace类的子类是EthereumHost类。EthereumHost类也是一个非常重要的类,我们后面再谈,这个类的定义有点意思,我们先来看看:

1
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker

可以看到这个类除了从Worker类继承外,还继承了 p2p::HostCapability<EthereumPeer>类。一下子又引入了两个新类。
一个一个来,我们先来看看 p2p::HostCapability类定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<class PeerCap>
class HostCapability: public HostCapabilityFace
{
public:
// ...
unsigned messageCount() const override { return PeerCap::messageCount(); }

std::shared_ptr<Capability> newPeerCapability(
std::shared_ptr<SessionFace> const& _s, unsigned _idOffset, CapDesc const& _cap) override
{
auto p = std::make_shared<PeerCap>(_s, this, _idOffset, _cap);
_s->registerCapability(_cap, p);
return p;
}

// ...
};

原来HostCapability<>类才是HostCapabilityFace类的直接子类,EthereumHost类是HostCapabilityFace类的孙子类。messageCount()newPeerCapability()这两个函数在HostCapability<>类里有一份实现。而且这个类是一个模板类,messageCount()只是调用了模板参数PeerCapmessageCount()函数。newPeerCapability()函数只是创建一个PeerCap对象,并调用其registerCapability()函数。
对于EthereumHost类,模板参数PeerCap就是EthereumPeer,这点可以从EthereumHost类定义中得到,因此在EthereumHost类里,这两个函数相当于:

1
2
3
4
5
6
7
8
9
unsigned EthereumHost::messageCount() const override { return EthereumPeer::messageCount(); }

std::shared_ptr<Capability> EthereumHost::newPeerCapability(
std::shared_ptr<SessionFace> const& _s, unsigned _idOffset, CapDesc const& _cap) override
{
auto p = std::make_shared<EthereumPeer>(_s, this, _idOffset, _cap);
_s->registerCapability(_cap, p);
return p;
}

先来看newPeerCapability这个函数吧,它创建了一个EthereumPeer类对象,并调用SessionFace::registerCapability()注册了该对象,也就是把EthereumPeer类对象放到了Session类对象里,EthereumPeer类是Session消息处理器。
实际上EthereumHost类自身对newPeerCapability()函数也有自己的实现,这个实现与父类HostCapability<>的实现稍有不同,这个后面再谈。
messageCount()函数简单返回了一个消息数量,这个用来将不同的HostCapabilityFace的消息错开,方便消息分发,每个HostCapabilityFace处理某一段范围内的消息。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×