本記事の適用シーン
raspberry piでsocket通信をしたときに
「IndexError: string index out of range」
というエラーが発生した時の対処
前段
socket通信とは
ソケット インターネットはTCP/IPと呼ぶ通信プロトコルを利用しますが、そのTCP/IPをプログラムから利用するには、プログラムの世界とTCP/IPの世界を結ぶ特別な出入り口が必要となります。 その出入り口となるのがソケット(Socket)であり、TCP/IPのプログラミング上の大きな特徴となっています。
http://research.nii.ac.jp/~ichiro/syspro98/socket.html
この技術を使うことでチャットアプリ等を作ることができます。
また、異なるプロセス間でデータをやり取りすることもできます。
例えば、C#内のデータをsocket通信で同じPC内で動いているpythonのコードへ送信することもできます。ディープラーニングが得意なpythonと他の言語のコラボレーションをする際に使うことができます。
また、低スペックPCであるラズパイでカメラやセンサーでデータを取得し、実際に解析するクラウドのサーバーへデータを送信する、という使い方も想定できます。
また、世の中にはsocket通信でコマンドを送ることで機器の状況を把握できる装置があります。
例えば、PLCという装置にはsocket通信を用いてPCから制御を行うことができます。
エラーの状況・原因
例えば、以下のようなpythonでのsocket通信のコードがあったとします
#文字列の送信
client.sendall(b"送信文")
#返信の文字列の取得
response = client.recv(16)
#返信文字列の一部を取り出す
output = response.decode()[8] #<--ここでエラーになる
そうしますと最後の行でエラーとなることがあります。
原因は
全ての受信文字列を取得せずに次の処理に行ってしまい、文字列の範囲外を指定しまう
ということです。
イメージ図にすると以下のようになります。
以下の例では、16の長さの文字列を受信することを想定しています。
ソケット通信のサンプルコードを載せている記事の中には上記のような書き方をしている場合がありますので、次に載せるコードのような変更が必要です。
実は前述のコードでも処理能力の高いPCであればエラーにならないことがほとんどですが、ラズパイのような低スペックのPCだと発生します。
従いまして、socket通信のコードを組む際には後述の公式ドキュメントのような実装をすることが非常に重要になります。
対処方法
必要な文字列数を取得できるまでrecvを続ける必要があります。
recv(16)の16は「16文字受け取る」ではなく「最大16文字受け取る」ということなので確実に16文字受け取れるかわかりません。
従いまして、
①outputという空の文字列を作り
②while文で文字列を受け取り続け
③recvで受け取った後にoutputにくっつけていき
④16文字になったら、while文を抜ける
という処理が必要になっています。
公式のドキュメントでもそのようなコードになっています。
#出力の文字列の初期化
output = ""
#16個の文字列になるまで受信を続ける
while len(output) < 16:
response = client.recv(16).decode()
output = output + response
#返信文字列の一部を取り出す
output = response.decode()[8]
実際には上記のコードを関数にして使うことになります。
def receive(client, msglen):
output = ""
while len(output) < msglen:
response = client.recv(24).decode()
output = output + response
return output
最後に
socket通信でネットのコピペコードを使う際にハマりがちなエラーの回避方法について紹介しました。ネットにはサンプルコードが多く存在しますがエラーでハマった際には今回のように公式ドキュメントのサンプルコードを見ることをお勧めします。
コメント