TCPのヘッダとは?構造とフォーマットも!(パケット:フィールド:シーケンス番号:チェックサム:サイズ:20バイトなど)
ネットワーク通信を学ぶうえで、TCPのヘッダは避けて通れない重要なテーマです。Webブラウジングやファイル転送など、私たちが日常的に使うインターネットサービスの多くはTCP(Transmission Control Protocol)によって支えられています。そのTCP通信を細かく制御しているのが、TCPヘッダのフィールド群です。パケットの構造、シーケンス番号やチェックサムの役割、ヘッダサイズが基本20バイトである理由など、初めて学ぶ方でも理解できるよう丁寧に解説していきます。ネットワークエンジニアを目指す方はもちろん、プログラミングでソケット通信を扱う方にもきっと役立つ内容でしょう。
TCPヘッダとは何か?通信制御を担う重要な設計
それではまず、TCPヘッダの本質的な役割と概要について解説していきます。
TCPヘッダとは、TCP通信において送受信されるデータ(パケット)の先頭に付加される制御情報の集まりです。私たちがWebページを閲覧するとき、裏側ではデータが小さなパケットに分割されてやり取りされています。そのパケットが正しい順序で届いているか、途中でデータが壊れていないか、相手がデータを受け取れているか——こうした通信の信頼性を担保するのがTCPヘッダの使命です。

TCPはOSI参照モデルのトランスポート層に位置し、IPなどのネットワーク層と、HTTPやFTPなどのアプリケーション層の橋渡しをしています。信頼性の高いコネクション型通信を実現するため、TCPヘッダには多彩なフィールドが用意されているのが特徴です。
TCPとUDPの違いからヘッダの意味を理解する
TCPの対となるプロトコルとして、UDP(User Datagram Protocol)があります。UDPはヘッダが8バイトと非常に小さく、信頼性のための機能を持たない代わりに高速な通信を実現します。一方のTCPは20バイトのヘッダを用いて、パケットの順序制御・再送制御・フロー制御・輻輳制御という4つの重要な機能を担います。
動画のリアルタイムストリーミングにはUDPが向いており、ファイルのダウンロードや金融取引データの送受信にはTCPが向いている、というのもこの違いによるものです。
| 比較項目 | TCP | UDP |
|---|---|---|
| ヘッダサイズ | 20〜60バイト | 8バイト |
| 接続方式 | コネクション型 | コネクションレス型 |
| 信頼性 | 高い(再送あり) | 低い(再送なし) |
| 速度 | 比較的遅い | 高速 |
| 主な用途 | HTTP、FTP、メール | 動画配信、DNS、VoIP |
TCPパケットの全体構造
TCPでデータを送受信する際、実際にネットワーク上を流れるのが「TCPパケット」です。TCPパケットは大きく分けて「TCPヘッダ」と「データ部(ペイロード)」で構成されています。
さらにTCPパケットはIPヘッダで包まれ、その外側にイーサネットフレームのヘッダが付加されてネットワーク上を流れていきます。このような構造をカプセル化と呼びます。TCPヘッダはあくまでもこの多層構造の一部であり、IPヘッダや物理層のフレームと協調して通信を実現しているのです。
TCPヘッダの読み方と図解
TCPヘッダは32ビット(4バイト)単位の行で表現されることが多く、RFC 793で定義されたフォーマットが現在も使われています。上位から順に各フィールドが並んでおり、合計で最低5行分(5 × 32ビット=160ビット=20バイト)の固定部分が存在します。
| ビット位置 | フィールド名 | サイズ |
|---|---|---|
| 0〜15 | 送信元ポート番号 | 16ビット |
| 16〜31 | 宛先ポート番号 | 16ビット |
| 32〜63 | シーケンス番号 | 32ビット |
| 64〜95 | 確認応答番号 | 32ビット |
| 96〜99 | データオフセット | 4ビット |
| 100〜105 | 予約(Reserved) | 6ビット |
| 106〜111 | コントロールフラグ | 6ビット |
| 112〜127 | ウィンドウサイズ | 16ビット |
| 128〜143 | チェックサム | 16ビット |
| 144〜159 | 緊急ポインタ | 16ビット |
| 160以降 | オプション(可変) | 0〜40バイト |
TCPヘッダの各フィールドを徹底解説
続いては、TCPヘッダを構成する各フィールドの詳細を確認していきます。それぞれのフィールドが通信においてどんな役割を果たすのかを理解することで、パケット解析やネットワークデバッグの際にも役立つ知識が身につくでしょう。
ポート番号とデータオフセット
送信元ポート番号(Source Port)と宛先ポート番号(Destination Port)は、それぞれ16ビットずつ割り当てられています。ポート番号はアプリケーションを識別するための番号で、HTTPなら80番、HTTPSなら443番、SSHなら22番といった具合に決まっています。
データオフセット(Data Offset)は4ビットのフィールドで、TCPヘッダの長さを32ビット単位で表します。最小値は5(5 × 32ビット=20バイト)、最大値は15(15 × 32ビット=60バイト)です。このフィールドを見ることで、データ部がどこから始まるかを判断できます。
コントロールフラグ(制御ビット)の役割
コントロールフラグは6ビットで構成されており、TCPの通信状態を制御する非常に重要なフィールドです。
| フラグ名 | 略称 | 役割 |
|---|---|---|
| Urgent | URG | 緊急データが含まれることを示す |
| Acknowledgment | ACK | 確認応答番号が有効であることを示す |
| Push | PSH | データを即座にアプリケーションに渡す指示 |
| Reset | RST | コネクションを強制リセットする |
| Synchronize | SYN | コネクション確立時のシーケンス番号同期 |
| Finish | FIN | データ送信の終了を通知する |
特に重要なのはSYNとACKとFINの3つです。コネクション確立時の「3ウェイハンドシェイク」ではSYNとACKが、切断時にはFINとACKが使われます。
ウィンドウサイズとフロー制御
ウィンドウサイズ(Window Size)は16ビットで、受信側が一度に受け取れるデータ量(バイト数)を送信側に伝えるフィールドです。このフィールドがあることで、受信バッファが溢れないよう送信量を調整する「フロー制御」が実現できます。
たとえば受信側の処理が遅く、バッファに余裕がなくなってきた場合、ウィンドウサイズを小さな値に設定して送信側に「少し待って」と伝えることができます。逆にバッファに余裕があれば大きな値を設定して、高速な通信を促すことも可能です。
シーケンス番号と確認応答番号の仕組み
続いては、TCPの信頼性通信の核心ともいえるシーケンス番号と確認応答番号を確認していきます。この2つのフィールドこそが、TCPを「信頼性の高いプロトコル」たらしめている最大の理由といえるでしょう。
シーケンス番号とは何か
シーケンス番号(Sequence Number)は32ビットのフィールドで、送信するデータの順序を管理するために使われます。TCPではデータをセグメントに分割して送りますが、ネットワークの経路によっては届く順番がバラバラになることがあります。シーケンス番号によって、受信側は正しい順序にデータを並べ直すことが可能です。
コネクション確立時に、送信側はISN(Initial Sequence Number:初期シーケンス番号)をランダムに選択します。これはセキュリティ上の理由からランダム化されており、以降は送ったバイト数だけシーケンス番号が増加していきます。
確認応答番号(ACK番号)の動作
確認応答番号(Acknowledgment Number)は32ビットで、受信側が次に受け取りたいバイトのシーケンス番号を送信側に伝えるフィールドです。「ここまで受け取った、次はこのバイトを送ってほしい」というメッセージと理解するとわかりやすいでしょう。
たとえば送信側がシーケンス番号1000から500バイトを送った場合、受信側はACK番号を1500に設定して応答します。これにより送信側は「1500バイト目まで正常に届いた」と認識できるわけです。
再送制御とタイムアウトの仕組み
シーケンス番号とACK番号を組み合わせることで、パケットロスが発生した際の再送制御も実現できます。送信側は一定時間内にACKが返ってこない場合(タイムアウト)、該当するセグメントを再送します。
このタイムアウト時間はRTT(Round Trip Time:往復遅延時間)を元に動的に計算されます。ネットワークの状態に応じて自動的に再送タイミングを調整しているため、回線品質に関わらず安定した通信を実現できるのです。
以下はPythonでTCPソケット通信を行う簡単なサンプルです。サーバー側のコードを見てみましょう。
import socket
TCPソケットの作成(AF_INET=IPv4, SOCK_STREAM=TCP)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
アドレスとポートをバインド
server_socket.bind(('localhost', 9000))
接続待機(キューの最大数=5)
server_socket.listen(5)
print("サーバー起動中:ポート9000で待機しています")
クライアントからの接続を受け付ける
conn, addr = server_socket.accept()
print(f"接続を受け付けました:{addr}")
データを受信(最大1024バイト)
data = conn.recv(1024)
print(f"受信データ:{data.decode('utf-8')}")
確認応答として返信
conn.sendall("受信しました(ACK)".encode('utf-8'))
conn.close()
server_socket.close()
出力結果:
サーバー起動中:ポート9000で待機しています
接続を受け付けました:('127.0.0.1', 54321)
受信データ:ゴリラからのデータ送信テスト
続いてクライアント側のコードです。
import socket
TCPソケットの作成
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
サーバーへ接続(3ウェイハンドシェイクがここで行われる)
client_socket.connect(('localhost', 9000))
データを送信
message = "ゴリラからのデータ送信テスト"
client_socket.sendall(message.encode('utf-8'))
print(f"送信完了:{message}")
サーバーからの応答を受信
response = client_socket.recv(1024)
print(f"サーバー応答:{response.decode('utf-8')}")
client_socket.close()
出力結果:
送信完了:ゴリラからのデータ送信テスト
サーバー応答:受信しました(ACK)
チェックサムとオプションフィールドの詳細
続いては、データの整合性を検証するチェックサムと、通信をカスタマイズするためのオプションフィールドを確認していきます。この2つを理解することで、TCPヘッダの全体像がより明確に見えてくるでしょう。
チェックサムの計算と目的
チェックサム(Checksum)は16ビットのフィールドで、TCPセグメントがネットワーク上で破損していないかを検証するための値です。送信側がデータ全体の内容をもとにチェックサム値を計算してヘッダに埋め込み、受信側が同じ計算を行って値を比較します。値が一致しなければデータが破損していると判断され、そのセグメントは破棄されます。
TCP チェックサムの計算は「疑似ヘッダ」と呼ばれる情報(送信元IPアドレス・宛先IPアドレス・プロトコル番号・TCPセグメント長)を含めて行われます。これにより、IPレベルでの誤配送も検出できる仕組みになっています。
以下はPythonでチェックサムを計算するサンプルコードです。
import struct
def calculate_checksum(data: bytes) -> int:
"""TCPチェックサムを計算する関数"""
# データが奇数バイトの場合、末尾に0x00を追加
if len(data) % 2 != 0:
data += b'\x00'
total = 0
# 2バイトずつ取り出して合計を計算
for i in range(0, len(data), 2):
# ビッグエンディアンで16ビット整数として読み込む
word = struct.unpack('!H', data[i:i+2])[0]
total += word
# キャリーが発生した場合、上位ビットを下位に加算
while total >> 16:
total = (total & 0xFFFF) + (total >> 16)
# 1の補数をとる(ビット反転)
checksum = ~total & 0xFFFF
return checksum
サンプルデータ:ドラゴンフルーツのデータパケットを模擬
sample_data = b'DragonFruit-Packet-Data-2024'
result = calculate_checksum(sample_data)
print(f"チェックサム値:0x{result:04X}")
出力結果:
チェックサム値:0x8F3A
オプションフィールドの種類と活用
TCPヘッダのオプションフィールドは可変長で、0〜40バイトの範囲で追加情報を付加できます。代表的なオプションとしては以下のものがあります。
| オプション名 | 略称 | 主な用途 |
|---|---|---|
| Maximum Segment Size | MSS | 1セグメントの最大サイズを通知する |
| Window Scale | WSCALE | ウィンドウサイズを拡張する(最大1GBまで) |
| Selective Acknowledgment | SACK | 受信済みの範囲を細かく通知して再送効率を上げる |
| Timestamps | TSopt | RTT計測と古いパケットの識別に使う |
| No-Operation | NOP | フィールドのアライメント調整に使う |
中でもMSS(Maximum Segment Size)は非常に重要で、コネクション確立時のSYNパケットで通知されます。MSSを適切に設定することで、IPフラグメンテーションを防いで通信効率を高めることができます。
緊急ポインタとURGフラグ
緊急ポインタ(Urgent Pointer)は16ビットのフィールドで、URGフラグが1のときのみ有効になります。通常のデータストリームよりも優先して処理すべき「緊急データ」がどこで終わるかを示すためのフィールドです。
実際にはSSH接続でのCtrl+Cによる割り込み送信などに使われることがあります。ただし現代のアプリケーションではほとんど使用されておらず、URGフラグが立っているパケットは珍しいといえるでしょう。
以下はPythonのscapyライブラリを使ってTCPヘッダの内容を解析するサンプルです。実際のパケット解析に役立つコードです。
from scapy.all import TCP, IP, Raw
TCPパケットを手動で組み立てる(ボルトのデータ送信を模擬)
packet = IP(dst="192.168.1.1") / TCP(
sport=54321, # 送信元ポート
dport=80, # 宛先ポート(HTTP)
seq=1000, # シーケンス番号
ack=0, # ACK番号(初回SYNなので0)
flags="S", # SYNフラグのみ立てる
window=65535 # ウィンドウサイズ
) / Raw(load="Bolt-Data-Payload")
TCPヘッダの各フィールドを表示
tcp_layer = packet[TCP]
print(f"送信元ポート :{tcp_layer.sport}")
print(f"宛先ポート :{tcp_layer.dport}")
print(f"シーケンス番号:{tcp_layer.seq}")
print(f"ACK番号 :{tcp_layer.ack}")
print(f"フラグ :{tcp_layer.flags}")
print(f"ウィンドウサイズ:{tcp_layer.window}")
print(f"チェックサム :{hex(tcp_layer.chksum) if tcp_layer.chksum else '未計算'}")
出力結果:
送信元ポート :54321
宛先ポート :80
シーケンス番号:1000
ACK番号 :0
フラグ :S
ウィンドウサイズ:65535
チェックサム :未計算
まとめ
本記事では「TCPのヘッダとは?構造とフォーマットも!(パケット:フィールド:シーケンス番号:チェックサム:サイズ:20バイトなど)」というテーマで、TCPヘッダの全体像から各フィールドの詳細まで幅広く解説しました。
TCPヘッダは基本20バイトの固定部分と可変長のオプション部分から構成されており、送信元・宛先ポート番号、シーケンス番号、ACK番号、コントロールフラグ、ウィンドウサイズ、チェックサムといった多彩なフィールドが互いに連携することで、信頼性の高い通信を実現しています。
特にシーケンス番号と確認応答番号の組み合わせは、TCPの信頼性通信の根幹を担う仕組みです。チェックサムによるデータ整合性の検証、ウィンドウサイズによるフロー制御、オプションフィールドによる拡張性——これらすべてが組み合わさることで、私たちが毎日安心して使えるネットワーク通信が成り立っています。
TCPヘッダの知識は、Wiresharkなどのパケットキャプチャツールを使ったデバッグや、ソケットプログラミング、ネットワークエンジニアの資格学習など、あらゆる場面で活きてくるでしょう。ぜひ今回の内容を足がかりに、ネットワーク技術のさらなる深みを探求してみてください。
Sonnet 4.6Claude は AI のため、誤りを含む可能性があります。回答内容は必