Skip to content

필사 모드: [コンピュータネットワーク] 07. ソケットプログラミング:TCPとUDP

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

本記事は James Kurose, Keith Ross 著 Computer Networking: A Top-Down Approach (6th Edition) の教科書を基にまとめた内容です。

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 *

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. 確認問題

UDPはコネクションレスプロトコルであるため、接続が事前に確立されない。したがって、各データグラムを送信するたびに**宛先IPアドレスとポート番号**を明示しなければならない。`sendto(data, (ip, port))` の形式で宛先を指定する。一方TCPは `connect()` ですでに接続が確立されているため `send(data)` だけで十分である。

- **ウェルカムソケット(serverSocket)**:クライアントの接続要求を受け入れるソケット。`listen()` と `accept()` に使用。サーバーが生きている限り維持される。

- **接続ソケット(connectionSocket)**:`accept()` が返す新しいソケット。特定のクライアントとのデータ送受信に使用。通信が終わったら閉じる。

この分離により、サーバーが複数のクライアントと同時に通信できる。

TCPはメッセージ境界を維持しない。`send()` で100バイトを送っても、`recv()` が一度に100バイトを受け取る保証はない。50バイトずつ2回に分けて受け取る可能性もある。したがって、アプリケーションで**メッセージ境界を直接処理**しなければならない(例:区切り文字の使用、長さヘッダの追加など)。

현재 단락 (1/225)

本記事は James Kurose, Keith Ross 著 Computer Networking: A Top-Down Approach (6th Edition) の教科書を基にまとめた内容...

작성 글자: 0원문 글자: 6,891작성 단락: 0/225