当前位置: 首页 > 工具软件 > Plugin.NET > 使用案例 >

EOS插件初始化之net_plugin

梁丘凯定
2023-12-01

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];
   }

做两件事情:

  1.  建立本地监听
  2.  连接种子节点

建立本地节点比较简单,我们看连接种子节点的过程

  /**
    *  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进入失败。

完!

 

 类似资料: