読者です 読者をやめる 読者になる 読者になる

Tech と Culture

テクノロジーとカルチャー

翻訳:Satoshiクライアントの初期化とスタートアップ

続けてこちら。
 
Satoshiクライアントの初期化とスタートアップ
 
1 スタートアッププロセス
2  fClientモード
3  net.cppのStartNode()スレッド
4  net.cppのIRCSeedスレッド
5  net.cppのThreadSocketHandlerスレッド
6  net.cppのThreadOpenConnectionsスレッド
7  net.cppのThreadMessageHandlerスレッド
8  rpc.cppのThreadRPCServerスレッド
 
1 スタートアッププロセス
クライアントはスタートアップ時に一定の数のスレッドを作ります。アクティブなネットワークソケットの全ての読み、書きは一つのスレッドによってハンドルされます。
プログラム中のmain()のエントリポイントはinit.cppにあります。main.cppの中にmain()を探さないようにーその中には見つかりません。
main()が最初に行う事はAppInitをコールすることで、それはAppInit2をコールします。AppInit2はどのようにアプリケーションがエラーをハンドルするかに関する、たくさんの初期化の仕事をします。あるポイントでAppInit2はParseParametersをコールし、基本的な引数の幾つかがハンドリングされます。
さあ、GUIウィンドウがすでに存在しているかチェックし、もし存在していればフォアグラウンドに持ってきます。
次にbitcoinディレクトリのlock fileを生成し、もしすでにbitcoinが実行中であればメッセージを出力してイグジットします。
次にbitcoinのポートをリッスンします。もしポートがすでに使われていたり、他のエラーがあればメッセージを出力してイグジットします。
次にデータベースからIPアドレスをロードし、ブロックインデックスをロードし、それからウォレットをロードします。
次にウォレットからトップのブロックナンバーを取得します。 -rescan引数が指定された時は0になります。
Side Note: printfはutil.hで再定義されています。なのでprintfは実際にはOutputDebugStringFであり、それはもし適切であればファイルへ出力されます(util.hとutil.cppを参照)。“loop”はutil.hでfor(;;)と定義されています。
次に、もう少し初期化処理を行い、パラメータを処理し、最後にGUIのメインウィンドウを開きます。
次に、二つのスレッドを作ります。
  1. StartNode()
  2. サーバーとして動作する場合:ThreadRPCServer()
ここがこれらのスレッドが生成される唯一の場所です。
StartNode()はnet.cppの中にあり、ThreadRPCServer()はrpc.cppの中にあります。
最後に、GUIとしてコンパイルされていなかったら、五秒間のスリープの後、無限ループに入ります。
 
2 fClient Mode
クライアントには、ブロックヘッダーのみをダウンロードするモードを許すコードが存在していることは注目に値する。この実装はlightweightクライアントで使われることを意図しており、そこでは全てのブロックとtransactionを検証やストアする必要がない。これはfClient変数でコントロールでき、現在はfalseにハードコードされている。これは現在はまだコードが完成しているとは想定されていないということです。このモードはfClientモードと知られており、Simplified Payment Verification(SPV)モードという言葉もlightweightクライアントを表現するのに使われています。
 
3 net.cppのStartNode()スレッド
StartNodeはネットワーキングスレッドの一種のマスターのようなものだ。
最初にCNodeを作り、コミュニケーションを取り扱うためにlocalhost 127.0.0.1を内部アドレスとする。
次にローカルのIPアドレスを取得する。Windowsではlocal host nameを使用して見つける。Linuxでは、loopbackインターフェースでない、upされているインターフェースを見つけて最初のIPアドレスとします。
次にThreadGetMyExternalIPスレッドをつくり、ローカルIPアドレスサードパーティの検証を使用して確認します。もしproxyを使用していても、接続しようとしてくるコネクションは無視するので問題ありません。
次に、もしuPnPが使用されている場合は、ThreadMapPortが作られてポートマッピングを行います。
次にThreadIRCSeedスレッドが作られてIPアドレスを交換します。
次にThreadSocketHandlerスレッドが作られて、ソケットで送信、受信を行い、コネクションを受け付けます。
次にThreadOpenConnectionsスレッドによって外向きのコネクションを初期化します。
次にThreadMessageHandlerスレッドをつくり、メッセージを処理します。
最後にもし指定していたなら、マイニングスレッドが始まります。
 
4 net.cppのIRCSeedスレッド
92.243.23.21のポート6667に接続し、チャンネルにジョインし、それからラインを読み、他のユーザを検索します。
 
5 net.cppのThreadSocketHandlerスレッド
必要なサービスを行うサービスソケットの無限ループに入ります。接続が切れたノードをハンドルします。”選択”デスクリプタリストを準備し、”選択”ヲコールシ、カンケイする全てのソケットを50msecのタイムアウトでI/O待ちします。リスニングソケット上の新しい、接続してくるコネクションを処理します。それらにCNodeを作ります。受信と送信をハンドルします。何もしないソケットをディスコネクテッド状態にします。
 
6 net.cppのThreadOpenConnectionsスレッド
パラメータ、シード、IRC等からノードを解決し、ループに入って一つづつ接続していきます。
 
7 net.cppのThreadMessageHandlerスレッド
このスレッドは全てのノードをとおして、main.cppのProcessMessages(pnode)をコールします。そこではノード受信キュー(pFrom->vRecv)に有効なメッセージを探し、もし見つかった場合は、main.cppのProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
をコールします。
それからスレッドはそれぞれのノードに対しmain.cppのSendMessagesをコールシ、それぞれのノードに対する適切なメッセージを作って送信します。
 
8 rpc.cpp内のThradRPCServerスレッド
このスレッドは少し込み入っていて、HTTP(S)+JSONRPCサーバーをboostクラスで実装しています。おそらく大半の開発者にとってはあまり馴染みのないものです。
以下のような行を見ることができます。
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
 
また以下のような行もあります。
boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
if(!api_caller.timed_join(boost::posix_time::seconds(GetArg(“-rpctimeout”,30))))
 
上記のコードは30秒タイムアウトをHTTPrequestを読むときに設定するためのスレッドを作ることが見て取れます。HTTPリクエストが読まれたとき、パースされ、リクエストコマンド名に対応するルーチンが呼ばれてリクエストがハンドルされます。これはシャットダウンされるまでループします。