質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Q&A

解決済

2回答

5302閲覧

ソケット通信でrecvしたデータをdecodeうまくできません。

oniko

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

0グッド

0クリップ

投稿2021/09/04 15:13

前提・実現したいこと

TCPIPによりc言語のプログラム(Client側)からPython 2.7のプログラム(Server側)へデータを渡したい。※c言語のプログラムはYOLOv3を実装しているプログラムの一部で,下のコードはdarknet上のsrc/image.cというプログラムの中で、検出した物体のピクセル位置を出力する関数です。Pythonはただの自作のスクリプトで、ROS上で動かすものです。

ここに質問の内容を詳しく書いてください。
サーバ側で受け取ったデータをきちんと変換できず、

return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x9a in position 4: invalid start byte

というエラーが帰ってきます。

発生している問題・エラーメッセージ

return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: 'utf8' codec can't decode byte 0x9a in position 4: invalid start byte

該当のソースコード

Python

1#!/usr/bin/env python 2# coding: UTF-8 3import rospy 4from std_msgs.msg import String 5import socket 6import datetime 7def talker(): 8 pub = rospy.Publisher('chatter', String, queue_size=10) 9 rospy.init_node('talker', anonymous=True) 10 PORT = 50000 11 BUFSIZE = 4096 12 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 server.bind(("", PORT)) 14 server.listen(1) 15 while not rospy.is_shutdown(): 16 client, addr = server.accept() 17 msg = str(datetime.datetime.now()) 18 client.sendall(msg.encode("UTF-8")) 19 print(msg, "Oh, you got message!!!") 20 data = client.recv(BUFSIZE) 21 print(data.decode("UTF-8")) 22 client.close 23 hello_str = "hello world %s" % rospy.get_time() 24 pub.publish(hello_str) 25 26if __name__ == '__main__': 27 try: 28 talker() 29 except rospy.ROSInterruptException: 30 pass

c

1void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes) 2{ 3 int i,j; 4 5 for(i = 0; i < num; ++i){ 6 char labelstr[4096] = {0}; 7 int class = -1; 8 for(j = 0; j < classes; ++j){ 9 if (dets[i].prob[j] > thresh){ 10 if (class < 0) { 11 strcat(labelstr, names[j]); 12 class = j; 13 } else { 14 strcat(labelstr, ", "); 15 strcat(labelstr, names[j]); 16 } 17 printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100); 18 printf(" -> (x, y, w, h)\n"); 19 20 printf(" = (%d, %d, %d, %d)\n", 21 (int)(dets[i].bbox.x*im.w), 22 (int)(dets[i].bbox.y*im.h), 23 (int)(dets[i].bbox.w*im.w), 24 (int)(dets[i].bbox.h*im.h)); 25 printf("初期化はOK\n"); 26 int detection_coordinates[3]; 27 28 detection_coordinates[0] = (int)(dets[i].bbox.x*im.w); 29 detection_coordinates[1] = (int)(dets[i].bbox.y*im.h); 30 detection_coordinates[2] = (int)(dets[i].bbox.w*im.w); 31 detection_coordinates[3] = (int)(dets[i].bbox.h*im.h); 32 printf("代入はOK\n"); 33 34 char x[256]; 35 sprintf(x, "%d", detection_coordinates[0]); 36 printf(x); 37 char server_response[256] = "you reached to the server"; 38 //tcpip 39 int network_socket; 40 //socket() 41 //specifies to use TCP 42 network_socket = socket(AF_INET, SOCK_STREAM, 0); 43 if(network_socket < 0){ 44 printf("ERROR opening socket dude."); 45 } 46 struct sockaddr_in server_address; 47 server_address.sin_family = AF_INET; 48 server_address.sin_port = htons(50000); 49 server_address.sin_addr.s_addr = INADDR_ANY; 50 51 //connect() 52 int connection_status = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address)); 53 if(connection_status == -1){ 54 printf("connect() error\n"); 55 sleep(1); 56 } 57 else { 58 //send() 59 //char server_response[256]; 60 send(network_socket, &x, sizeof(x), 0); 61 //colse() 62 close(network_socket); 63 sleep(1); 64 } 65 // 66 } 67 }

試したこと

Python(サーバ側)で、print(data.decode("UTF-8"))のdecodeをCP932、shift-jisでも試してみましたが同じ結果でした。"UTF-16"でやるとエラーは出ませ得んが、文字化けで何がprintfで出力されているのか全くわかりません。

補足情報(FW/ツールのバージョンなど)

Ubuntu18.04で動かしています。
ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

二つ、重要な知識が欠けている気がします。

ひとつめ。
char x[256];
sprintf(x, "%d", detection_coordinates[0]);
send(network_socket, &x, sizeof(x), 0);
では、xという名前で256バイトの領域を確保し、sprintfでその先頭から数値を文字列に変換して格納し(Cの文字列なので'\0'終端)、xの領域256バイトを全部送信します。データが充填されていようといまいと。
また、Cでは、ローカル(のauto)変数は、生成された段階で初期化されず不定(プログラムを実行してみるまで値が確定されない)です。つまり、整数データはたかだか11桁かそこいらですから、最大11byteの数字と1byteの'\0'と、実行毎になにが入るかわからない243byte(+α)のデータが送信されます。

もうひとつ。
ストリームソケット(TCP/IP)での通信のデータ単位は「バイト」です。データは勝手にはまとまってはくれません。例えば'1' '2' '3' '4' '5' '6'を送信したとしてそれが1回のread()で"123456"と受信されるか、read()6回で'1' '2 '...と受信されるかあるいはread()2回で'1'と"23456"になるか、(ソケット通信のレベルでは)管理されません。あるいは、2回のwrite()で'1'と'2'を送信したものが1回のread()で"12"と受信される可能性だってあります。
送信間隔と受信間隔の関係で「たまたま」送る側でまとめて送ったデータが受信側でまとめて受信されていることはあるかも知れませんが、途中で様々な通信路を通った場合にその区切りが保存される保証はありません。なので、2バイト以上のデータの塊をやりとりしたいのなら、送信側と受信側でデータの塊を作る規則を決めて、それに則ってデータを構成して送受信する必要があります(プロトコル)。例えば、ASCII文字で改行文字やヌル文字を区切りにするとか、データの開始記号-データのサイズ-データ本体-チェックサム-終了記号を送るとか、いろいろなパターンはあるでしょう。基本は送信側と受信側で合意していればよいのですが、通信状況によるデータの欠落などにどう対応するか等も考えるといろいろ付加情報をつけたりすることになります。

今回程度の話であれば、
送信側は、送信データの(有効な)文字数を得て、それだけを送り、さらに改行をつける。
受信側は、read_until readline()関数で改行までをひとかたまりとして受信する。
ぐらいで対応してみてはいかがでしょう。

投稿2021/09/05 01:46

編集2021/09/05 23:23
thkana

総合スコア7607

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oniko

2021/09/05 13:24

ありがとうございます。とても勉強になります。read_until()関数というのは初めて聞きました。まずは伺った内容をきちんと理解できるように、調べていくことから始めます。
thkana

2021/09/09 22:37

> x[4]でうまく行きました 今回はうまくいったということなんだけど、それじゃ本当はいけないかもしれないということは、一度どこかで痛い目をみたら思い出してくださいね。 http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/lame-list.html#item20 (Winsockが題材だけどソケット一般として同じ話。古いページだけど、いまでも事情は変わらない)
guest

0

ベストアンサー

python側でdata = client.recv(BUFSIZE)で受信したデータ全体を確認してみてください。
おそらくですが受信データ長は256で冒頭には数値文字列、終端コードであるb"\x00"があり、それ以降ゴミデータが入っているのではないかと考えられます。

>>> a = b"100\x00\xa5" >>> a.decode("utf-8") Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa5 in position 4: invalid start byte

といった具合にエラーになるのではないかと考えられます。

送信側で送信データ長を文字列長にするか
受信側で終端コード以降のデータを切り捨ててください。


2021-09-06追記

送信側で送信データ長を文字列長にするか

send(network_socket, &x, sizeof(x), 0);の第三引数sizeof(x)strlen(x)に変更します。
xのサイズを変えるのはデータの桁数(文字数)が変わったときに対応できないのでこちらを推奨します。

受信側で終端コード以降のデータを切り捨ててください。

現状はdataの長さが256になっていると思われますが有効な文字列長になるように変数の内容を加工してあげればよいです。findを使って終端コードの位置を求め、そこまで切り詰めます。

以下はpython3ですが2でも多分同じなはず。

>>> a=b"100\x00\xa5" >>> index=a.find(b"\x00") >>> index 3 >>> if index!=-1: ... a=a[:index] ... >>> a b'100' >>> a.decode("utf-8") '100'

全く知識がなくこれを実現する見当がつきません。必要となる前提知識として何が必要になるのか良ければ教えていただけますでしょうか。

難しい質問ですね。
今回の場合は言語仕様、特に配列、文字列の取り扱いを知っていればできると思います。
ただ、言語仕様をあらかじめすべて把握することは難しいことです。
問題にぶつかったときに解決して覚えていけばいいと思います。

あと、質問の際、初心者アイコンをつけておくと最初からコードで修正内容を案内してもらえる確率が高くなるかもしれません。

投稿2021/09/04 15:36

編集2021/09/05 15:32
nomuken

総合スコア1627

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oniko

2021/09/04 22:46

なるほどそうなのですね。ご回答ありがとうございました。int型でクライアントからサーバを送ると、サーバ側ではprintfでの出力は空白になってしまうのですが、文字列をおくるときちんと表示されたので文字列型で送らなければならないのかな?と思い、sprintf()をもちいてint型を一旦文字列型に変換してみました。 ただ、これだと完全な文字列型には変換されていないということでしょうか?これによって送信データ長が文字列長になっていないということなのでしょうか。
nomuken

2021/09/04 23:28 編集

> ただ、これだと完全な文字列型には変換されていないということでしょうか? C言語の文字列としては正しいです。 ただし、sprintfは終端文字より後ろは設定しないため、下地のデータが残ります。これがゴミデータと言っているところです。 ソケットでデータをやり取りするとき、データは文字列ではなくバイト列でやり取りします。 送信データサイズが文字列長ではなくバッファ長になっているのでゴミデータが一緒に送信されます。 受信側ですが受信データを文字列ではなくバイト列として受け取ります。 pythonの`バイト列.decode("utf-8")`としたとき、途中で\x00を見つけてもそこで変換を打ち切ってくれるわけではないようなのでゴミデータを参照したとき、エラーになります。 >これによって送信データ長が文字列長になっていないということなのでしょうか。 送信側のコードが`send(network_socket, &x, sizeof(x), 0);`とかいてあり、xは char[256]なのでsizeof(x)は256ですね。xの中身がどのようなデータかは関係ないです。
oniko

2021/09/05 13:15

ありがとうございます。そもそも\x00におけるxというのが16進数を表しているということすら今回知りませんでした。とりあえず、x[4]でうまく行きました。迅速な回答ありがとうございました。とても勉強になります。
oniko

2021/09/05 13:18

ちなみに「受信側で終端コード以降のデータを切り捨ててください。 」とのことでしたが、ネットではどのようなワードをもとに調べてみるとよいでしょうか。全く知識がなくこれを実現する見当がつきません。必要となる前提知識として何が必要になるのか良ければ教えていただけますでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問