socket

【Python】ソケット(socket)通信の基本的な使い方

【Python】ソケット(socket)通信の基本的な使い方

Pythonでsocketモジュールサーバーとクライアント間の通信をする基本的な使い方を解説します。

ソケット(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で待機し、接続があるとconnaddressに接続情報が格納されます。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でサーバーに送信しています。

実行方法と実行結果例

上記プログラムを実行する場合は、それぞれのプログラムを以下の順で実行します。

  1. サーバー側プログラムを実行する。
  2. 別プロンプトを立ち上げてクライアント側プログラムを実行する。

サーバー側プログラムを起動した状態でクライアントプログラムを実行すると、クライアントプログラム実行のたびにサーバー側プロンプトでは以下の実行結果例のように受信したメッセージが表示されます。

【実行結果例】※サーバー側プロンプト
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の使い方を理解してもらえればと思います。