Raspberry Pi & Python 開発ブログ ☆彡

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

Python Socket通信の仕方:クライアント編

socket client

この記事の内容は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

クライアント側のサンプルコード

今回、サンプルコードを2パターン紹介したいと思います。

  • 通信する毎にソケットのオープン/クローズをする方法
  • はじめの1回だけソケットのオープン/クローズをする方法

現在のハードウェアスペックであれば、毎回ソケットのオープン/クローズをしても全く問題ありませんが、こちらのほうが処理は重たいです。

クライアント側のサンプルコード ①

通信する毎にソケットのオープン/クローズをするサンプルコードです。コメントも記載していますので、まずざっと目を通してみてください。

import socket
import time
from datetime import datetime

HOST_IP = "192.168.0.10" # 接続するサーバーのIPアドレス
PORT = 12345 # 接続するサーバーのポート
DATESIZE = 1024  # 受信データバイト数

class SocketClient():

    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.socket = None

    def send_recv(self, input_data):
        
        # sockインスタンスを生成
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            # ソケットをオープンにして、サーバーに接続
            sock.connect((self.host, self.port))
            print('[{0}] input data : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), input_data) )
            # 入力データをサーバーへ送信
            sock.send(input_data.encode('utf-8'))
            # サーバーからのデータを受信
            rcv_data = sock.recv(DATESIZE)            
            rcv_data = rcv_data.decode('utf-8')
            print('[{0}] recv data : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rcv_data) )

if __name__ == '__main__':

    client = SocketClient(HOST_IP, PORT)
    while True:
        input_data = input("send data:") # ターミナルから入力された文字を取得
        client.send_recv(input_data)

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

  • with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    ⇒ sockモジュールをインスタンス化。with文を抜けると自動的にソケットをcloseする。

  • sock.connect( (self.host, self.port) )
    ⇒ サーバー側のIPアドレスとポートを引数に指定。ソケットをオープンにして、サーバーに接続する。

あとは、send関数でデータをサーバーへ送信、recv関数でサーバーからデータを受信しています。

クライアント側のサンプルコード ②

はじめの1回だけソケットのオープンをするサンプルコードです。

import socket
import time
from datetime import datetime

HOST_IP = "192.168.0.10" # 接続するサーバーのIPアドレス
PORT = 12345 # 接続するサーバーのポート
DATESIZE = 1024 # 受信データバイト数
INTERVAL = 3 # ソケット接続時のリトライ待ち時間
RETRYTIMES = 5 # ソケット接続時のリトライ回数

class SocketClient():

    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.socket = None

    def connect(self):
    
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)       
    
        # サーバーとの接続 RETRYTIMESの回数だけリトライ
        for x in range(RETRYTIMES):
            try:
                client_socket.connect((self.host, self.port))
                self.socket =  client_socket
                print('[{0}] server connect -> address : {1}:{2}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), self.host, self.port) )
                break
            except socket.error:
                # 接続を確立できない場合、INTERVAL秒待ってリトライ
                time.sleep(INTERVAL)
                print('[{0}] retry after wait{1}s'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str(INTERVAL)) )
 
    # サーバーへデータ送信関数
    def send(self):
        # ターミナルから入力された文字を取得
        input_data = input("send data:")
        print('[{0}] input data : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), input_data) )
        input_data = input_data.encode('utf-8')
        self.socket.send(input_data) # データ送信
    
    # サーバーからデータ受信関数
    def recv(self):
        rcv_data = self.socket.recv(DATESIZE) # データ受信
        rcv_data = rcv_data.decode('utf-8')
        print('[{0}] recv data : {1}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rcv_data) )
        return rcv_data
    
    # 上記の送信/受信関数を順番に行う
    def send_rcv(self):
        self.send()
        return self.recv()

    # ソケットをクローズする関数
    def close(self):
    
        self.socket.close() # ソケットクローズ
        self.socket = None
             
if __name__ == '__main__':
    
    client = SocketClient(HOST_IP, PORT)
    client.connect() # はじめの1回だけソケットをオープン
    
    while True:
        if client.socket is not None:
             # quitが戻ってくる(自分でquitと入力する)とソケットをクローズして終了
            if client.send_rcv() == 'quit':
                client.close()
        else:
            break
  • def connect(self):
    ⇒ client_socket.connectでサーバーとの接続を試みます。接続ができない場合は、INTERVALの値だけ待った後に、RETRYTIMESの回数だけリトライをします。

あとは、send関数でデータをサーバーへ送信、recv関数でサーバーからデータを受信しています。ターミナルで"quit"と入力すると、quitの文字列がサーバーに送られ、サーバーからquitの文字列がクライアントに返ってきます。この操作を行うと、client.close()が呼ばれ、ソケットがクローズされます。

以上でSocket通信のクライアント側の説明は終了です。