要求データを送り終わったので接続を閉じたい。でも、相手がこれから返す結果は受け取りたい。TCPには、二つの通信方向を別々に終了するハーフクローズという考え方があります。「送るのはここまで」と「もう何も受け取らない」を区別すると、終了処理で応答を取りこぼす理由が見えてきます。
簡単にまとめると?
- TCPは双方向の通信で、片方の送信が終わっても、逆方向のデータ送信は続けられます。この一方向だけを閉じた状態がハーフクローズです。
- LinuxのshutdownでSHUT_WRを指定すると送信側を終了し、受信側を残せます。両方向を禁止するSHUT_RDWRとは目的が異なります。
- 送信の終了やTCPの受領確認だけでは、相手のアプリケーションが処理を成功させた証拠にはなりません。必要な結果は応答として確認し、待ち時間や異常終了を別に扱います。
一本の接続に、二方向の流れがある
TCP接続を一本の管のように思い浮かべると、閉じる操作も一つに見えます。ここではAからBへのバイト列と、BからAへのバイト列という、二方向の流れに分けて考えます。それぞれの送り手が、これ以上送るデータがないことを相手へ知らせます。
TCPの仕様RFC 9293では、送信の終了を知らせた側が、相手側も終了するまで受信を続ける動作を説明しています。一方からFINを受け取った側が、その瞬間に自分のFINも返すとは限りません。自分のアプリケーションが送る内容を残していれば、その方向の通信を続けられます。
このように一方向だけが閉じられた状態が、ハーフクローズです。接続の両端で認識が食い違っている「ハーフオープン」とは別の用語です。ここでは、片側が故障して消えた例ではなく、通信の終了を方向ごとに扱う正常な使い方を考えます。
送信完了を、応答待ちの開始にする例
例として、Aが短い入力データをBへ送り、Bが入力全体を受け取ってから計算結果を返す、独自の小さな通信規約を考えます。この例では、Aからのストリームの終端を「入力はこれで全部」という印に使う約束にします。HTTPなどの既存プロトコル全般がこの方式だという意味ではありません。
Aがデータを送った後、送信方向だけを終了すれば、Bは入力の終わりを認識できます。その後Bが結果を逆方向へ送り、Aは受け取り続けます。Bも送信を終えると、Aは応答側の終端を確認できます。Aが最初の送信終了時点で受信まで手放してしまうと、この後半の結果を受け取れなくなります。

本稿のモデルでは、AがABCを送った後にAの送信を終了し、それからBがRESULTを返せることを確認しています。Aから追加で送ろうとする操作は、モデル上で拒否します。これは方向の独立性を確かめる例で、実機のパケット交換や待ち時間を測ったものではありません。
Linuxではshutdownの方向指定を読む
LinuxのソケットAPIには、接続の全部または一部を終了させるshutdown(sockfd, how)があります。howにSHUT_WRを指定すると、それ以降の送信を禁止します。SHUT_RDは受信を、SHUT_RDWRは受信と送信の両方を禁止する指定です。
今回のように「入力を送り切った後も結果は読みたい」という設計で検討するのは、送信側だけを終了するSHUT_WRです。単に「終了用の関数を呼ぶ」と覚えると、両方向を閉じる指定や、ファイル記述子を解放するcloseと混同しやすくなります。利用する言語やライブラリが、どのAPIと方向指定に対応しているかも確認します。
shutdownの戻り値も点検します。成功は0、失敗は−1で、errnoが理由を示します。接続されていない、対象がソケットではないなどの条件で失敗するため、呼び出したという事実だけを、送信終了が正常に受理された証拠にはしません。
また、TCP仕様に書かれた抽象的なCLOSE操作と、個々のOSや言語のclose関数を、名前だけで同一視しないことも大切です。RFCは、CLOSE後に受信できない実装の扱いも記しています。実際に使うAPIの契約を読み、片方向終了を意図どおり表せるかを確認します。
FINは、業務処理の成功通知ではない
TCPの正常終了と、接続を打ち切るRSTは区別されます。通常のFINを用いた終了では、先に送ったデータも含めて通信を終える手順が進みます。一方、異常な打切りを、同じ「入力を最後まで受け取った」という意味にはできません。
さらに、相手のTCPがデータを受け取ったことと、その先のアプリケーションが計算・検査・保存を成功させたことは、異なる層の出来事です。たとえばBが入力を受け取った後に内容の検査で拒否するなら、Aが知るべきなのはBの応答です。Aの送信が終わったことだけでは、結果の成功・失敗を判断できません。
このため、実際の通信規約では、正常な結果、相手が返した失敗、通信途中の異常、期限切れを区別しておくと整理しやすくなります。ハーフクローズは逆方向を残す仕組みであり、相手が必ず応答する保証ではありません。応答を待つ期限も、アプリケーション側の目的に合わせて決める必要があります。
終了で区切る方式を選ぶ前に
今回の例は、入力の終わりを一方向のストリームの終端で表しています。同じ送信方向で次の要求も続けて送りたいなら、この方式はそのまま使えません。長さや区切りを通信内容の中に持たせるなど、別の規則が必要になります。TCPはバイト列を運ぶので、アプリケーションの要求単位はその上で定義します。
設計や障害調査では、Aの送信、Bの受信、Bの送信、Aの受信を別々に並べ、「どこを終え、どこをまだ使うのか」を確認します。送信終了後も読む必要があるのか、何をもって処理成功とするのか、いつ待つのをやめるのか。この三つが明確になると、終了処理を単なる後片付けではなく、通信規約の一部として扱えます。
出典・参考資料
- RFC 9293 — Transmission Control Protocol(2022年8月)、第3.6節・3.6.1節の終了とハーフクローズ、第3.7節のバイトストリームとアプリケーション境界。本文はこの版の規定を参照し、すべての実装の動作を実証するものではない。
- Linux man-pages 6.19 — shutdown(2):方向指定、戻り値、エラー。掲載HTMLを2026年10月22日に確認。ABC/RESULTの例と図は独自モデル。
