TT Lab
はじめる
学ぶ 学習パス コース

接続ひとつが遅くなり、残り全部が止まった

書き込み可能なのに接続拒否:非ブロッキング接続

TT Labで続きを見る

一言でいうと

書き込みの準備完了は、接続成功の印ではありません。接続中の状態で完了の準備を観察したあと、SO_ERRORを1回読み、最初の結果をアプリケーションの状態に保管します。

なぜ必要なのか

サーバーが接続を受け付ける例だけをテストすると、EVENT_WRITEが来た途端にconnectedと記録するコードも通ります。同じコードを閉じたポートに適用すると、接続が拒否されたのに緑のランプが点きます。もう待つ必要がないことと、成功したことを、同じ意味で使ったからです。「失敗も完了イベントを作る」という視点があってはじめて、診断器の成功率を信頼できます。

ここで作るのは、複数のターゲットへのTCP接続確立の結果を集める診断器です。接続できたらすぐに閉じ、HTTPリクエストやTLS証明書は検査しません。したがって、connectedは、ログインできること、APIが正常であること、データの保存に成功したことを意味しません。ユーザーに見える結果の名前が実際の検証範囲を超えると、正確なコードも不正確な運用ツールになります。

どう動くのか

connect(2)によると、Linuxの非ブロッキングTCP接続は、すぐに終わることも、進行中になることもあります。Python connect_exの整数の結果を、状態遷移に使います。このラボは、AF_INET/SOCK_STREAMに限定します。AF_UNIXの進行中のEAGAINの規則を、数値のIPv4 TCPにそのまま持ち込みません。

現在の状態 観察 次の状態
new connect_exの結果が0またはEISCONN connected、エラー0
new EINPROGRESSまたはEALREADY pending、結果は未定
new それ以外のエラー failed、該当するerrno
pending WRITEのあとSO_ERRORが0 connected
pending WRITEのあとSO_ERRORがエラー failed
new/pending 絶対的な締め切りまたはキャンセル timed_outまたはcancelled

pendingだからといって、connect_exを毎回呼び直すことはしません。一度開始した試行の結果を待つのです。失敗したソケットの再利用を、一般的なリトライの方法とはせず、閉じてから新しい試行を作ります。今回の診断器は自動リトライ自体を行わないので、結果1つが接続の試行1つに対応します。

socket(7)のSO_ERRORは、エラーを取得しながら消します。ログを残す関数が先に読み、状態判定の関数が2回目に読むと、2つの結果が変わることがあります。Dial.errorを置いて、最初の完了判定の値を保存してください。読むたびに「最新のエラー」を要求することが、精度を上げる方法ではありません。

python3 /opt/fixtures/reactor/connect_probe.py

実験は、1つのソケットにはlistenを呼び出し、もう1つのソケットはbindだけを行います。どちらもポートを押さえているので、「空いているポートを探して閉じ、もう一度接続する」競合がありません。Linuxコンテナで観察した進行中の接続の場合、どちらもwritable=trueでしたが、SO_ERRORはそれぞれ0とECONNREFUSEDでした。拒否の結果をもう一度読むと0でした。数字の111のようなerrnoの定数値はOSごとに違うことがあるので、コードにはerrno.ECONNREFUSEDを使います。すぐに完了した場合は、connect_exの最初の戻り値で分類します。

名前解決をこっそり挟まないこと

数値のアドレスの代わりにドメインを入れると、接続の前に名前解決の段階が生じます。ソケットを非ブロッキングに変えたという事実だけで、名前解決まで非ブロッキングになったとは言えません。このラボのhostは、数値のIPv4で検証し、localhostも拒否します。IPv6のアドレス候補の競合、DNSキャッシュ、Happy Eyeballsは、追加の設計が必要な別のテーマです。範囲を明示するのは、エラーを隠すためではなく、測定した段階が何なのかをはっきりさせるためです。

締め切りは、試行の開始時ではなく、Dialの生成時に決めます。待機キューに長くとどまった接続も、総時間を消費するからです。connect_exを呼び出す前にすでに締め切りを過ぎていたら、ネットワークの試行を始めずに、timed_outで終わらせます。終了状態は、以後のcancelやexpireが上書きしません。成功の直後に全体のキャンセルが来ても、すでに確認した成功をキャンセルに変えません。

現場での姿

2026-09-13に確認したCloudflare Software Engineer, Spectrumの求人情報は、Linuxソケットと、TCP接続状態、ライフサイクルの問題のデバッグを扱っています。ここでは、そのうち、接続確立と失敗原因の保存を練習します。その企業の公式教育や採用の保証ではなく、GoやRust、および大規模運用の能力は、別に学ぶ必要があります。

次の確認ですること

次のクイズで、観察と判定の違いを確認します。そのあとの総合ラボでは、Dialのnew→pending→終了の遷移を作り、実際のloopback TCPの成功と拒否をテストします。正解に見える「WRITEなら成功」の実装と、「SO_ERRORを再照会し続ける」実装が、なぜ間違いなのかを自分の言葉で説明してから、クイズで確認してください。