現在テスト中です: 完了後にGitHubのコードを公開する予定です。

トランスポート層

項目
トランスポートTCP(永続接続)
デフォルトポート5225
ソケットオプションTCP_NODELAY有効 — Nagleアルゴリズムによる遅延なしで小さいパケットも即座に送信
TLS任意 — サーバーに証明書が設定されていれば、ストリーム全体が片方向TLS(サーバー認証)で包まれる
エンコーディング固定長バイナリフィールド + UTF-8のキー。整数はビッグエンディアン(ネットワークバイトオーダー)
フレーム終端\nバイト1つ。\rの処理はなし
空フレーム単独の\n(op位置が\n)はキープアライブとして黙って無視される
最大フレームサイズ192バイト — opを含み\nを除く(超過 → E line_too_long後に接続終了
パイプライニング可能 — レスポンスはリクエストと異なる順序で届くことがある
アイドルタイムアウトなし — サーバーは静かな接続を自ら閉じることはない
フレーム進行時間opの最初の1 byteからfixed headerとLFまで最大5秒。部分送信でconnectionを無限占有できない

接続確立の順序

  1. TCP接続
  2. サーバーがTLSを有効にしている場合はTLSハンドシェイク — TLSサーバーに平文で接続すると この段階で切断される
  3. 認証ハンドシェイクを1回
  4. 以降はロックプロトコルのフレーム交換

フレーミングのルール

すべてのメッセージは1フレーム = 1行です。

[ op: 1バイト ][ opごとの固定ヘッダー ][ 可変長ボディ ] \n

受信側はopを見て、先に固定ヘッダーをバイト数分読み取り、その後で初めて\nを探す 必要があります。固定ヘッダーはバイナリであり、値として0x0A(\n)を含みうるためです (例: lease=100A)。\nの走査は末尾のキー/テキスト部分にのみ適用されます。

方向op固定ヘッダー
クライアント → サーバーA10 bytes (wait:u8 + lease:u8 + owner:u64 BE)
クライアント → サーバーR8 bytes (token:u64 BE)
サーバー → クライアントA16 bytes (token:u64 BE + owner:u64 BE)
サーバー → クライアントT · B8 bytes (owner:u64 BE)
サーバー → クライアントR · N8 bytes (token:u64 BE)
サーバー → クライアントM · L · E0 bytes (address/reason UTF-8 text)

frameが192 bytesを超えるかmalformedなら、サーバーは可能な場合Eをflushした後に接続を閉じます。クライアントもmalformed・oversized・未知op・相関不能なEをconnection-fatalとして扱います。このsessionで1 byteでも送信されたacquireは自動再試行せず、確定応答がなければIndeterminateです。

用語

  • フレーム — 連続したバイトストリーム(TCP)を「1つのメッセージ」として区切る単位。 このプロトコルでは、\nで終わる1行が1フレームです。
  • ビッグエンディアン — 整数の最上位バイトを先に書き込むバイト順序。ネットワーク プロトコルの標準であるため、ネットワークバイトオーダーとも呼ばれます。
  • パイプライニング — 前のレスポンスを待たずに次のリクエストを送ること。往復 レイテンシを重ねることでスループットが上がります。
  • キープアライブ — 接続がまだ生きていることを知らせるために定期的に送る、意味を 持たない信号。ここでは空フレーム(\n)がその役割を担います。