EOS会逐次调用插件的初始化函数
void application::startup() {
try {
for (auto plugin : initialized_plugins)
plugin->startup();
} catch(...) {
shutdown();
throw;
}
调用插件类的公共基类函数plugin::startup
virtual void startup() override {
if(_state == initialized) {
_state = started;
static_cast<Impl*>(this)->plugin_requires([&](auto& plug){ plug.startup(); });
static_cast<Impl*>(this)->plugin_startup();
app().plugin_started(*this);
}
assert(_state == started); // if initial state was not initialized, final state cannot be started
}
plugin_startup对应到net_plugin为
void net_plugin::plugin_startup() {
//类型为tcp::acceptor
//监听本地端口
if( my->acceptor ) {
my->acceptor->open(my->listen_endpoint.protocol());
my->acceptor->set_option(tcp::acceptor::reuse_address(true));
my->acceptor->bind(my->listen_endpoint);
my->acceptor->listen();
ilog("starting listener, max clients is ${mc}",("mc",my->max_client_count));
my->start_listen_loop();
}
{
chain::controller&cc = my->chain_plug->chain();
cc.accepted_block_header.connect( boost::bind(&net_plugin_impl::accepted_block_header, my.get(), _1));
cc.accepted_block.connect( boost::bind(&net_plugin_impl::accepted_block, my.get(), _1));
cc.irreversible_block.connect( boost::bind(&net_plugin_impl::irreversible_block, my.get(), _1));
cc.accepted_transaction.connect( boost::bind(&net_plugin_impl::accepted_transaction, my.get(), _1));
cc.applied_transaction.connect( boost::bind(&net_plugin_impl::applied_transaction, my.get(), _1));
cc.accepted_confirmation.connect( boost::bind(&net_plugin_impl::accepted_confirmation, my.get(), _1));
}
my->incoming_transaction_ack_subscription = app().get_channel<channels::transaction_ack>().subscribe(boost::bind(&net_plugin_impl::transaction_ack, my.get(), _1));
my->start_monitors();
//连接种子节点
for( auto seed_node : my->supplied_peers ) {
connect( seed_node );
}
if(fc::get_logger_map().find(logger_name) != fc::get_logger_map().end())
logger = fc::get_logger_map()[logger_name];
}
做两件事情:
- 建立本地监听
- 连接种子节点
建立本地节点比较简单,我们看连接种子节点的过程
/**
* Used to trigger a new connection from RPC API
*/
string net_plugin::connect( const string& host ) {
if( my->find_connection( host ) )
return "already connected";
//构造连接对象
connection_ptr c = std::make_shared<connection>(host);
fc_dlog(logger,"adding new connection to the list");
//将连接对象添加到connections
my->connections.insert( c );
fc_dlog(logger,"calling active connector");
//开始连接
my->connect( c );
return "added connection";
}
我们继续了解看下是如何连接的
void net_plugin_impl::connect( connection_ptr c ) {
if( c->no_retry != go_away_reason::no_reason) {
fc_dlog( logger, "Skipping connect due to go_away reason ${r}",("r", reason_str( c->no_retry )));
return;
}
auto colon = c->peer_addr.find(':');
if (colon == std::string::npos || colon == 0) {
elog ("Invalid peer address. must be \"host:port\": ${p}", ("p",c->peer_addr));
for ( auto itr : connections ) {
if((*itr).peer_addr == c->peer_addr) {
(*itr).reset();
close(itr);
connections.erase(itr);
break;
}
}
return;
}
auto host = c->peer_addr.substr( 0, colon );
auto port = c->peer_addr.substr( colon + 1);
idump((host)(port));
tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str() );
connection_wptr weak_conn = c;
// Note: need to add support for IPv6 too
//解析ip,解析成功则连接
// 解析出的节点ip可能不止一个(0到n个)
resolver->async_resolve( query,
[weak_conn, this]( const boost::system::error_code& err,
tcp::resolver::iterator endpoint_itr ){
auto c = weak_conn.lock();
if (!c) return;
if( !err ) {
//连接种子节点
connect( c, endpoint_itr );
} else {
elog( "Unable to resolve ${peer_addr}: ${error}",
( "peer_addr", c->peer_name() )("error", err.message() ) );
}
});
}
这是个什么样一个过程呢?我们看下种子节点在配置文件中的形式
p2p-peer-address = p2p.prod.eosgravity.com:80
p2p-peer-address = eu-west-nl.eosamsterdam.net:9876
p2p-peer-address = p2p.mainnet.eosgermany.online:9876
p2p-peer-address = eosbattles.com:9877
p2p-peer-address = 34.226.76.22:9876
...
域名+端口,或ip+端口,经过resolver->async_resolve的解析域名可能解析出多个ip,ip则原样输出,完成之后,进入真正连接操作
void net_plugin_impl::connect( connection_ptr c, tcp::resolver::iterator endpoint_itr ) {
if( c->no_retry != go_away_reason::no_reason) {
string rsn = reason_str(c->no_retry);
return;
}
auto current_endpoint = *endpoint_itr;
//指向下一个种子节点
++endpoint_itr;
c->connecting = true;
connection_wptr weak_conn = c;
c->socket->async_connect( current_endpoint, [weak_conn, endpoint_itr, this] ( const boost::system::error_code& err ) {
auto c = weak_conn.lock();
if (!c) return;
if( !err ) {
start_session( c );
c->send_handshake ();
} else {
//如果没有出错则连接下一个种子节点
//tcp::resolver::iterator()返回默认的迭代器,index_为0——>解析域名的时候出错,没有解析出ip
//指示到达末尾了,解析出来节点ip都不能用
if( endpoint_itr != tcp::resolver::iterator() ) {
close(c);
connect( c, endpoint_itr );
}
else {
elog( "connection failed to ${peer}: ${error}",
( "peer", c->peer_name())("error",err.message()));
c->connecting = false;
my_impl->close(c);
}
}
} );
}
如果连接成功,则进入握手阶段
start_session( c );
c->send_handshake ();
如果失败则尝试解析出的下一个ip,或已经没有下一个ip进入失败。
完!