- Authors

- Name
- Youngju Kim
- @fjvbn20031
本記事は James Kurose, Keith Ross 著 Computer Networking: A Top-Down Approach (6th Edition) の教科書を基にまとめた内容です。
- 1. ソケットプログラミングの概要
- 2. UDPソケットプログラミング
- 3. TCPソケットプログラミング
- 4. UDP vs TCPソケットプログラミングの比較
- 5. マルチスレッドTCPサーバー
- 6. まとめ
- 7. 確認問題
1. ソケットプログラミングの概要
ネットワークアプリケーションは2つのプロセス(クライアント、サーバー)間の通信で構成される。この通信の接点が**ソケット(socket)**である。
┌──────────────┐ ┌──────────────┐
│ アプリケーション│ │ アプリケーション│
│ プロセス │ │ プロセス │
│ │ │ │ │ │
│ ソケット(ドア)│ │ ソケット(ドア)│
├──────┼────────┤ インターネット ├──────┼────────┤
│ トランスポート層│ ←═════════════> │ トランスポート層│
└──────────────┘ └──────────────┘
ソケットはアプリケーション層とトランスポート層の間の**インタフェース(API)**である。
1.1 2つのトランスポートサービス
| サービス | TCP | UDP |
|---|---|---|
| 接続 | コネクション指向 | コネクションレス |
| 信頼性 | 信頼性あり(順序保証、再送) | 信頼性なし(損失あり) |
| フロー制御 | あり | なし |
| 輻輳制御 | あり | なし |
| オーバーヘッド | 高い | 低い |
2. UDPソケットプログラミング
2.1 UDP通信の特徴
- 接続設定なし:データ送信前にハンドシェイクが不要
- 各データグラムに宛先IPとポート番号を明示的に添付
- パケット順序保証なし、パケット損失の可能性あり
2.2 UDPクライアント-サーバー相互作用フロー
サーバー クライアント
──── ────────
ソケット作成 ソケット作成
(serverSocket) (clientSocket)
│ │
アドレスバインド │
(IP, ポート) │
│ │
受信待機 ←──── データグラム送信 ──── メッセージ送信
(recvfrom) (宛先IP:ポート) (sendto)
│ │
データ処理 受信待機
│ (recvfrom)
応答送信 ────── データグラム ──────→ 応答受信
(sendto) │
│ ソケット閉じ
2.3 UDPサーバーコード(Python)
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
print("UDP server ready. Port:", serverPort)
while True:
# Receive message from client
message, clientAddress = serverSocket.recvfrom(2048)
print(f"Received: {message.decode()} from {clientAddress}")
# Convert to uppercase and respond
modifiedMessage = message.decode().upper()
serverSocket.sendto(modifiedMessage.encode(), clientAddress)
コード解説
socket(AF_INET, SOCK_DGRAM)
AF_INET: IPv4
SOCK_DGRAM: UDPソケット
bind(('', serverPort))
'': すべてのインタフェースで受信
serverPort: バインドするポート番号
recvfrom(2048)
最大2048バイト受信
返り値: (データ, クライアントアドレス)
sendto(data, address)
データを指定されたアドレスに送信
2.4 UDPクライアントコード(Python)
from socket import *
serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = input("Enter a lowercase sentence: ")
clientSocket.sendto(message.encode(), (serverName, serverPort))
# Receive server response
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print("Server response:", modifiedMessage.decode())
clientSocket.close()
実行例
# Server terminal
$ python UDPServer.py
UDP server ready. Port: 12000
Received: hello world from ('127.0.0.1', 54321)
# Client terminal
$ python UDPClient.py
Enter a lowercase sentence: hello world
Server response: HELLO WORLD
3. TCPソケットプログラミング
3.1 TCP通信の特徴
- コネクション指向:データ転送前に必ずTCP接続を確立
- ウェルカム(welcome)ソケットと接続ソケットの区別
- バイトストリームベース(メッセージ境界なし)
- 信頼性のある転送:順序保証、再送
3.2 TCPクライアント-サーバー相互作用フロー
サーバー クライアント
──── ────────
ウェルカムソケット作成 │
(serverSocket) │
│ │
アドレスバインド │
listen() │
│ ソケット作成
accept() 待機 ←── TCP接続要求 ── connect()
│ (3-way handshake) │
接続ソケット作成 │
(connectionSocket) │
│ │
recv() ←──────── データ ──────── send()
│ │
データ処理 │
│ │
send() ────────── 応答 ──────────→ recv()
│ │
接続ソケット閉じ ソケット閉じ
(connectionSocket.close()) │
│
次の接続待機...
核心:2種類のソケット
サーバー側のソケット:
1. ウェルカムソケット(Welcome/Listening Socket)
- serverSocket:クライアントの接続要求を受け付けるドア
- accept()呼び出し時に待機
2. 接続ソケット(Connection Socket)
- connectionSocket:特定クライアントとの専用ソケット
- accept()が返す新しいソケット
- データ送受信に使用
3.3 TCPサーバーコード(Python)
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(1)
print("TCP server ready. Port:", serverPort)
while True:
# Accept client connection (blocking)
connectionSocket, clientAddr = serverSocket.accept()
print(f"Connection established: {clientAddr}")
# Receive data
message = connectionSocket.recv(1024).decode()
print(f"Received: {message}")
# Convert to uppercase and respond
capitalizedMessage = message.upper()
connectionSocket.send(capitalizedMessage.encode())
# Close connection socket (welcome socket remains open)
connectionSocket.close()
コード解説
socket(AF_INET, SOCK_STREAM)
SOCK_STREAM: TCPソケット
listen(1)
最大1つの待機接続を許可
(バックログキューサイズ)
accept()
接続要求が来るまでブロッキング
返り値: (新しい接続ソケット, クライアントアドレス)
recv(1024)
最大1024バイト受信
TCPはバイトストリームのため境界なし
send(data)
接続されたソケットでデータ送信
宛先アドレス指定不要(すでに接続済み)
3.4 TCPクライアントコード(Python)
from socket import *
serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)
# Establish TCP connection (3-way handshake)
clientSocket.connect((serverName, serverPort))
message = input("Enter a lowercase sentence: ")
clientSocket.send(message.encode())
# Receive server response
modifiedMessage = clientSocket.recv(1024)
print("Server response:", modifiedMessage.decode())
clientSocket.close()
実行例
# Server terminal
$ python TCPServer.py
TCP server ready. Port: 12000
Connection established: ('127.0.0.1', 54322)
Received: hello tcp world
# Client terminal
$ python TCPClient.py
Enter a lowercase sentence: hello tcp world
Server response: HELLO TCP WORLD
4. UDP vs TCPソケットプログラミングの比較
4.1 コードレベルの違い
| 項目 | UDP | TCP |
|---|---|---|
| ソケットタイプ | SOCK_DGRAM | SOCK_STREAM |
| 接続設定 | なし | connect() / accept() |
| データ送信 | sendto(data, addr) | send(data) |
| データ受信 | recvfrom(bufsize) | recv(bufsize) |
| アドレス指定 | 毎回 | 接続時に1回だけ |
| サーバーソケット | 1つのみ | ウェルカム + 接続ソケット |
4.2 フロー比較ダイアグラム
UDP: TCP:
──── ────
Server: socket() Server: socket()
Server: bind() Server: bind()
Server: listen()
Client: socket() Server: accept() (blocking)
Client: sendto() ──────────>
Client: socket()
Server: recvfrom() Client: connect() ──────>
Server: sendto() ──────────> 3-way handshake
Server: (new connection socket returned)
Client: recvfrom()
Client: close() Client: send() ──────────>
Server: recv()
Server: send() ──────────>
Client: recv()
Client: close()
Server: close() (connection socket)
5. マルチスレッドTCPサーバー
基本的なTCPサーバーは一度に1つのクライアントしか処理できない。複数のクライアントを同時に処理するにはマルチスレッドを使用する。
from socket import *
import threading
def handle_client(connectionSocket, addr):
"""Thread function to handle each client"""
print(f"Thread started: {addr}")
try:
message = connectionSocket.recv(1024).decode()
response = message.upper()
connectionSocket.send(response.encode())
finally:
connectionSocket.close()
print(f"Thread ended: {addr}")
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(5)
print("Multi-threaded TCP server ready")
while True:
connectionSocket, addr = serverSocket.accept()
# Handle client in a new thread
thread = threading.Thread(
target=handle_client,
args=(connectionSocket, addr)
)
thread.start()
マルチスレッドサーバーの動作:
クライアント1 ──接続──> [ウェルカムソケット] ──accept()──> [接続ソケット1] ──Thread 1
クライアント2 ──接続──> [ウェルカムソケット] ──accept()──> [接続ソケット2] ──Thread 2
クライアント3 ──接続──> [ウェルカムソケット] ──accept()──> [接続ソケット3] ──Thread 3
各クライアントが独立したスレッドで処理される
6. まとめ
ソケットプログラミングの核心要約:
UDPソケット:
├── コネクションレス、データグラム単位
├── sendto/recvfrom(毎回アドレス指定)
├── 高速だが信頼性なし
└── 用途:DNS、ストリーミング、ゲーム
TCPソケット:
├── コネクション指向、バイトストリーム
├── connect/acceptで接続確立
├── send/recv(アドレス指定不要)
├── 信頼性あるがオーバーヘッドあり
└── 用途:Web、メール、ファイル転送
7. 確認問題
Q1. UDPソケットでsendto()を使用する理由は?
UDPはコネクションレスプロトコルであるため、接続が事前に確立されない。したがって、各データグラムを送信するたびに宛先IPアドレスとポート番号を明示しなければならない。sendto(data, (ip, port)) の形式で宛先を指定する。一方TCPは connect() ですでに接続が確立されているため send(data) だけで十分である。
Q2. TCPサーバーにおけるウェルカムソケットと接続ソケットの違いは?
- ウェルカムソケット(serverSocket):クライアントの接続要求を受け入れるソケット。
listen()とaccept()に使用。サーバーが生きている限り維持される。 - 接続ソケット(connectionSocket):
accept()が返す新しいソケット。特定のクライアントとのデータ送受信に使用。通信が終わったら閉じる。
この分離により、サーバーが複数のクライアントと同時に通信できる。
Q3. TCPがバイトストリームであることがプログラミングにどのような影響を与えるか?
TCPはメッセージ境界を維持しない。send() で100バイトを送っても、recv() が一度に100バイトを受け取る保証はない。50バイトずつ2回に分けて受け取る可能性もある。したがって、アプリケーションでメッセージ境界を直接処理しなければならない(例:区切り文字の使用、長さヘッダの追加など)。