はじめに
以前、Pythonでサーバーを自作していたときに、こんな疑問が浮かびました。
「recv(1024)って指定しても、TCPだと全部届かないときあるのに、UDPは一発で届く…なんで?」
それまではなんとなく「TCPは信頼性があってUDPは速いけど雑」くらいの認識だったけど、このリアルな違和感から調べた結果、ソケットの仕組みやTCP/UDPの本質がストンと腹落ちしました。
この記事では、そのときの疑問→調査→理解をベースに、ソケット通信の本質とプロトコルの違いを整理します。
以下は、Pythonで開発した TCP/UDP ソケットを用いて独自のサーバーとプロトコルを実装したシンプルなチャットメッセンジャーアプリです。
https://github.com/ga-techcraft/Online-Chat-Messenger
そもそもソケット通信ってどこに保存されてるの?
パイプの理解をベースにすると
- パイプはカーネルのリングバッファ(メモリ)に一時的に保存
- アプリはファイルディスクリプタを通じてバッファに読み書き
ソケットも同じ
ソケットも、カーネルの送信バッファ・受信バッファに保存されている!
アプリ → write() → カーネルの送信バッファ → ネットワークへ
アプリ ← read() ← カーネルの受信バッファ ← ネットワークから
TCPやUDPは、その送受信の過程でデータの整形や再構築をしている
TCPとUDPの違いって何?
特徴 | TCP | UDP |
---|---|---|
データ構造 | バイトストリーム〜流れる水 | メッセージ〜1通ずつ |
分割・再構築 | プロトコルが自動で分割 / 復元 | 1パケット = 1通 |
境界 | なし(アプリが分ける必要あり) | あり(1送信 = 1受信) |
信頼性 | 高い〜再送、順序保証あり | なし(失われることも) |
recv(1024)
で感じた違和感と答え
TCP の場合
conn.sendall(b"HELLO")
conn.sendall(b"WORLD")
data = conn.recv(1024)
→ data == b"HELLOWORLD"
になることもあるし,分割されてることもある
理由:TCPは流れだから。どこまでが1通かは自分で判断する必要がある
UDP の場合
sock.sendto(b"HELLO", addr)
sock.sendto(b"WORLD", addr)
data, _ = sock.recvfrom(1024)
→ data == b"HELLO"
(2回目のrecvfrom()
でb"WORLD"
が来る)
理由:UDPは「メッセージ」だから。送信 = 受信が保証されている
まとめ
ポイント | 理解 |
---|---|
保存元 | カーネルの送受信バッファ(RAM上) |
TCP | 流れのデータ。区切りなし。自分で分ける必要あり |
UDP | 送信 = 受信の1通単体。一発で取得。 |