このページでは、Python でのソケット通信における “辞書を送信する方法” について解説していきます。
といっても、残念なことに、実はソケット通信では辞書を直接送信することは不可能です。実際にソケット通信で辞書を送信しようとすると下記の例外が発生することになります。
TypeError: a bytes-like object is required, not 'dict'
この例外のメッセージからも分かるように、ソケット通信で送信できるデータは bytes-like なデータのみです。シンプルにソケット通信で送信可能なのはバイト型のデータのみと考えてしまってよいでしょう(以降、バイト型のデータをバイトデータと呼びます)。
なので、本ページのタイトルような「ソケット通信での辞書の送信」を直接的に実現する方法なんて存在しないのです…。
なんですが、疑似的に実現することは可能です。具体的には、結局ソケット通信ではバイトデータしか送受信できないため、送信側のプログラムで辞書をバイトデータに変換して送信し、受信側のプログラムで受信したバイトデータを辞書に逆変換することで送信側から受信側への辞書の受け渡しを実現します。
ここまで読んで、ちょっと騙された気持ちになっている方もおられるかもしれませんが、辞書は非常に扱いやすいデータとなりますので、疑似的な方法でも辞書をソケット通信で送受信できればかなり便利になると思います。
是非、ページを最後まで読んでいただき、辞書のソケット通信での疑似的な送信方法をマスターしていただければと思います!
このページでは、ソケット通信に socket
モジュールを利用することを前提に解説を行います
他の通信モジュール、特にアプリケーション層のプロトコルに対応した通信モジュールではデータの送信用メソッドで直接辞書が指定可能なものも存在しますので注意してください
ちなみに、ソケット通信自体については下記ページで詳しく解説していますので、ソケット通信について学び直したいという方はぜひ下記ページを読んでみてください!かなり長い記事になっていますが、その分ソケット通信について幅広く・深く理解できると思います。
Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)Contents
ソケット通信で辞書を送信する方法
では、ソケット通信で辞書を送信する方法について解説していきます。
先ほど説明したように、辞書から変換したバイトデータを送信し、さらに通信相手が受信したバイトデータを辞書に変換することで、ソケット通信での辞書の送信を疑似的に実現していきます。
で、ここでポイントになるのが、辞書をバイトデータに、さらにバイトデータを辞書に変換する方法になります。
辞書をバイトデータに変換する方法
ということで、この変換方法について考えていきましょう!
文字列とバイトデータの変換
まず、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 形式の文字列は、json
モジュールの loads
関数によって辞書に変換することも可能です。
すなわち、JSON 形式の文字列を引数に指定して json.loads
を実行すれば、その文字列を辞書に変換した結果が得られます。
import json
str_data = '{"Name": "Yamada Hanako", "Height": 169.5, "Hobbies": ["Soccer", "Music", "Movie"]}'
# 文字列(JSON形式)を辞書に変換する
dict_data = json.loads(str_data)
つまり、文字列とバイトデータとの間の変換に encode
と decode
を利用し、さらに辞書と文字列との間の変換に json.dumps
と json.loads
を利用すれば、疑似的にですが辞書をソケット通信で通信相手に送信することができることになります。
スポンサーリンク
辞書をソケット通信で送信する流れ
ということで、ソケット通信で辞書のを送信する手順は下記となります。
- 送信側
- 送信したい辞書を用意する
- 辞書を
json.dumps
で文字列に変換する - 変換後の文字列を
encode
でバイトデータに変換する send
やsendto
で変換後のバイトデータを送信する
- 受信側
recv
やrecvfrom
でバイトデータを受信する- 受信したバイトデータを
decode
で文字列に変換する - 変換後の文字列を
json.loads
で辞書に変換する
(送信側が 1. で用意した辞書が取得できる!)
ソケット通信で辞書を送信するプログラム
では、ここまでの解説を踏まえてソケット通信で辞書を送信するプログラムを紹介していきます。辞書を送信する側のプログラム(クライアント)と辞書を受信する側のプログラム(サーバー)の2つのスクリプトを紹介していきます。
今回は UDP で通信を行うプログラムを紹介しますが、TCP の場合も同様の手順で辞書の受け渡しが可能です。
また、ここで紹介する2つのスクリプトは同じ PC 上で実行されることを前提としたものになっています。異なる2つの PC 上でスクリプトを実行する場合は 辞書を送信するプログラム の '127.0.0.1'
の部分を送信先の PC (のネットワークインターフェース)の IP アドレスに変更してください。
UDP や TCP についてや、ソケット通信プログラム自体の作り方については下記ページで解説していますので、これらについて詳しく知りたい方は下記ページを参照していただければと思います。
Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)辞書を送信するプログラム
下記が辞書を送信するプログラムのスクリプトの例になります。コメントで記述している通り、今まで説明してきた方法・流れで辞書をバイトデータに変換したのちに、そのバイトデータを sendto
メソッドで 127.0.0.1
へ送信しています。
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
でその辞書の出力を行っています。
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()
を実行すれば良いだけじゃないの?」と疑問に感じたかもしれません。例えば、下記を実行しても辞書を文字列に変換することは確かに可能です。
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_data
を 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
モジュールの利用にも活用していただければと思います!
また、他のデータを送信する例として、下記ページでファイルの送信方法について解説しています。興味があれば下記ページも是非読んでみてください!
【Python】ソケット通信でのファイルの送信方法