400-700-1307

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

2019-01-26 16:44

从Host类到Session类

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

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类中:

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

CapDesc定义为:

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

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

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()函数中

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(),里面有一段代码:

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

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

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

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

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类里,这两个函数相当于:

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处理某一段范围内的消息。


开班信息

当前校区

北京校区
  • 北京校区
  • 上海校区
  • 广州校区
  • 沈阳校区
  • 杭州校区
  • 南京校区
  • 郑州校区
  • 成都校区
  • 深圳校区
  • 西安校区
  • 苏州校区
  • 深圳沙井
  • 深圳观澜
  • 深圳龙岗
  • 武汉校区
  • 西宁校区
  • 石家庄校区
  • 湖北咸宁
  • 广东惠州
  • 广东佛山

                    Copyright © 2007-2019 IT兄弟连教育 版权所有 京ICP备11018177号  京公网安备 11010802026987号