【TCP】シーケンス番号とACK番号について解説

音声で聞く

読む前にこの記事のポイントを音声で確認できます。この音声は本記事をもとにAIツール(NotebookLM)で自動生成したもので、発音や言い回しが不自然な箇所や、内容の誤りが含まれる可能性があります。理解の補助としてご利用ください。

TCP は、シーケンス番号(SEQ)と確認応答番号(ACK)を使って、送ったデータが相手に届いたかを確かめながら通信します。パケットキャプチャで再送や欠落を調べるときも、この2つの番号の動きが読めることが前提になります。

この記事では、SEQ と ACK の意味を整理したあと、接続から切断までの簡単な例で、2つの番号がどう増えていくかを1つずつ確認します。

この記事でわかること
  1. シーケンス番号と ACK 番号が、それぞれ何を表す数字か
  2. SYN と FIN はデータがなくても番号を1つ使うこと
  3. 3-way handshake からデータ送信、切断までの SEQ と ACK の計算

シーケンス番号とACK番号とは

TCP は、送るデータを1バイトずつ番号を付けて管理します。シーケンス番号は、送るデータの先頭が何番目のバイトかを表します。ACK 番号は、次に受け取りたいバイトの番号で、「その手前までは受け取った」という意味になります。

番号意味次の値の決まり方
シーケンス番号(SEQ)自分が送るデータの先頭バイトの番号次の SEQ = 今の SEQ + 送ったデータのバイト数
ACK 番号相手から次に受け取りたいバイトの番号(その手前までは受け取った)ACK = 受け取ったセグメントの SEQ + そのデータのバイト数

シーケンス番号は、クライアントからサーバへの方向と、サーバからクライアントへの方向で別々に数えます。クライアントの SEQ はサーバの ACK と、サーバの SEQ はクライアントの ACK と対応します。

シーケンス番号の考え方

シーケンス番号は、最初の値から、送ったデータのバイト数だけ増えていきます。たとえば、1000 バイトのデータを 100、200、300、400 バイトの4つのセグメントに分けて送る場合を考えます。

12・・・10001000 バイトのデータ12・・・100100 バイトSEQ=1101102・・・300200 バイトSEQ=101301302・・・600300 バイトSEQ=301601602・・・1000400 バイトSEQ=601
図1 セグメントの先頭バイトの番号がシーケンス番号になる
セグメント送るバイトシーケンス番号
1つ目(100 バイト)1〜100 番目1
2つ目(200 バイト)101〜300 番目101
3つ目(300 バイト)301〜600 番目301
4つ目(400 バイト)601〜1000 番目601

受信側は、シーケンス番号を見てセグメントを正しい順番に並べ直し、抜けているバイトがないかを確認します。

実際のシーケンス番号

実際のシーケンス番号は 1 や 0 から始まるわけではありません。接続ごとに決まる初期シーケンス番号(ISN)から始まり、32 ビットの範囲で増えていきます。Wireshark は、初期値を 0 として数え直した相対シーケンス番号(Relative sequence numbers)を既定で表示するので、番号の増え方を追いやすくなっています。大事なのは番号がどこから始まるかではなく、どう増えていくかです。

接続から切断までの例

次の擬似的な通信を例に、シーケンス番号と ACK 番号がどう計算されるかを確認します。番号は Wireshark の相対シーケンス番号と同じく、初期値を 0 として数えます。

  • クライアントからサーバへ TCP 接続を開始する
  • クライアントからサーバへ 111 バイトのデータを送る
  • サーバからクライアントへ 222 バイトのデータを送る
  • サーバから TCP 接続を終了する
クライアントサーバ3-way handshakeサーバからの切断クライアントからの切断データの送受信SYNSEQ=0SYN/ACKSEQ=0, ACK=1ACKSEQ=1, ACK=1データSEQ=1, ACK=1111 バイトデータSEQ=1, ACK=112222 バイトACKSEQ=112, ACK=2230 バイトFIN/ACKSEQ=223, ACK=112ACKSEQ=112, ACK=224FIN/ACKSEQ=112, ACK=224ACKSEQ=224, ACK=113
図2 接続から切断までの SEQ と ACK の流れ

3-way handshake で接続する

クライアントは、SEQ=0 で SYN を送ります。SYN はデータを持ちませんが、シーケンス番号を1つ使います。そのため、SYN を受け取ったサーバは「0 番まで受け取った。次は 1 番がほしい」として ACK=1 を返します。サーバも自分の SEQ=0 で SYN を送るので、SYN と ACK を1つのセグメント(SYN/ACK)にまとめて送ります。

クライアントは、サーバから ACK=1 を受け取ったので、次は SEQ=1 で送ります。また、サーバの SYN(SEQ=0)を受け取ったので ACK=1 を返します。

補足

最初の SYN には ACK フラグが立っていないので、ACK 番号は意味を持ちません。ACK 番号が使われるのは、ACK フラグが立っているセグメントだけです。

クライアントが 111 バイトを送る

クライアントは、SEQ=1、ACK=1 のまま 111 バイトのデータを送ります。直前の ACK はデータを持たず、シーケンス番号を使わないので、SEQ は 1 のままです。このデータは 1〜111 番目のバイトです。

サーバが ACK=112 で 222 バイトを送る

サーバは 111 番目まで受け取ったので、次は 112 番目がほしいという意味で ACK=112(1+111)を送ります。同時に、サーバは自分の SEQ=1 から 222 バイトのデータを送ります。

クライアントが ACK=223 を返す

クライアントは、サーバから ACK=112 を受け取ったので、次は SEQ=112 で送ります。また、サーバのデータを 1〜222 番目まで受け取ったので、ACK=223(1+222)を返します。このセグメントはデータを持たない(0 バイト)確認応答です。

サーバから切断する

サーバは、SEQ=223、ACK=112 で FIN を送ります(ACK フラグも立っているので FIN/ACK と表示されます)。FIN も SYN と同じく、データを持たずにシーケンス番号を1つ使います。クライアントは FIN を受け取ったので、ACK=224(223+1)を返します。

クライアントからも切断する

クライアントも、SEQ=112、ACK=224 で FIN/ACK を送ります。サーバは FIN を受け取ったので、SEQ=224、ACK=113(112+1)を返し、TCP 接続が終わります。

すべてのセグメントの一覧

長さは、セグメントに含まれるデータのバイト数です。

No.送信元種類SEQACK長さ番号の根拠
1クライアントSYN0—0初期値
2サーバSYN/ACK010ACK=0+1(SYN の分)
3クライアントACK110SEQ=0+1(SYN の分)、ACK=0+1
4クライアントデータ11111No.3 はデータがないので SEQ は同じ
5サーバデータ1112222ACK=1+111
6クライアントACK1122230SEQ=1+111、ACK=1+222
7サーバFIN/ACK2231120SEQ=1+222
8クライアントACK1122240ACK=223+1(FIN の分)
9クライアントFIN/ACK1122240No.8 はデータがないので SEQ は同じ
10サーバACK2241130SEQ=223+1、ACK=112+1(FIN の分)
実際に体験してみる

SEQ と ACK の動きは、文章だけではつかみにくいところです。TCP SEQ/ACK Lab — シーケンス番号とACK番号の動きを触る では、ボタンを順に押すと、3-way handshake からデータ送信、切断までの SEQ と ACK の変化を1ステップずつ追えます。tcpdump 風の出力も一緒に表示されるので、実際のパケットキャプチャを読むときの練習にもなります。

まとめ

ポイント内容
シーケンス番号送るデータの先頭バイトの番号。送ったバイト数だけ増える
ACK 番号次に受け取りたいバイトの番号。受け取った SEQ + データのバイト数
SYN と FINデータを持たないが、シーケンス番号を1つ使う
データのない ACKシーケンス番号を使わない(次のセグメントも同じ SEQ)
番号の向きクライアント側とサーバ側で別々に数える

TCP の解析では、Wireshark などのパケットキャプチャソフトをよく使います。Wireshark で TCP を解析するときによく使う表示フィルタや、再送(Retransmission)や重複 ACK(Dup ACK)の見方は、次の記事を参考にしてください。

コメント