【Python】サーバーとクライアント間でのデータ送信・受信方法(socketモジュール)

時計 2021.01.17 / 時計

【Python】サーバーとクライアント間でのデータ送信・受信方法(socketモジュール)

本記事ではPythonサーバーにデータを送信する方法を解説していきます。

ここではSocket通信を用いてサーバーとクライアントでデータのやり取りを行います。
使用するモジュールはsocketモジュールです。

本記事を通して以下の知識を学べます。

学べる知識
  • ソケット通信について
  • ソケット通信におけるTCP・UDPについて
  • SocketモジュールでのSocket通信方法
  • サーバー・クライアント間のSocket通信によるデータの送信・受信方法

Socket通信

Socket通信とは

Socket通信とはサーバーとクライアント間でネットワークを介してデータの送信・受信を行う仕組みです。
データのやり取りではIPアドレスとポート番号を指定します。

SocketはOSI参照モデルではトランスポート層(プログラム間の通信を規定している層)です。

コネクション型(TCP)で通信するか、コネクションレス型(UDP)で通信するかによって、大きくプログラムの流れが変わります。

トランスポート層

トランスポート層とはOSI参照モデルのレイヤ4であり、データ通信の信頼性などに関する規約が定義されています。

トランスポート層のプロトコルにはTCP(Transmission Control Protocol)とUDP(User Datagram Protoco)の二つがあります。

TCPはコネクション型のプロトコルであり、相手にデータが届いているかの確認を行いながらの通信のため信頼性が高いです。その分UDPと比べると速度は遅くなります。
信頼性を重視したプロトコルと言えます。

UDPはコネクションレス型のプロトコルのため、TCPと比べると信頼性は劣ります。その分高速な通信を行えます。
速度を重視したプロトコルと言えます。

プロトコル 信頼性 速さ
TCP コネクション型 高い 低速
UDP コネクションレス型 低い 高速

Socket通信ではTCPによる通信かUDPによる通信かの2種類があります。
どちらの通信かによって、Socket通信の方法が異なります。

サーバーとクライアントのSocket通信の流れ

TCP

TCPプロトコルでSocket通信を行う場合の流れを以下に記します。
サーバー側では以下の流れで動きます。

  1. ソケットの作成(TCP用)
  2. IPアドレスとポート番号を指定してソケットに割り当てる
  3. ソケットをクライアントからの接続待機状態にする
  4. クライアントから要求があれば接続の確立を行う
  5. クライアントからのデータを受信する(byte形式)
  6. (クライアントへデータを送信する)
  7. 接続を切断する

クライアント側では以下の流れで動きます。

  1. ソケットの作成(TCP用)
  2. IPアドレスとポート番号を指定してサーバーに接続要求をする
  3. サーバーへデータを送信する(byte形式)
  4. (サーバーからのデータを受信する)

UDP

UDPプロトコルでSocket通信を行う場合の流れを以下に記します。
サーバー側では以下の流れで動きます。

  1. ソケットの作成(UDP用)
  2. IPアドレスとポート番号を指定してソケットに割り当てる
  3. クライアントからのデータを受信する(byte形式)

クライアント側では以下の流れで動きます。

  1. ソケットの作成(UDP用)
  2. サーバーへデータを送信する(byte形式)

Socketモジュール

Pythonでソケット通信を行うにはSocketモジュールを使用します。
Socketモジュールは標準モジュールのためpipでインストールする必要はないです。

Socketモジュールをプログラムで使用するためにはインポートする必要があります。

import socket

以下でSocket通信で使用する各メソッドを解説していきます。

メソッド 説明
socket() ソケットの作成
bind() ソケットにIPアドレスとポート番号を割り当てる
listen() ソケットの接続待機状態にする
connect() サーバーに接続要求をする
accept() ソケット接続の確立を行う
recv() ソケットから送られたデータを受け取る
recvfrom() ソケットから送られたデータを受け取る
sendall() ソケットにデータを送信する
sendto() ソケットにデータを送信する
close() ソケットの接続を切断する

socket()メソッド

ソケットの作成にはsocket()メソッドを使用します。

socket.socket(address family, socket type)

address familyには利用するアドレス体系を指定します。

address family 説明
AF_INET IPv4(デフォルト)
AF_INET6 IPv6
AF_UNIX ローカルなプロセス間通信用

上記のほかにAF_CANやAF_PACKET、AF_RDSなどがあります。

socket typeにはソケットの性質を指定します。

socket type 説明
SOCK_STREAM TCP(デフォルト)
SOCK_DGRAM UDP

上記以外にはSOCK_RAWなどがあります。

bind()メソッド

IPアドレスとポート番号を指定してソケットに割り当てるためにbind()メソッドを使用します。

socket.bind(address)

addressにはタプルでIPアドレスとポート番号を指定します。

listen()メソッド

ソケットをクライアントからの接続待機状態にするためにlisten()メソッドを使用します。

socket.listen([backlog])

backlogには同時接続可能なクライアント数を指定する。指定する場合は0以上の数値を指定すること。

connect()メソッド

サーバーに接続要求をするためにconnect()メソッドを使用します。

socket.connect(address)

addressには接続先のIPアドレスとポート番号をタプルで指定します。

accept()メソッド

クライアントから要求により接続の確立を行うためにaccept()メソッドを使用します。

socket.accept()

accept()メソッドの返り値はconnとaddressです。

connはコネクション上でデータの送信や受信のために利用されるソケットオブジェクトです。
addressは相手のアドレスを示します。

recv()メソッド

recv()メソッドはソケットから送られたデータを受け取る際に使用されます。

socket.recv(bufsize)

bufsizeは一度で受け取れる最大データ量を指定します。
bufsizeには例えば4096や2048のような、2の累乗を指定することがお勧めです。

返り値は受け取ったデータのバイトオブジェクトです。

recvfrom()メソッド

recvfrom()メソッドはソケットから送られたデータを受け取る際に使用されます。

socket.recvfrom()

返り値は受け取ったデータのバイトオブジェクトとアドレスのペアです。
アドレスはデータを送るためのソケットのアドレスです。

recv()メソッドとrecvfrom()メソッドは返り値に違いがあります。

sendall()メソッド

sendall()メソッドはソケット(IPアドレスとポート番号で指定した相手)にデータを送信するために使用します。

socket.sendall(bytes)

bytesにはバイト型のデータを指定します。

TCPソケットへの通信時にsendallメソッドは使用されます。

sendto()メソッド

sendto()メソッドはソケットにデータを送信するために使用します。

socket.sendto(bytes, address)

bytesにはバイト型のデータを指定します。
データがstring型の場合はencode()メソッドでバイトに変換する必要がある。

addressにはタプルでIPアドレスとポート番号を指定します。

サーバー側は常にコネクト状態(待機状態)である必要がなく、UDPソケットへの通信時にsendtoメソッドは使用されます。

close()メソッド

ソケットを閉じる(接続を切断する)ためにclose()メソッドを使用します。

socket.close()

ServerとClient間のsocket通信サンプル(TCP)

Server

# server.py
import socket

ip_address = '127.0.0.1'
port = 7010
buffer_size = 4092

# Socketの作成
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # IP Adress とPort番号をソケット割り当てる
    s.bind((ip_address, port))
    # Socketの待機状態
    s.listen(5)
    # while Trueでクライアントからの要求を待つ
    while True:
        # 要求があれば接続の確立とアドレス、アドレスを代入
        conn, addr = s.accept()
        # データを受信する
        data = conn.recv(buffer_size)
        print('data-> {}, addr->{}'.format(data, addr))
        # データを送信する
        conn.sendall(b'I received the data correctly.')

Client

# client.py
import socket

ip_address = '127.0.0.1'
port = 7010
buffer_size = 4092

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # サーバーに接続を要求する
    s.connect((ip_address, port))
    # データを送信する
    s.sendall(b'I sent a message.')
    # サーバーからのデータを受信
    data = s.recv(buffer_size)

    print(data.decode())

サンプルプログラムについて

上記サンプルプログラムのようなSocket通信のプログラムは以下の流れで起動させます。

  1. サーバー側(server.py)のSocket通信を起動し、要求の待機状態にする
  2. クライアント側(client.py)を起動し、サーバーに接続要求、データ送信を行う

サンプルプログラムではローカルPC(127.0.0.1)でサーバーとクライアントの1台2役としています。

両プログラムを起動するとサーバー側では以下の表示がされます。

data-> b'I sent a message.', addr->('127.0.0.1', 63748)

クライアント側では以下の表示がされます。

I received the data correctly.

上記結果より、TCPによるSocket通信が成功していることがわかります。

ServerとClient間のsocket通信サンプル(UDP)

Server

# server.py
import socket

ip_address = '127.0.0.1'
port = 7010
buffer_size = 4092

# Socketの作成
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    # IP Adress とPort番号をソケット割り当てる
    s.bind((ip_address, port))
    # while Trueでクライアントからの要求を待つ
    while True:
        # データを受信する
        data, addr = s.recvfrom(buffer_size)
        print('data-> {}, addr->{}'.format(data, addr))

# client.py
import socket

ip_address = '127.0.0.1'
port = 7010
buffer_size = 4092

# Socketの作成
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    # データを送信する
    s.sendto(b'I sent a message.', (ip_address, port))

サンプルプログラムについて

サンプルプログラムの起動の流れはTCPと同じです。

プログラムを起動するとサーバー側で以下の表示がされます。

data-> b'I sent a message.', addr->('127.0.0.1', 61319)

上記の結果より、UDPによるソケット通信が成功したことがわかります。

まとめ

本記事「サーバーとクライアント間でのデータ送信・受信方法(socketモジュール)」はいかがでしたか。

Socket通信はWebアプリケーションでよく使用される技術です。

リアルタイムでサーバーとクライアント間でデータのやり取りができるようになります。
ぜひSocket通信技術を使ったWebアプリケーションを開発してみてください。