Pythonは、ネットワークプログラミングを行うためのsocketモジュールを提供しています。この記事では、socketモジュールを使用してTCP/IPおよびUDPソケットを介した基本的なクライアント/サーバー通信を実装する方法を、具体的なコード例とともに解説します。
ソケットとは
ソケットは、ネットワーク通信のエンドポイントです。プロセス間通信(IPC)の一種で、異なるコンピュータ上のプロセス間、または同一コンピュータ内の異なるプロセス間でデータを送受信するために使用されます。ソケットには主に以下の2つのタイプがあります。
- TCP (Transmission Control Protocol): 信頼性の高い、接続指向のプロトコルです。データの順序と到達性が保証されます。Webブラウジング、メール送信など、データの正確性が重要なアプリケーションで使用されます。
- UDP (User Datagram Protocol): 信頼性の低い、コネクションレス型のプロトコルです。データの順序や到達性は保証されませんが、TCPに比べてオーバーヘッドが少なく、高速な通信が可能です。オンラインゲーム、ビデオストリーミングなど、多少のデータ損失が許容されるリアルタイムアプリケーションで使用されます。
TCPソケットプログラミング
TCPソケットは、信頼性の高い双方向通信を提供します。以下の例では、PythonでシンプルなTCPサーバーとクライアントを作成する方法を示します。
TCPサーバーの実装
TCPサーバーを実装し、クライアントからの接続を待ち受け、受信したデータをそのままエコーバック(送り返す)します。
import socket HOST = '127.0.0.1' # ローカルホスト PORT = 65432 # 使用されていないポート番号 # AF_INET: IPv4, SOCK_STREAM: TCP with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # ポートを再利用可能にする (TIME_WAIT状態のポートをすぐに再利用) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((HOST, PORT)) # ソケットにアドレスをバインド except OSError as e: print(f"Error binding to port {PORT}: {e}") exit(1) s.listen() # クライアントからの接続を待機 print(f"Listening on {HOST}:{PORT}") conn, addr = s.accept() # クライアントからの接続を受け入れる with conn: print('Connected by', addr) while True: try: data = conn.recv(1024) # クライアントからデータを受信 if not data: break # データがなければ接続終了 print(f"Received: {data.decode()}") conn.sendall(data) # 受信したデータをそのままクライアントに送り返す (エコー) except ConnectionResetError: print("Client disconnected unexpectedly") break except Exception as e: print(f"An error occurred: {e}") break
クライアントからの接続を待ち受けるため、socket.socket(socket.AF_INET, socket.SOCK_STREAM)でTCPソケットを作成します。s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)は、サーバー停止後すぐにポートを再利用できるようにするための設定です。
s.bind((HOST, PORT))でソケットを特定のアドレスとポートにバインドし、s.listen()で接続要求の受付を開始します。s.accept()でクライアントからの接続を確立し、新しいソケットオブジェクト(conn)を取得します。これで、conn.recv(1024)でクライアントからデータを受信し、conn.sendall(data)でそのデータをそのままクライアントに送り返すエコーサーバーとして動作します。
TCPクライアントの実装
上記のTCPサーバーに接続し、文字列を送信して、サーバーからの応答(エコー)を受信・表示します。
import socket HOST = '127.0.0.1' # サーバーのホスト名またはIPアドレス PORT = 65432 # サーバーが使用しているポート番号 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: try: s.connect((HOST, PORT)) # サーバーに接続 except ConnectionRefusedError: print(f"Connection refused. Ensure the server is running on {HOST}:{PORT}.") exit(1) s.sendall(b'Hello, server') # サーバーにデータを送信 data = s.recv(1024) # サーバーからデータを受信 print('Received', repr(data.decode()))
socket.socket(socket.AF_INET, socket.SOCK_STREAM)でTCPソケットを作成し、s.connect((HOST, PORT))でサーバーに接続を試みます。接続後、s.sendall(b'Hello, server')でサーバーにデータを送信し、s.recv(1024)でサーバーからの応答を受信、表示します。
UDPソケットプログラミング
UDPはコネクションレス型プロトコルです。TCPに比べて軽量ですが、信頼性は保証されません。
UDPサーバーの実装
UDPサーバーを実装し、クライアントからデータを受信し、受信したデータを大文字に変換してクライアントに送り返します。
import socket HOST = '127.0.0.1' PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((HOST, PORT)) except OSError as e: print(f"Error binding to port {PORT}: {e}") exit(1) print(f"Listening on {HOST}:{PORT}") while True: try: data, addr = s.recvfrom(1024) # クライアントからデータとアドレスを受信 print(f"Received: {data.decode()} from {addr}") s.sendto(data.upper(), addr) # 受信データを大文字にしてクライアントに送り返す except Exception as e: print(f"An error occurred: {e}") break
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)でUDPソケットを作成し、s.bind((HOST, PORT))で指定のIPアドレスとポートにバインドします。UDPはコネクションレス型なので、TCPのlisten()やaccept()は不要です。
s.recvfrom(1024)でクライアントからのデータと送信元アドレスを受信し、s.sendto(data.upper(), addr)で受信データを大文字に変換して送信元に返します。
UDPクライアントの実装
上記のUDPサーバーにデータを送信し、サーバーからの応答(大文字に変換されたデータ)を受信・表示します。
import socket HOST = '127.0.0.1' PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: try: s.sendto(b'Hello, server', (HOST, PORT)) # サーバーにデータを送信 data, addr = s.recvfrom(1024) # サーバーからデータとアドレスを受信 print(f"Received: {data.decode()} from {addr}") except Exception as e: print(f"An error occurred: {e}")
socket.socket(socket.AF_INET, socket.SOCK_DGRAM) でUDPソケットを作成し、s.sendto(b'Hello, server', (HOST, PORT))でサーバーにデータを送信します。その後、s.recvfrom(1024)でサーバーからの応答と送信元アドレス(ここではサーバーのアドレス)を受信し、表示します。
応用: ノンブロッキングソケット
上記の例では、ソケット操作(recv, send, acceptなど)はブロッキングモードで行われます。つまり、これらの操作は完了するまでプログラムの実行をブロックします。大規模なアプリケーションでは、これはパフォーマンス上の問題を引き起こす可能性があります。
ノンブロッキングソケットを使用すると、ソケット操作がすぐに完了しない場合でも、プログラムの実行をブロックせずに他の処理を続行できます。ソケットをノンブロッキングモードに設定するには、setblocking(False)を呼び出します。ノンブロッキングソケットを使用する場合は、selectモジュールやasyncioライブラリなどを組み合わせて、ソケットの準備状態を効率的に監視する必要があります。実際には下記のように実装します。
# 例:ノンブロッキングソケットの設定 (TCPサーバーの一部) s.setblocking(False)
ネットワーク自動化とプログラマビリティ 次世代ネットワークエンジニアのためのスキルセット [ Jason Edelman ]
まとめ
この記事では、Pythonのsocketモジュールを使用してTCPおよびUDPソケットによる基本的なネットワーク通信を実装する方法を解説しました。ソケットプログラミングは、ネットワークに詳しい開発者にはなじみのあるテクノロジーですが、Pythonを使うことで、初心者でも簡単にネットワークプログラミングを始めることができます。これらのプログラムはPythonの基本的な文法とライブラリで実装することができます。Pythonの基礎学習には下記のようなサイトの利用が有効です。
[PR]