区块链特辑 :https://blog.csdn.net/fusan2004/article/details/80879343,欢迎查阅,原创作品,转载请标明!
承继前篇, 前一篇介绍了连接底层的一些细节,包括socket,握手等详细步骤,等待握手完成后,会调用startPeerSession抛向上层,这个时候连接表明已经建立了,从代码里面,为了表征这种连接的建立,会再定义一些数据结构来表达这种关系,本篇文章我们将首先去了解下startPeerSession里面都做了些什么事情,然后再去详细了解逻辑上的连接表达方式。
一、建立连接
这部分我们只介绍host.cpp中的startPeerSession函数,其他关于host的部分将在下篇中介绍,那也是整个p2p系列代码部分的最后一篇。首先研读下cpp的代码,如下
// 这是在握手成功之后被handshake调用,握手是在connect或者acceptor中进行
// 可以细致了解下这个函数的参数,_id表示对方的公钥,也是对方的节点id,rlp实际上
// 是writeHello中填写的对方的一些信息,io是数据帧编解码,_s就是底层通信的socket
void Host::startPeerSession(Public const& _id, RLP const& _rlp, unique_ptr<RLPXFrameCoder>&& _io, std::shared_ptr<RLPXSocket> const& _s)
{
// session可能是主动或者被动建立的,所以peers和node table里面可能不包括这个node id
shared_ptr<Peer> p;
DEV_RECURSIVE_GUARDED(x_sessions)
{
if (m_peers.count(_id)) //判断peer是否存在
p = m_peers[_id];
else
{
// 不存在的话,先尝试从node table中获取端口信息
if (Node n = nodeFromNodeTable(_id))
p = make_shared<Peer>(n);
if (!p)
p = make_shared<Peer>(Node(_id, UnspecifiedNodeIPEndpoint)); //如果node_table中也没有,先不指定ip断点信息
m_peers[_id] = p; //添加到m_peers中去
}
}
if (p->isOffline()) // 如果之前已经下线了,session不存在了
p->m_lastConnected = std::chrono::system_clock::now(); //更新
p->endpoint.address = _s->remoteEndpoint().address(); //更新地址
auto protocolVersion = _rlp[0].toInt<unsigned>(); // 协议版本号
auto clientVersion = _rlp[1].toString(); // 客户版本
auto caps = _rlp[2].toVector<CapDesc>(); // 远方端点支持的能力
auto listenPort = _rlp[3].toInt<unsigned short>(); // 监听的端口
auto pub = _rlp[4].toHash<Public>(); // 公钥,与node id相同
if (pub != _id)
{
cdebug << "Wrong ID: " << pub << " vs. " << _id; // 不同认为有问题,不建立session
return;
}
// clang error (previously: ... << hex << caps ...)
// "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments"
stringstream capslog;
// leave only highset mutually supported capability version
// 如果当前host不支持,或者有重复,清理
caps.erase(remove_if(caps.begin(), caps.end(), [&](CapDesc const& _r){ return !haveCapability(_r) || any_of(caps.begin(), caps.end(), [&](CapDesc const& _o){ return _r.first == _o.first && _o.second > _r.second && haveCapability(_o); }); }), caps.end());
for (auto cap: caps)
capslog << "(" << cap.first << "," << dec << cap.second << ")";
cnetlog << "Hello: " << clientVersion << " V[" << protocolVersion << "]"
<< " " << _id << " " << showbase << capslog.str() << " " << dec << listenPort;
// create session so disconnects are managed
// session来管理连接,所以先创建session,然后再去判断信息的合法性
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}));
if (protocolVersion < dev::p2p::c_protocolVersion - 1) //协议版本不一致的话断开
{
ps->disconnect(IncompatibleProtocol);
return;
}
if (caps.empty())
{
ps->disconnect(UselessPeer); //没有注册功能的话,直接断开连接
return;
}
if (m_netPrefs.pin && !isRequiredPeer(_id)) //如果设置要求只接受可信的peer,但是当前不可信,也要断开
{
cdebug << "Unexpected identity from peer (got" << _id << ", must be one of " << m_requiredPeers << ")";
ps->disconnect(UnexpectedIdentity);
return;
}