Raspberry Pi & Python 開発ブログ ☆彡

Raspberry PiとPythonの基本的な使い方をわかりやすく解説。初心者、入門者必見!!

Python Socket通信の仕方:サーバー側

socket server

この記事の内容はRaspberry Pi 4と3の両方で動作確認済みです

ブログ管理者のP.Hです!

今回はRaspberry PiでSocket通信を行う方法を紹介したいと思います。ネットワークが繋がっていれば、Socket通信で制御やデータのやり取りが出来るので、とても便利です。最近はデバイスやコントローラを全てLANで接続して、制御することが多くなっていますので、Socket通信を使う機会がかなり増えていると思います。多くの機器に対応した通信方式ですので、マスターしておくことをお勧めします。

それではSocket通信の仕方を紹介します。Socket通信はサーバー⇔クライアント間で、通信する方式になっています。今回はサーバー側のサンプルコードの説明となります。
※下記のクライアント側の記事と合わせてお読みください。

www.raspberrypirulo.net

サンプルコードの内容

今回紹介するサンプルコードは以下のように動作します。

  • クライアントから文字を入力
  • 入力した文字列をサーバーに送信
  • サーバーがデータを受け取る
  • 受け取ったデータをそのままクライアントに送信

構成

  • サーバー:Raspberry Pi IPアドレス 192.168.1.10 ポート:12345
  • クライアント:Raspberry Pi IPアドレス 192.168.1.15 ポート:12345

サーバー側のサンプルコード

下記がサンプルコードになります。コメントも記載していますので、まずざっと目を通してみてください。

import socket
import threading
from datetime import datetime

HOST_IP = "192.168.0.10" # サーバーのIPアドレス
PORT = 12345 # 使用するポート
CLIENTNUM = 3 # クライアントの接続上限数
DATESIZE = 1024 # 受信データバイト数

class SocketServer():
    def __init__(self, host, port):
        self.host = host
        self.port = port

    # サーバー起動 
    def run_server(self):

        # server_socketインスタンスを生成
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_socket.bind((self.host, self.port))
            server_socket.listen(CLIENTNUM)
            print('[{}] run server'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
            
            while True:
                # クライアントからの接続要求受け入れ
                client_socket, address = server_socket.accept()
                print('[{0}] connect client -> address : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), address) )
                client_socket.settimeout(60)
                # クライアントごとにThread起動 send/recvのやり取りをする
                t = threading.Thread(target = self.conn_client, args = (client_socket,address))
                t.setDaemon(True)
                t.start()

    # クライアントごとにThread起動する関数
    def conn_client(self, client_socket, address):
        
        with client_socket:
            while True:
                # クライアントからデータ受信
                rcv_data = client_socket.recv(DATESIZE)
                if rcv_data:
                    # データ受信したデータをそのままクライアントへ送信
                    client_socket.send(rcv_data)
                    print('[{0}] recv date : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rcv_data.decode('utf-8')) )
                else:
                    break

        print('[{0}] disconnect client -> address : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), address) )

if __name__ == "__main__":
    
    SocketServer(HOST_IP, PORT).run_server()

それではコードの説明をしてきます。

  • socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ⇒ server_socketインスタンスを生成します。

  • server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    ⇒ プログラムを終了 -> 再起動をすぐに行うと"ソケットが既に使用されています"というエラーが発生する場合があります。この問題を解消する関数です。

  • server_socket.bind( (self.host, self.port) )
    ⇒ IPアドレスとポートを設定を引数に指定します。

  • server_socket.listen(CLIENTNUM)
    ⇒ クライアントの接続台数を設定、クライアントからの接続を待ちます。

  • client_socket, address = server_socket.accept()
    ⇒ クライアントからの接続要求受け入れ。接続が確立され、下記の戻り値を返します。
    ・client_socket:接続を通じてデータの送受信を行うための新しいクライアントのソケットオブジェクト
    ・ address :接続先でソケットにbindしているクライアントのアドレス
    ⇒ 戻り値を引数に与えて、self.conn_client関数をThreadで起動します。

  • rcv_data = client_socket.recv(DATESIZE)
    ⇒ クライアントからデータ受信。受信データをrcv_dataに格納します。

  • client_socket.send(rcv_data)
    ⇒ データ受信したデータをそのままクライアントへ送信します。

以上でSocket通信のサーバー側の説明は終了です。