Pythonでsocketモジュールでサーバーとクライアント間の通信をする基本的な使い方を解説します。
Contents
ソケット(socket)通信
ソケット(socket)通信とは、異なるプロセス間でデータをやり取りするための通信方法です。通常、ソケット通信では、TCP (Transmission Control Protocol) または UDP (User Datagram Protocol)といったインターネットの通信プロトコルを使用します。
TCPはハンドシェイクというプロセスにより接続を確立して通信をする信頼性の高いプロトコルで、UDPは、接続を確立せずにデータを送信するため高速ですが信頼性は低下します。要件に応じて適切なプロトコルを選択することが重要です
Pythonでは、ソケット通信のためのモジュールとしてsocketモジュールが提供されています。本記事では、socketモジュールでサーバーとクライアント間の通信をする基本的な使い方を紹介します。
socketモジュールを使った通信の基本
以降で、socketモジュールを利用したソケット通信のためのサーバープログラムおよびクライアントプログラムを紹介します。
通常はサーバーとクライアントは異なる端末間での通信になりますが、今回は自端末(localhost, 127.0.0.1)でサーバープログラムとクライアントプログラムを両方動かすように作成します。ポート番号については、今回は動的・プライベートポートの50000番を使用しますが必要に応じて変更してください。
実際の使用ケースでは、サーバーのIPやポートを適切なものに読み替えて使用してください。また、実用的なアプリケーションでは適切な例外処理が必要ですが、本記事ではsocketの基本的な使い方を説明することを目的とするため、例外処理は除外していますのでご注意ください。
socketを使ったサーバー/クライアント通信
TCPを使用した通信
サーバーとクライアント間のsocketを使用したTCP通信の例を紹介します。
サーバー側プログラム
import socket def tcp_server_program(): host = "127.0.0.1" port = 50000 # ソケット作成 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: # バインド(ソケットにIPとポート番号を紐づけ) server_socket.bind((host, port)) # 同時接続を1つ待機 server_socket.listen(1) while True: # 接続を待機 conn, address = server_socket.accept() with conn: while True: # データを受信 (データの最大バイトを1,024) data = conn.recv(1024) if not data: break print(f"data: {data}, address:{address}") def main(): tcp_server_program() if __name__ == "__main__": main()
上記例では、tcp_server_program
関数がサーバー機能として動作します。
socket.socket
関数を使用してソケットを作成します。引数のsocket.AF_INET
は、IPv4を使用することを意味します。また、socket.SOCK_STREAM
は、プロトコルとしてTCPを使用するための指定です。なお、使用後にソケットが適切に閉じられるようにwith
句を使用しています。
ソケットとIP・ポートを紐づけることをバインドといいます。bind
を使用して「server_socket.bind((host, port))
」の部分でサーバーのIPとポート番号を指定します。また、listen
でシステムが処理を受け入れる前にキューに入れる最大数を指定しています。
クライアントからの接続はaccept
で待機し、接続があるとconn
とaddress
に接続情報が格納されます。conn.recv
でデータを受信しますが、引数で指定している1024
は一度に受信するデータの最大バイト数です。1024
バイトを超えるデータは複数回受信することになり、受信データがなくなるまでループします。
クライアント側プログラム
import socket def tcp_client_program(): host = "127.0.0.1" port = 50000 # ソケット作成 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket: # 接続 client_socket.connect((host, port)) # メッセージの作成 message = b"Message" # メッセージをサーバーに送信 client_socket.sendall(message) def main(): tcp_client_program() if __name__ == "__main__": main()
上記例では、tcp_client_program
関数がサーバー機能として動作します。
ソケットの作成方法がサーバー側プログラムと同様です。クライアントがサーバーに接続するためには、connect
を使用して「client_socket.connect((host, port))」の部分でサーバーのIPとポート番号を指定します。サーバーの情報であることに注意してください。
その後メッセージを作成して、sendall
でサーバーに送信しています。
実行方法と実行結果例
上記プログラムを実行する場合は、それぞれのプログラムを以下の順で実行します。
- サーバー側プログラムを実行する。
- 別プロンプトを立ち上げてクライアント側プログラムを実行する。
サーバー側プログラムを起動した状態でクライアントプログラムを実行すると、クライアントプログラム実行のたびにサーバー側プロンプトでは以下の実行結果例のように受信したメッセージが表示されます。
【実行結果例】※サーバー側プロンプト data: b'Message', address:('127.0.0.1', 9161) data: b'Message', address:('127.0.0.1', 9162) data: b'Message', address:('127.0.0.1', 9163)
なお、サーバー側プログラムは受信を待機し続けるように無限ループとなっているので、サーバー側プログラムを停止する際には「Ctrl+C
」等で強制終了してください。
UDPを使用した通信
サーバーとクライアント間のsocketを使用したUDP通信の例を紹介していきます。
サーバー側プログラム
import socket def udp_server_program(): host = "127.0.0.1" port = 50000 # ソケット作成 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server_socket: # バインド(ソケットにIPとポート番号を紐づけ) server_socket.bind((host, port)) while True: # データを受信 (データの最大バイトを1,024) data, address = server_socket.recvfrom(1024) print(f"data: {data}, address:{address}") def main(): udp_server_program() if __name__ == "__main__": main()
UDPの場合も、TCPのプログラム例とよく似ています。上記例では、udp_server_program`関数がサーバー機能として動作します。
ソケット作成やバインドの方法は、TCPの場合とほとんど同じですが、今回はソケット作成時にUDPプロトコルを示すsocket.SOCK_DGRAM
を指定します。
データの受信の部分では、TCPではaccept
で接続確立をしていましたが、UDPは接続確立が不要であるため直接recvfrom
でデータを受信しています。
クライアント側プログラム
import socket def udp_client_program(): host = "127.0.0.1" port = 50000 # ソケット作成 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client_socket: # メッセージの作成 message = b"Message" # メッセージをサーバーに送信 client_socket.sendto(message, (host, port)) def main(): udp_client_program() if __name__ == "__main__": main()
クライアントプログラムについても見ていただけると分かる通りTCPの場合とほとんど同じですが、ソケット作成時には、UDPプロトコルを示すsocket.SOCK_DGRAM
を指定します。
また、TCPではconnect
で接続確立をしていましたがUDPでは不要なため直接sendto
によりサーバーにデータを送信しています。sendto
では、第1引数にデータを、第2引数にサーバーのIPとポートのタプルを指定します。
実行方法と実行結果例
実行方法はTCPの例で紹介した方法と同様です。結果としては同じ結果になります。
UDPでは、接続を確立せずにデータを送信するため高速ですが信頼性は低下します。そのため、使用する場面についてはよく要件を確認して採用するようにしましょう。
まとめ
Pythonでsocketモジュールでサーバーとクライアント間の通信をする基本的な使い方を解説しました。
Pythonでは、ソケット通信のためのモジュールとしてsocketモジュールが提供されています。ソケット通信は通常TCP、UDPといったプロトコルを使用するため、それぞれの使用例について紹介しました。
なお、本記事ではsocketの基本的な使い方を説明することを目的としましたので例外処理は除外しています。そのため、実用的なアプリケーションでは例外に対する処理やセキュリティなどについて十分に考慮する必要がある点は注意してください。
ソケット通信は、プロセス間の通信の基本的な内容になるため、是非socketの使い方を理解してもらえればと思います。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。