回答編集履歴

3

通信の話追記

2019/09/13 14:18

投稿

thkana
thkana

スコア7639

test CHANGED
@@ -63,3 +63,105 @@
63
63
  `send(sock, buf, strlen(buf),0);`
64
64
 
65
65
  としましょう。
66
+
67
+
68
+
69
+ ---
70
+
71
+ 通信の話
72
+
73
+
74
+
75
+ 通信ってのは、送る側の誰かさんと、受ける側の誰かさんとの間での情報のやり取りです。双方向通信ってのもありますけど、これは送りと受け、受けと送りの2系統を同時に行っているだけなので特別視するものではありません。
76
+
77
+
78
+
79
+ 通信で大事なのは送る側と受ける側で、どういうデータをどういう手順で送るかがしっかりと合意されていること。送る側が日本語で喋って、受ける側が英語のつもりで聞いてたら「通信」が成り立ちませんよね。まずはそんな話。コンピュータの場合は、「データ」をやり取りするのでもっとちゃんと決めておかないと簡単に破綻します。
80
+
81
+
82
+
83
+ 通信にもいろいろな方法がありますが、とりあえずTCP/IPに絞りましょうか。
84
+
85
+ TCP/IPの通信でなにより忘れてならないのは、「通信の単位は1バイト」それ以上ではない、ということ。どういうことかというと、例えば'H' 'e' 'l' 'l' 'o' と送信したときに、通信の単位は1バイトなので、受け取る側では'H'と'e'と'l'と'l'と'o'はバラバラだということ。もう少し具体的な現象としてみると、
86
+
87
+ send(sock, "Hello", 5, 0);を実行すると、通信ラインには'H' 'e' 'l' 'l' 'o'が順次送り出されます。これをchar buf[128]; read(sock, buf, 128);で受信するとします。受信側ではbuf[0]に'H',buf[1]に'e', buf[2]に'l', buf[3]に'l', buf[4]に'o'が受信されると期待...してはいけません。送り側が3文字目の'l'を送信中だったり、通信回線の遅延でデータがまだ届いていない場合にはbuf[0]に'H',buf[1]に'e'、そこでread()関数が終わりになるということは普通にあって対応しなければいけない事態です。なお、それが当然なので、対応するためread()は受信した文字数を戻り値に返してきます。
88
+
89
+ また、
90
+
91
+ send(sock, "Hello",5,0);
92
+
93
+ send(sock, " World",6,0);
94
+
95
+ と送信したときには同様に、read(sock, buf, 128);一回で"Hello"が、二回目に" World"が受信出来るという期待を持ってはいけません。1回目のread()で"He"、二回目で"llo World"が受信出来たり、あるいは一回目で"Hello w"、二回目で"o"しか受信できない、何ていう事態も決して異常ではありません。
96
+
97
+
98
+
99
+ では、"Hello"をひとかたまりとして「通信」するためにはどうすればいいでしょう...いくつかの方針があります。
100
+
101
+
102
+
103
+ A. 通信全てNバイトの塊、ということに送受両側で取り決めておく
104
+
105
+ (read()の結果がNバイトに満たなかったら受信したデータは溜めておいて、さらに受信する。受信したデータを足してNバイトになれば結果を出力する。ここで、Nバイト分を超えてデータがあったら「次」のデータなので受信したデータとして溜めておく必要がある)
106
+
107
+
108
+
109
+ B. 送る側が「これからNバイト送ります」という情報を先頭に付加し、受信側はそれを受けて、受信したデータからNバイトを一塊として取り出す
110
+
111
+ (最初に「Nバイト分」というデータを受け取る手続きはAに準じることが多い。そこでNが決まったら以下はAと同様)
112
+
113
+
114
+
115
+ C. 送る側が「データ終了」のマークを付加し、受信側はそのマークを受けたらデータを一塊として受信を終了する
116
+
117
+ (受信したデータは次々溜めていく。受信したデータを監視して、データ終了マークが登場したらそこで結果を出力する。データ終了マーク以降もデータがあれば、それは次のデータなので溜めておく)
118
+
119
+
120
+
121
+ このような規則を、read()で受信したデータを処理することで得るような上位の処理を使用します。
122
+
123
+
124
+
125
+ 今回は、受信側にrubyのgetsメソッドを使っています。これは、改行(MS-DOS/Windows系だとCR(\xd)LF(\xa)の2バイトシーケンス)をデータ終了マークとしたCタイプを期待しています。なので、送る側のプログラムは期待されたデータを送らなければいけません。
126
+
127
+
128
+
129
+ さて、例えばchar buf[10];に対し、ファイル(やパイプ)からデータ"Hello\n"をfgets()で取得したら、buf[10]の中身はどうなるでしょう。
130
+
131
+ buf[0]='H' buf[1]='e' buf[2]='l' buf[3]='l' buf[4]='o' buf[5]='\xd' buf[6]='\xa' buf[7]:'\0' (ここまではfgets()で値を設定。以降はたまたまメモリーにあった値) buf[8]:不定(例えば0xc0) buf[9]:不定(例えば0xc1)
132
+
133
+ で、これをsend()で送るわけです。send(sock, buf, sizeof(buf),0);で送ったら
134
+
135
+ 'H' 'e' 'l' 'l' 'o' '\xd' '\xa' '\0' '0xc0' '0xc1'
136
+
137
+ の10バイトが送られます。
138
+
139
+ これを受信した側では
140
+
141
+ 'H' 'e' 'l' 'l' 'o' '\xd' '\xa' までをfgetsの戻り値として返し、またそれ以降の'\0' '\xc0' '0xc1'は「次のデータ」として溜め込みます。
142
+
143
+ 引き続き、送る側が"World\xd\xa"を送ったとしましょう。受信側でfgets()が返すデータは、先のデータと合わせて
144
+
145
+ '\0' '0xc0' '0xc1' 'W' 'o' 'r' 'l' 'd' '\xd' '\xa'
146
+
147
+ となります。「通信」で伝えられるべきデータは'W' 'o' 'r' 'l' 'd''\xd' '\xa' だったはずなので、'\0' '0xc0' '0xc1'があるのは明らかにおかしいことです。で、この'\0' '0xc0' '0xc1'のようなゴミデータを表示したのがあなたのいう「文字化け」です。別に化けているわけでもなんでもないです。あなたが、余計なデータを送り込んでいるだけ。
148
+
149
+ ゴミデータは送り込んではいけません。では、'H' 'e' 'l' 'l' 'o' '\xd' '\xa' '\0' '0xc0' '0xc1'からどれだけ送ればいいのか...'\0'の前まで。そこまでのデータ量(バイト数)を数えてくれるのは? strlen()関数です。
150
+
151
+ strlen()+1にしたら、'\0'も送られます。'\0'は画面上表示はされないから送ってもいい、ですか? 元のデータになかったデータですからやっぱりダメです。
152
+
153
+ (もちろん、C言語のプログラム同士が通信する等の事情で、Cタイプの通信の終了マークを'\0'にしよう、という取り決めをしたのなら話は違います。)
154
+
155
+
156
+
157
+ ここで、配列bufの大きさはなにかに影響したでしょうか。していません。データバッファは送るべきデータを十分に格納出来る(データのサイズより大きい)ものでさえあればいいのです。送るデータサイズに配列のサイズをあわせなければいけない、なんてことは全くありません。あなたが、
158
+
159
+ ```C
160
+
161
+ char mojiretu[] = "HelloWorld";
162
+
163
+ send(sock,mojiretu,sizeof(mojiretu),0);
164
+
165
+ ```
166
+
167
+ としたときに余計なデータが表示されなかった、その状況の意味を誤解しただけ。この場合はsizeof(mojiretu)がstrlen(mojiretu)+1になりますけど、それが正しいデータサイズでないのは既に説明した通り。

2

誤り修正

2019/09/13 14:18

投稿

thkana
thkana

スコア7639

test CHANGED
@@ -46,13 +46,13 @@
46
46
 
47
47
 
48
48
 
49
- 不定,不定,不定,... の部分が、あなたの言う「文字化け」です。
49
+ '\0',不定,不定,不定,... の部分が、あなたの言う「文字化け」です。
50
50
 
51
51
 
52
52
 
53
53
  この場合、送らなければいけないのは、そうじゃなくて、
54
54
 
55
- 'H''e''l''l''o''\0'
55
+ 'H''e''l''l''o'
56
56
 
57
57
  までのはず。
58
58
 

1

「解答」追記

2019/09/08 03:07

投稿

thkana
thkana

スコア7639

test CHANGED
@@ -13,3 +13,53 @@
13
13
  ```
14
14
 
15
15
  あなたの送信したいデータサイズは本当にsizeof(buf)バイトですか?
16
+
17
+
18
+
19
+ ---
20
+
21
+ 追記。
22
+
23
+ そういえば最近「Cの文字列ってのは実体は配列で」なんて話をしたんでしたっけ。だとすると、自力での理解はまだ難しいかな...
24
+
25
+
26
+
27
+ `char buf[128];`
28
+
29
+ に、文字列"Hello"を格納してみましょうか。配列の各要素の内容は
30
+
31
+ buf[0]:'H', buf[1]:'e', buf[2]:'l', buf[3]:'l', buf[4]:'o', buf[5]:'\0', buf[6]:不定, buf[7]:不定, buf[8]:不定, <略> , buf[127]:不定
32
+
33
+ となっています。
34
+
35
+
36
+
37
+ その状況で、
38
+
39
+ `send(sock, buf, sizeof(buf),0);`
40
+
41
+ はどういうデータを送れと言っているでしょう。
42
+
43
+
44
+
45
+ 'H''e''l''l''o''\0',不定,不定,不定,...(全部で128バイト分)
46
+
47
+
48
+
49
+ 不定,不定,不定,... の部分が、あなたの言う「文字化け」です。
50
+
51
+
52
+
53
+ この場合、送らなければいけないのは、そうじゃなくて、
54
+
55
+ 'H''e''l''l''o''\0'
56
+
57
+ までのはず。
58
+
59
+
60
+
61
+ であれば、そのようなデータサイズを求める関数があります。
62
+
63
+ `send(sock, buf, strlen(buf),0);`
64
+
65
+ としましょう。