動画と記事で学ぶ / IP・通信プロトコル
TCPの3ウェイハンドシェイクをSYN→SYN/ACK→ACKの図とWiresharkで解説。Seq・ACK番号の読み方、SYN再送・接続拒否・ACK欠落の違いまで現場の確認順で整理します。
ここからは、検索目的に合わせて再構成した記事本文を読めます。図解・手順・コマンド例がある場合は本文内で確認できます。
3ウェイハンドシェイクとは、TCP通信を始める前に、クライアントとサーバーがSYN、SYN/ACK、ACKの3つのセグメントを交換して接続を確立する手順です。
言葉だけなら「SYN→SYN/ACK→ACK」で終わります。しかし、実務で本当に役立つのは、なぜ3回必要なのか、シーケンス番号がなぜ+1されるのか、どのパケットで止まると何を疑うのかまで説明できることです。
この記事では、元インフラエンジニアの筆者が、3ウェイハンドシェイクの仕組みを図でほどき、Wiresharkでの見方と通信障害の切り分けまで一つの流れで解説します。
3ウェイハンドシェイクを理解すると、「TCPポートへ到達できない」のか「接続後のTLSやアプリケーションで失敗した」のかを切り分けやすくなります。
3ウェイハンドシェイクとは
3ウェイハンドシェイク(three-way handshake)は、TCPでデータ通信を開始する前に行う接続確立の手順です。クライアントとサーバーが3つのTCPセグメントを交換し、次の準備を整えます。
- クライアントからサーバーへ到達できることを確認する
- サーバーからクライアントへ応答できることを確認する
- 双方の初期シーケンス番号を交換する
- MSSやWindow ScaleなどのTCPオプションを交換する
- 双方が通信状態をESTABLISHEDへ進める
「TCPコネクションを張る」といっても、経路上に専用線が作られるわけではありません。クライアントとサーバーが、送信元IP、送信元ポート、宛先IP、宛先ポートの組み合わせと、送受信する番号をそれぞれ管理する状態を作ります。
| 比較 | TCP | UDP |
|---|---|---|
| 通信前の接続確立 | 3ウェイハンドシェイクを行う | 行わない |
| 到達確認・再送 | ACKや再送で制御する | UDP自体にはない |
| データの順序 | シーケンス番号で管理する | UDP自体では保証しない |
| 代表例 | HTTP/HTTPS、SSH、メール | DNS、音声・映像、監視 |
3ウェイハンドシェイクの流れ|SYN・SYN/ACK・ACK
例として、クライアントがWebサーバーのTCP/443へ接続する場面を考えます。クライアント側の初期シーケンス番号を1000、サーバー側を5000とします。
192.0.2.10:52000
198.51.100.20:443
1回目:クライアントがSYNを送る
クライアントは、サーバーの待受ポートへSYNフラグを立てたTCPセグメントを送ります。SYNは「新しい接続を開始したい。こちらの初期シーケンス番号は1000です」という意味です。
- 送信元:クライアントの一時ポート
- 宛先:サーバーの待受ポート
- フラグ:SYN=1、ACK=0
- シーケンス番号:1000
クライアントの状態はCLOSEDからSYN-SENTへ移ります。ここで応答がなければ、一定時間後にSYNを再送します。
2回目:サーバーがSYN/ACKを返す
サーバーが対象ポートで待ち受けており、接続を受け付けられる場合はSYNとACKの両方を立てて返します。これは「SYNを受け取りました。次は1001を待っています。こちらの初期シーケンス番号は5000です」という応答です。
- フラグ:SYN=1、ACK=1
- サーバーのシーケンス番号:5000
- クライアントへのACK番号:1001
サーバーの状態はLISTENからSYN-RECEIVEDへ移ります。この段階ではまだ接続確立の途中です。
3回目:クライアントがACKを返す
クライアントはサーバーのSYN/ACKを受け取り、ACKを返します。「サーバーの初期シーケンス番号5000を受け取り、次は5001を待っています」という確認です。
- フラグ:SYN=0、ACK=1
- クライアントのシーケンス番号:1001
- サーバーへのACK番号:5001
このACKをサーバーが受け取ると、双方がESTABLISHEDになります。その後、HTTPSならTLSのClient Hello、HTTPならリクエストなど、アプリケーションデータの送受信へ進みます。
| 順番 | 方向 | フラグ | 役割 |
|---|---|---|---|
| 1 | クライアント → サーバー | SYN | 接続要求とクライアント側ISNの通知 |
| 2 | サーバー → クライアント | SYN/ACK | SYNの受領確認とサーバー側ISNの通知 |
| 3 | クライアント → サーバー | ACK | サーバー側SYNの受領確認 |
なぜ3回のやり取りが必要なのか
TCPは双方向通信です。クライアントからサーバーへ送るデータと、サーバーからクライアントへ送るデータには、それぞれ独立したシーケンス番号があります。そのため双方が自分の初期シーケンス番号を伝え、相手が受け取ったことを確認する必要があります。
必要な役割は、次の4つです。
- クライアントが自分のSYNを送る
- サーバーがクライアントのSYNをACKする
- サーバーが自分のSYNを送る
- クライアントがサーバーのSYNをACKする
サーバーは2と3を一つのSYN/ACKにまとめられるため、合計3セグメントになります。
2回で終えると、サーバーは自分が送ったSYN/ACKをクライアントが受け取ったか確認できません。古いSYNや送信元を偽装したSYNでも、相手が存在すると判断して接続状態を作り続けることになります。3回目のACKには「あなたの開始番号も確かに受け取りました」という役割があります。
シーケンス番号とACK番号の読み方
シーケンス番号(Sequence Number)は、送信するバイト列の位置を表します。ACK番号(Acknowledgment Number)は「ここまで受け取った」ではなく、正確には次に受け取りたいシーケンス番号です。
クライアントがSeq=1000のSYNを送った場合、サーバーはAck=1001を返します。SYNには通常アプリケーションデータがありませんが、TCPのシーケンス空間を1つ消費するためです。同じ理由で、サーバーのSeq=5000に対してクライアントはAck=5001を返します。
Seq=1000返すACK
Ack=1001
SYNとFINはペイロードがなくても、それぞれシーケンス番号を1つ消費します。
WiresharkでSeq=0と表示される理由
実際の初期シーケンス番号は大きな値ですが、Wiresharkは読みやすくするため、通常は最初の番号を0とする相対シーケンス番号で表示します。その場合、正常な3ウェイハンドシェイクは次のように見えます。
- SYN:
Seq=0 - SYN/ACK:
Seq=0 Ack=1 - ACK:
Seq=1 Ack=1
「本当に0から始まっている」と判断しないでください。パケット詳細の設定で相対表示を外せば、TCPヘッダーの実際の値を確認できます。
3ウェイハンドシェイク中のTCP状態遷移
パケットだけでなく、OSが持つTCPの状態も合わせて見ると理解が深まります。
CLOSED
↓ SYN送信
SYN-SENT
↓ SYN/ACK受信・ACK送信
ESTABLISHED
LISTEN
↓ SYN受信・SYN/ACK送信
SYN-RECEIVED
↓ ACK受信
ESTABLISHED
Linuxではss -tn、WindowsではGet-NetTCPConnectionやnetstat -anoで状態を確認できます。SYN-SENTが残っていればクライアントは応答待ち、SYN-RECEIVEDが大量に残っていればサーバーが最後のACKを受け取れていない可能性があります。
SYNで交換するTCPオプション
SYNとSYN/ACKでは、初期シーケンス番号だけでなく、データ転送に使う条件も交換します。パケットサイズや性能問題を調べるときは、次の項目を確認します。
| 項目 | 意味 | 見る場面 |
|---|---|---|
| MSS | 1セグメントで受信できるTCPデータの最大値 | 断片化、VPN、MTU問題 |
| Window Scale | 受信ウィンドウを拡大する倍率 | 高帯域・高遅延回線の性能 |
| SACK Permitted | 欠落部分を選択的に通知できることを示す | 再送が多い通信 |
| Timestamps | 時刻値を使ったRTT計測など | 遅延や再送の解析 |
MSSは双方が同じ値である必要はありません。各方向で「自分が受信できる上限」を伝えます。また、Window Scaleは接続確立後に突然有効化できるものではなく、SYN段階で合意します。3ウェイハンドシェイクは、後続通信の性能条件を決める場面でもあります。
Wiresharkで3ウェイハンドシェイクを確認する方法
Wiresharkでは、まず送信元・宛先IPとポート番号を確認し、その後にフラグ、Seq、Ack、再送の有無を同じTCPストリームで追います。
基本の表示フィルタ
# SYNだけを見る
tcp.flags.syn == 1 && tcp.flags.ack == 0
# SYNとSYN/ACKの両方を見る
tcp.flags.syn == 1
# RSTを見る
tcp.flags.reset == 1
# 特定の接続を最初から最後まで追う
tcp.stream eq 0
# 特定のIPとポートに絞る
ip.addr == 198.51.100.20 && tcp.port == 443
正常な通信の確認順序
- クライアントからサーバーへSYNが出ている
- 逆方向にSYN/ACKが返っている
- クライアントがACKを返している
- 3つのパケットでIPアドレスとポートの組み合わせが反転している
- SeqとAckが相手の番号に対応している
- 直後にTLSやHTTPなどのデータが続いている
フラグだけを見て「正常」と決めないことが重要です。別の接続のSYN/ACKを見ている可能性があるため、同じtcp.streamで追います。私は障害調査で、正常系と異常系を同じフィルタ、同じ取得地点で並べ、最初に差が出たパケットを探していました。
どのパケットで止まったかを先に見る
| キャプチャの状態 | 最初に確認すること |
|---|---|
| SYNだけが再送される | 宛先IP・経路・FW・サーバー到達性。戻りのSYN/ACKも確認 |
| SYNにRSTが返る | 宛先ポートの待受、送信元・宛先、途中機器による拒否 |
| SYN/ACKまでは見える | 最終ACKが返る経路、端末側FW、非対称経路 |
| 3回成立後に止まる | アプリの要求・応答、TLS、サーバーログ。TCP接続だけでは業務正常とは判定しない |
同じ通信を送信元と宛先の両側で観測すると、途中で消えた方向を絞れます。接続状態とシーケンス番号の根拠はRFC 9293(TCP)を参照してください。
3ウェイハンドシェイクの失敗パターンと障害切り分け
通信障害では「接続できない」という結果より、どのパケットまで確認できたかが重要です。SYNの有無、応答の種類、再送方向を分けると、調査範囲を狭められます。
| 見え方 | 状態 | 主な確認先 |
|---|---|---|
| SYNが出ていない | 接続処理が始まっていない | 名前解決、アプリ、宛先指定、ローカル設定 |
| SYNだけを再送 | 応答がクライアントへ届かない | 宛先IP、経路、ARP、ACL/FW、NAT、サーバー停止、戻り経路 |
| SYNの直後にRST/ACK | 宛先まで届いたが接続を拒否 | 待受プロセス、ポート番号、サーバー側FW |
| SYN/ACKを再送 | サーバーが最後のACKを受け取れない | クライアント側FW、NAT、非対称経路、戻り通信 |
| 3回完了後に失敗 | TCP接続は一度成立 | TLS、証明書、HTTP、認証、アプリケーション |
SYNを再送してタイムアウトする
クライアントがSYNを繰り返し送っても何も返らない場合、一般に接続タイムアウトになります。パケットが往路で破棄されている、サーバーが停止している、サーバーのSYN/ACKが復路で破棄されているなど、原因は一つではありません。
送信元だけのキャプチャでは「SYN/ACKが返らない」ことまでしか分かりません。可能ならサーバー側やFWでも同時刻に確認し、SYNがどこまで届いたかを追います。
Connection refusedになる
SYNに対してRST/ACKが返ると、アプリケーションでは「Connection refused」「接続が拒否されました」と表示されることがあります。相手まで到達している可能性は高いものの、指定ポートでプロセスが待ち受けていない、またはFWが明示的に拒否している状態です。
サーバーでLinuxならss -lntp、WindowsならGet-NetTCPConnection -State Listenなどを使い、対象IPとポートで待ち受けているか確認します。
SYN/ACKの後にACKが返らない
サーバー側でSYN/ACKの再送が続く場合、クライアントの最後のACKが届いていません。クライアントがSYN/ACKを受信できているか、受信後にACKを送っているかを切り分けます。非対称経路やNAT配下では、往路と復路で異なるFWを通り、セッションとして認識されないことがあります。
3ウェイハンドシェイク後に通信が止まる
ACKまで完了していれば、原因をTCP接続確立より上の層へ移します。HTTPSならTLS Client HelloやServer Hello、HTTPならリクエストとレスポンス、SSHならバージョン交換を確認してください。「SYN/ACKが見えるからアプリも正常」とは限りません。
pingが通るのにTCP接続できない理由
pingはICMP、WebやSSHはTCPです。pingが成功しても、TCP/443やTCP/22がFWで拒否されている、対象ポートでアプリが待ち受けていない、NATルールがないといった問題は残ります。pingの成功はIP到達性を確認する一材料であり、TCPポートの疎通を保証しません。
3ウェイハンドシェイクとSYNフラッド攻撃
サーバーはSYNを受け取ってSYN/ACKを返すと、最後のACKを待つ半接続状態を一定時間保持します。この仕組みを悪用し、大量のSYNを送り続けて接続待ちの領域を消費させる攻撃がSYNフラッドです。
監視では、SYN-RECEIVEDの増加、SYNに対して完了するACKの割合、特定送信元からの急増、SYN/ACK再送を確認します。対策にはSYN Cookie、接続キューやタイムアウトの調整、FW・ロードバランサーでのレート制御などがあります。ただし、正常なアクセス急増や復路障害でも似た見え方になるため、パケット数だけで攻撃と断定しません。
3ウェイハンドシェイクと4ウェイハンドシェイクの違い
3ウェイハンドシェイクは接続を始める手順です。一方、TCP接続を正常に終了するときは、一般にFINとACKを4回交換します。
| 比較 | 3ウェイハンドシェイク | 4ウェイハンドシェイク |
|---|---|---|
| 目的 | TCP接続の確立 | TCP接続の正常終了 |
| 主なフラグ | SYN、ACK | FIN、ACK |
| 基本の流れ | SYN → SYN/ACK → ACK | FIN → ACK → FIN → ACK |
| 回数が違う理由 | サーバーがSYNとACKをまとめられる | 各方向を独立して閉じるため |
TCPは全二重通信なので、一方が送信を終えても、もう一方はデータを送り続けられます。そのため終了時は、両方向を個別に閉じるのが基本です。RSTはこの正常終了とは異なり、接続を即座に打ち切るために使われます。
3ウェイハンドシェイクのよくある質問
3ウェイハンドシェイクを一言で説明すると?
TCP通信を始める前に、クライアントとサーバーがSYN、SYN/ACK、ACKを交換し、双方の初期シーケンス番号と通信可能性を確認して接続を確立する手順です。
SYNとACKは何の略ですか?
SYNはSynchronize、ACKはAcknowledgmentの略です。SYNはシーケンス番号を同期して接続を開始する意思を示し、ACKは受信確認と次に期待するシーケンス番号を示します。
なぜACK番号はシーケンス番号より1大きいのですか?
ACK番号は次に受け取りたい番号を表し、SYNがTCPのシーケンス空間を1つ消費するためです。Seq=1000のSYNを受け取った側は、Ack=1001を返します。
UDPでも3ウェイハンドシェイクを行いますか?
行いません。UDPはコネクションレス型であり、UDP自体にはTCPのSYN、ACK、再送制御、順序保証がありません。必要な確認は上位のアプリケーションが独自に実装します。
HTTPSでは3ウェイハンドシェイクを何回行いますか?
基本的に新しいTCP接続ごとに1回です。その後にTLSハンドシェイクが続きます。ただし、接続の再利用やHTTP/2・HTTP/3などにより、Webページ上のリクエスト数とTCP接続数は一致しません。HTTP/3はUDP上のQUICを使うため、TCPの3ウェイハンドシェイクは行いません。
3ウェイハンドシェイクの途中でパケットが失われたら?
送信側は応答を待ち、タイムアウトするとSYNまたはSYN/ACKを再送します。規定回数で確立できなければ接続は失敗します。Wiresharkでは再送元と応答が消えた方向を確認します。
3ウェイハンドシェイクが完了すればアプリも正常ですか?
いいえ。分かるのは、その時点でTCP接続が成立したことです。TLS証明書、認証、HTTPエラー、アプリケーション停止などは別に確認する必要があります。
関連する記事
3ウェイハンドシェイクとあわせて、ポート番号、経路、パケットキャプチャを理解すると、TCP障害を一連の流れで追えるようになります。
まとめ:3パケットの役割と止まった場所を読む
3ウェイハンドシェイクは、クライアントのSYN、サーバーのSYN/ACK、クライアントのACKでTCP接続を確立します。双方が初期シーケンス番号を伝え、相手が受け取ったことを確認するため、3回の交換が必要です。
障害時は「接続できない」で止めず、SYNが出たか、宛先へ届いたか、SYN/ACKが返ったか、最後のACKが届いたかを順に確認してください。3回完了後なら、TLSやアプリケーション層へ調査を進めます。この切り分けができると、TCP障害の報告と初動が大きく変わります。
