TCPって、「信頼できる通信をしてくれる縁の下」として、中身を見ずに使いますよね。

でも、メッセージがくっついて届く、ポートが枯れる、謎の40ms遅延——ソケットを触った人が必ず出会う怪現象は、全部TCPの仕様が説明してくれます。今回は、その種明かしを8つ集めました。1つでも知らなかったら儲けものです。

① TCPに、「メッセージの区切り」はありません

いちばん大事な真実から始めます。

TCPが運ぶのは、メッセージではなく「切れ目のないバイトの川」です。

送信側がsendを2回呼んでも、受信側のrecv 1回でまとめて届くことがあります。逆に、send 1回分が2回に分割されて届くこともある。送った回数と受け取る回数は、一致する保証がありません。

「たまにメッセージが壊れる」自作プロトコルの原因は、ほぼこれです。だからHTTPは長さや区切りをヘッダで宣言し、あらゆるプロトコルが自前の「区切りルール」を持っているんです。

② 接続を「切った側」に、TIME_WAITが残ります

コネクションを閉じたのに、ソケットがしばらく居座る——TIME_WAITです。

これはバグではなく仕様で、先に切断した側が、遅れて届くパケットを受け止めるために数十秒(Linuxでは60秒)待つ仕組みです。

問題は、短命な接続を大量に張って切る側(クライアントやプロキシ)で、TIME_WAITが数万個溜まってポートが枯渇すること。「負荷試験でエラーが出るのに、サーバーは暇そう」の定番犯人です。

根本対策は、接続を使い回す(コネクションプーリング/keep-alive)こと。張っては切る設計そのものを見直すんです。

③ 謎の40ms遅延は、「気配り」の衝突です

小さいデータのやり取りで、たまに一定の遅延が乗る。これ、有名な相性問題です。

どちら側節約機能気配りの中身
送信側Nagleアルゴリズム小さいパケットを溜めて、まとめて送る
受信側遅延ACKACKを少し待って、まとめて返す

両方の気配りが噛み合うと、お互いに相手待ちになり、タイマー切れ(数十ms)まで固まるんです。Linuxの遅延ACKの最短タイマーが40msなので、「40ms」という数字でよく知られています。

対策は、送信側の節約をオフにするTCP_NODELAY。低遅延が命のプロトコルがこれを指定するのは、この衝突を避けるためなんです。

④ 「接続」は、途中の機器には見えていません

TCPコネクションという「線」が張られているイメージ、実は幻想です。

接続という状態は、両端のOSの中にしかありません。途中のルーターは、独立したパケットを転送しているだけです。

だから、片側だけが「接続は生きている」と信じ続ける状態(ハーフオープン)が起きます。相手が電源断で消えても、こちらが何か送るまで気づけない。

「切断を検知できると思っていたのに、何時間も気づかなかった」の理由がこれです。検知したいなら、次の⑤の仕組みが要るんです。

⑤ keep-aliveの既定は、「2時間」です

④の対策としてTCPには生存確認(keep-alive)がありますが、驚くのはその既定値です。

多くのOSで、最初の確認まで2時間。つまり既定のままでは、相手の死をリアルタイムには検知できません。しかもソケットオプションで明示的に有効化しないと、そもそも動きません。

だから実務では、アプリケーション層で自前のハートビートを打つのが定石です。WebSocketのping/pongやDB接続の死活確認は、TCP任せにしない、という設計判断なんです。

⑥ 1個のパケロスが、後続全部を道連れにします

TCPの「順序保証」には、代償があります。

パケットが1つ落ちると、その後ろのデータが届いていても、アプリには渡されません。抜けた部分の再送が来るまで、全員で待つ。ヘッドオブラインブロッキングです。

1本のTCPに複数のリクエストを相乗りさせるほど、この巻き添えは大きくなります。

HTTP/3がTCPを捨ててUDP上のQUICに移った最大の理由が、これです。順序保証をストリーム単位に分割して、巻き添えを止めたんですよね。

⑦ 受信が遅いと、送信は自動で止まります

TCPには、隠れた流量調整が組み込まれています。

受信側は「あとどれだけ受け取れるか(ウィンドウ)」を常に申告していて、受信アプリが読み取りをサボると、ウィンドウが0になり、送信側は完全に停止します。

これ自体は健全な仕組み(バックプレッシャ)ですが、知らないと怪奇現象に見えます。「送信側が遅い」と思って調べたら、犯人は受信側の処理詰まりだった——通信のボトルネック調査あるあるです。

遅いのはどちら側か。ウィンドウを見れば、答えが出るんです。

⑧ 接続確立だけで、1往復かかります

TCPは通信を始める前に、3回の挨拶(3ウェイハンドシェイク)をします。つまりデータを1バイトも送る前に、必ず1往復ぶんの時間(RTT)を払うわけです。

東京〜米国西海岸なら、それだけで100ms前後。TLSの鍵交換も乗れば、さらに往復が増えます。

だから現代の高速化は、「接続を減らす・使い回す・先に張っておく」が主戦場です。コネクションプール、keep-alive、そして接続と暗号化の往復を圧縮したQUIC。

速さの敵は帯域ではなく、往復回数。ネットワーク設計の合言葉なんです。

番外編:手元で確かめられる小ネタ

TIME_WAITを数えてみる

ss -tan | grep TIME-WAIT | wc -l

負荷試験の前後で数えると、②の話が目の前で起きます。

保証されるのは「順序と到達」だけ
TCPは「送った順に、いつか届く」を保証しますが、「いつ届くか」は保証しません。遅延の上限が要る世界(音声・ゲーム)でUDPが選ばれるのは、この割り切りなんですね。

SO_LINGERという危険なつまみ
切断時の挙動を変えるオプションですが、雑に使うと相手にエラーを叩きつける動きになります。「見つけたら意図を確認する」系の設定です。

RJCでも、通信と向き合っています

RJCが携わる決済や交通のシステムは、外部接続の安定性がそのままサービス品質になる世界です。②のプーリングや⑤のハートビートは、設計書の定番項目なんですよね。

こういう仕組みの話を、Techチームで共有しながら開発しています。

よくいただくご質問

Q. まずどれから押さえるべきですか?
A. ①かなと思います。「TCPはバイトの川」の一文だけで、通信まわりの不可解なバグの半分は説明がつくんですよね。

Q. ソケットを直接触らない仕事でも、必要ですか?
A. 効きます。②⑧はHTTPクライアントの設定(プール・keep-alive)の話そのものなので、APIを呼ぶ全員に関係あるんです。

Q. 障害調査でどこから見ればいいですか?
A. ssコマンドで接続の状態(TIME_WAIT、ウィンドウ)を見るのが最短です。⑦のとおり「どちら側が詰まっているか」まで分かることが多いです。

Q. 未経験からでも、分かるようになりますか?
A. なります。TCPは「郵便を、番号付きで、確認ハガキ付きで送る」という比喩で骨格が全部説明できるんです。

「信頼できる通信」の中身を知る

今日の8つに共通していたのは、これでした。

TCPの「信頼性」は魔法ではなく、待ち時間・再送・停止という実費で買われています。その実費がどこで発生するかを知っている人だけが、怪現象を仕様として説明できる。

「TCPだから大丈夫」から「TCPだから、ここに気をつける」へ。この一歩が、通信に強い人の入口なんですよね。

みなさんが踏んだ通信の怪現象、よかったら教えてください。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?