【Python】ソケット通信で辞書を送信する方法(JSONの利用)

Pythonのソケット通信で辞書を送信する方法の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Python でのソケット通信における “辞書を送信する方法” について解説していきます。

といっても、残念なことに、実はソケット通信では辞書を直接送信することは不可能です。実際にソケット通信で辞書を送信しようとすると下記の例外が発生することになります。

TypeError: a bytes-like object is required, not 'dict'

この例外のメッセージからも分かるように、ソケット通信で送信できるデータは bytes-like なデータのみです。シンプルにソケット通信で送信可能なのはバイト型のデータのみと考えてしまってよいでしょう(以降、バイト型のデータをバイトデータと呼びます)。

ソケット間で受信可能なデータがバイト型のデータのみであることを示す図

なので、本ページのタイトルような「ソケット通信での辞書の送信」を直接的に実現する方法なんて存在しないのです…。

なんですが、疑似的に実現することは可能です。具体的には、結局ソケット通信ではバイトデータしか送受信できないため、送信側のプログラムで辞書をバイトデータに変換して送信し、受信側のプログラムで受信したバイトデータを辞書に逆変換することで送信側から受信側への辞書の受け渡しを実現します。

ここまで読んで、ちょっと騙された気持ちになっている方もおられるかもしれませんが、辞書は非常に扱いやすいデータとなりますので、疑似的な方法でも辞書をソケット通信で送受信できればかなり便利になると思います。

是非、ページを最後まで読んでいただき、辞書のソケット通信での疑似的な送信方法をマスターしていただければと思います!

MEMO

このページでは、ソケット通信に socket モジュールを利用することを前提に解説を行います

他の通信モジュール、特にアプリケーション層のプロトコルに対応した通信モジュールではデータの送信用メソッドで直接辞書が指定可能なものも存在しますので注意してください

ちなみに、ソケット通信自体については下記ページで詳しく解説していますので、ソケット通信について学び直したいという方はぜひ下記ページを読んでみてください!かなり長い記事になっていますが、その分ソケット通信について幅広く・深く理解できると思います。

Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)

ソケット通信で辞書を送信する方法

では、ソケット通信で辞書を送信する方法について解説していきます。

先ほど説明したように、辞書から変換したバイトデータを送信し、さらに通信相手が受信したバイトデータを辞書に変換することで、ソケット通信での辞書の送信を疑似的に実現していきます。

辞書を擬似的に送信する方法の説明図

で、ここでポイントになるのが、辞書をバイトデータに、さらにバイトデータを辞書に変換する方法になります。

辞書をバイトデータに変換する方法

ということで、この変換方法について考えていきましょう!

文字列とバイトデータの変換

まず、Python では文字列のバイトデータへの変換は非常に簡単に行うことができます。文字列に  encode メソッドを実行させれば、その文字列をバイトデータに変換した結果が返却値として得られます。

文字列からバイトデータへの変換
str_data = 'Hello World'

# 文字列をバイトデータに変換
byte_data = str_data.encode()

さらに、バイトデータに decode メソッドを実行させれば、そのバイトデータを文字列に変換した結果が返却値として得られます。

バイトデータから文字列への変換
byte_data = b'Hello World'

# バイトデータを文字列に変換
str_data = byte_data.decode()

したがって、送信したい辞書のデータを文字列に変換することができれば、後は encode メソッドを利用することでソケット通信で通信相手に送信することができます。また、文字列を辞書に変換することができれば、受信したバイトデータを文字列に変換し、そこから通信相手が送信してきた辞書のデータを得ることができることになります。

辞書を文字列に変換すれば辞書の擬似的な送信が可能であることを示す図

ということで、後は辞書の文字列への変換、および文字列の辞書への変換が実現できれば、送信側から受信側にソケット通信で辞書を受け渡しすることができることになります。

辞書と文字列の変換

で、この辞書からの文字列への変換および、文字列からの辞書への変換は json モジュールを利用することで簡単に実現することが可能です。この json は Python の標準モジュールとなるため、おそらく Python をインストールするだけで利用可能になると思います。

そして、この json モジュールには dumps 関数が定義されており、引数に辞書型のデータを指定して json.dumps を実行すれば、返却値として、引数に指定した辞書型のデータを文字列に変換した結果を得ることができます。

辞書から文字列への変換
import json

dict_data = {
    'Name' : 'Yamada Hanako',
    'Height' : 169.5,
    'Hobbies' : ['Soccer', 'Music', 'Movie']
}

# 辞書を文字列(JSON形式)に変換する
str_data = json.dumps(dict_data)

この文字列は JSON 形式のものとなり、JSON 形式に関しては別途ググって調べていただければ良いのですが、ここでは簡単に “辞書のデータに含まれるキーと値の情報を文字列に変換したもの” と考えてもらってよいです。

json.dumpで辞書をJSON形式の文字列に変換可能であることを示す図

そして、この JSON 形式の文字列は、json モジュールの loads 関数によって辞書に変換することも可能です。

json.loadsでJSON形式の文字列を辞書に変換可能であることを示す図

すなわち、JSON 形式の文字列を引数に指定して json.loads を実行すれば、その文字列を辞書に変換した結果が得られます。

文字列から辞書への変換
import json

str_data = '{"Name": "Yamada Hanako", "Height": 169.5, "Hobbies": ["Soccer", "Music", "Movie"]}'

# 文字列(JSON形式)を辞書に変換する
dict_data = json.loads(str_data)

つまり、文字列とバイトデータとの間の変換に encodedecode を利用し、さらに辞書と文字列との間の変換に json.dumpsjson.loads を利用すれば、疑似的にですが辞書をソケット通信で通信相手に送信することができることになります。

辞書から文字列への変換がjson.dumps、文字列から辞書の変換がjson.loadsによって実現可能であることを示す図

スポンサーリンク

辞書をソケット通信で送信する流れ

ということで、ソケット通信で辞書のを送信する手順は下記となります。

  • 送信側
    1. 送信したい辞書を用意する
    2. 辞書を json.dumps で文字列に変換する
    3. 変換後の文字列を encode でバイトデータに変換する
    4. sendsendto で変換後のバイトデータを送信する
  • 受信側
    1. recvrecvfrom でバイトデータを受信する
    2. 受信したバイトデータを decode で文字列に変換する
    3. 変換後の文字列を json.loads で辞書に変換する
      (送信側が 1. で用意した辞書が取得できる!)

ソケット通信で辞書を送信するプログラム

では、ここまでの解説を踏まえてソケット通信で辞書を送信するプログラムを紹介していきます。辞書を送信する側のプログラム(クライアント)と辞書を受信する側のプログラム(サーバー)の2つのスクリプトを紹介していきます。

今回は UDP で通信を行うプログラムを紹介しますが、TCP の場合も同様の手順で辞書の受け渡しが可能です。

また、ここで紹介する2つのスクリプトは同じ PC 上で実行されることを前提としたものになっています。異なる2つの PC 上でスクリプトを実行する場合は 辞書を送信するプログラム'127.0.0.1' の部分を送信先の PC (のネットワークインターフェース)の IP アドレスに変更してください。 

UDP や TCP についてや、ソケット通信プログラム自体の作り方については下記ページで解説していますので、これらについて詳しく知りたい方は下記ページを参照していただければと思います。

Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)

辞書を送信するプログラム

下記が辞書を送信するプログラムのスクリプトの例になります。コメントで記述している通り、今まで説明してきた方法・流れで辞書をバイトデータに変換したのちに、そのバイトデータを sendto メソッドで 127.0.0.1 へ送信しています。

client.py
import socket
import json

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)


# 送信したい辞書を用意する
dict_data = {
    'Name' : 'Yamada Hanako',
    'Height' : 169.5,
    'Hobbies' : ['Soccer', 'Music', 'Movie']
}

# 辞書を文字列(JSON形式)に変換する
str_data = json.dumps(dict_data)

# 文字列をバイトデータに変換する
byte_data = str_data.encode()

# バイトデータを送信する
sock.sendto(byte_data, ('127.0.0.1', 40001))

sock.close()

スポンサーリンク

辞書を受信するプログラム

上記の 辞書を送信するプログラム の通信相手となるプログラムのスクリプトは下記となります。こちらもコメントを読んでいただければ分かる通り、recv メソッドで受信したバイトデータを今まで説明してきた方法・流れで辞書に変換し、最後に print でその辞書の出力を行っています。

server.py
import socket
import json

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

sock.bind(('0.0.0.0', 40001))

# バイトデータを受信する
byte_data = sock.recv(1024)

# バイトデータを文字列に変換する
str_data = byte_data.decode()

# 文字列を辞書に変換する
dict_data = json.loads(str_data)

print(dict_data)

sock.close()

プログラムの実行手順

プログラムの実行手順についても説明しておきます。

まず、辞書を送信するプログラム で紹介したスクリプトを client.py辞書を受信するプログラム で紹介したスクリプトを server.py という名前で同じフォルダに保存してください。次に、ターミナルアプリやコマンドプロンプト等のコマンド実行が可能なアプリ(以降、CLI アプリと呼びます)を2つ起動し、それぞれのアプリで先ほどスクリプトを保存したフォルダに cd コマンドで移動してください。

続いて、下記のように python コマンドで server.py を実行します。

python server.py

client.py よりも先に server.py を実行しておく必要がある点に注意してください。基本的には、通信プログラムは最初に受信待ちを行う方のプログラムを先に実行しておく必要があります。server.py を実行する前に client.py を実行すると、受信待ちしている相手がいない状態でデータが送信されてしまうことになります。

続いて、もう1つの CLI アプリで下記のように python コマンドで client.py を実行します。

python client.py

これにより、client.py から server.py に辞書をバイト型に変換したデータが送信されることになります。そして、server.py がそれを受信し、辞書に戻した結果が print で出力されます。ということで、server.py を実行中の方の CLI アプリを確認すれば下記のような辞書のデータの除法が出力されていることが確認できると思います。

{'Name': 'Yamada Hanako', 'Height': 169.5, 'Hobbies': ['Soccer', 'Music', 'Movie']}

この動作より、疑似的にではあるのですが、辞書をソケット通信で通信相手に送信できていることが確認できると思います。今回は単に print で辞書を出力しているだけですが、もちろん辞書のキーを指定して値を取得するといった通常の辞書の使い方も可能です。

辞書を送受信できるようになればソケット通信プログラムがかなり開発しやすくなると思いますので、是非今回紹介したような辞書の送信方法については理解しておきましょう!

参考:str() での辞書から文字列への変換

最後に1点補足しておきます。

Python に詳しい人であれば、ここまでの解説を読んで「文字列への変換であれば単に str() を実行すれば良いだけじゃないの?」と疑問に感じたかもしれません。例えば、下記を実行しても辞書を文字列に変換することは確かに可能です。

strでの文字列への変換
import json

dict_data = {
    'color' : 'orange',
    'is_fruit' : True
}

str_data = str(dict_data)

ですが、結論としては json モジュールを利用した文字列への変換を行う方が無難だと思います。

前述のとおり、確かに Python では辞書を str() で文字列に変換することは可能なのですが、str() で辞書を文字列に変換した場合、その文字列を辞書に戻すのが大変です。例えば eval 関数を利用して文字列を辞書に戻すことも可能なのですが、eval 関数の場合は引数で指定した文字列が Python のコードとして評価されて実行されてしまうためセキュリティに対するリスクが上がってしまいます。

また、この変換では単なる Python の文字列に変換されることになります。それに対し、json.dumps 関数では JSON 形式の文字列に変換することができます。両方とも文字列ではあるのですが、変換結果が異なることになります。

例えば下記は辞書のデータ dict_datastr() で文字列に変換した結果と json.dumps() で文字列に変換した結果を出力するスクリプトになります。 

strとjson.dumpsでの文字列への変換
import json

dict_data = {
    'color' : 'orange',
    'is_fruit' : True
}

str_data1 = str(dict_data)
str_data2 = json.dumps(dict_data)

print(str_data1)
print(str_data2)

出力される文字列は下記のように異なります。

{'color': 'orange', 'is_fruit': True}
{"color": "orange", "is_fruit": true}

JSON 形式の文字列は多くのプログラミング言語で利用可能な文字列なのですが、Python の文字列は基本的に Python 向けの文字列になります。なので、例えば Python 以外で開発された通信プログラムにデータを送信しても、その受信したデータを受信側で上手く扱えない可能性があります。それに対し、JSON 形式の文字列は JSON として扱われるため、Python 以外で開発された通信プログラムでも正常に扱うことが可能です。

そして、何より通信プログラムでは JSON 形式の文字列が送信されてくることを期待したつくり・設計となっているものも多いです。例えば REST API などは JSON 形式の文字列を入力として受け付けるようなものも多いです。

上記のような理由より、str() で辞書を文字列に変換するのではなく、json モジュールを利用して辞書を文字列に変換することをオススメします。特に通信プログラムではセキュリティのリスクを考慮し、安全なプログラムを開発することが他のプログラムに比べてより重要になるため、受信した文字列に対して eval 等を実行することは避けた方が良いです。

スポンサーリンク

まとめ

このページでは、ソケット通信で辞書を送信する方法について解説しました!

ページの冒頭でも述べたように、ソケット通信(socket モジュール)で辞書を直接送信することはできません。ですが、辞書をバイナリデータに変換してやれば送信することは可能で、この変換は json モジュールと encode (decode) メソッドの利用により簡単に実現可能です。

辞書を送受信することができればかなりプログラムが作りやすくなりますし、json モジュールはデータの送受信以外の場面でも利用頻度の高いモジュールになります。是非、このページで解説した内容を覚えておいていただき、辞書が送受信可能なプログラムの開発だけでなく、様々な場面での json モジュールの利用にも活用していただければと思います!

同じカテゴリのページ一覧を表示