dポイントプレゼントキャンペーン実施中!

C言語でソケット通信のプログラムを始めて作成しました。

受信時のデータ量が少ない場合は問題ないのですが、データ量が多くなると、
サーバーの受信が、出来たり、出来なかったりと不安定になります。

ログを確認しますと、パケットが勝手に分割されサーバーで受信した場合に、
おかしな動きになることがわかりました。
なぜそのようになるのか、また何がいけないのかわかりません。
どうぞご教授をお願いします。

FD_ZERO(&r_socket);
FD_SET(sock, &r_socket);
width = sock+1;

*leng = 0;
ef = TRUE;
memset(&s,0x00,sizeof(s));

/* タイムアウト設定 */
time.tv_sec = 3;
time.tv_usec = 0;

while(1) {

/* 受信待機 */
ret = select(width, &r_socket, NULL, NULL, &time)
if ( ret < 0 ) {
"受信待機エラー"表示
break;
}

if ( ret = 0 ) {
"タイムアウトエラー"表示
break;
}

/* パケットを受信 */
if ( FD_ISSET(sock, &r_socket)) {
memset(&rb,0x00,sizeof(rb));
read_len = read(sock, rb, sizeof(rb));

"受信した内容をログに出力"

/* 割り込み中断エラーは継続し、その他エラーは終了する */
if ( read_len < 0 ) {
if ( errno == EINTR ) {
continue;
} else {
ef = FALSE;
"受信エラー"表示
break;
}
}

/* クライアントのソケットが閉じた場合は終了する */
if ( read_len == 0 ) {
break;
}

/* データ送信要求を受信した場合は終了する */
if ( rb[read_len-1] == ACK ) {
break;
}

/* 受信バッファーへ取り込み */
if ( rb[read_len-1] == ETX || rb[read_len-1] == ETB ) {
len = read_len - 1;
} else {
len = read_len;
}

for (i = 0;i < len;i++) {
if ( rb[i] != ETB ) {
rsbuf[(*leng)++] = rb[i];
}
}

/* 最終データ時は終了する */
if ( rb[read_len-1] == ETX ) {
break;
}

/* 最終データでない場合はデータ送信要求を送信する */
if ( rb[read_len-1] == ETB ) {
sprintf(trxmsg,"%1c",ACK);
ef = send_socket(sock, strlen(trxmsg), trxmsg);
if ( ef != TRUE ) {
break;
}
}
}
}

A 回答 (5件)

とりあえず


「サーバーの受信が、出来たり、出来なかったりと不安定になります」
のところ, もっと正確に「どうなるか」を書けませんか? 「こんなデータを送ったはずなのに受け取ってみるとこんな風になってる」というのがあればもっと判断しやすいんだけど....

あと #3 あたりの指摘だけど, たぶん
「rb[read_len-1] == ACK 」とか「rb[read_len-1] == ETX」とかの条件が「本当の最後」以外に成り立ってしまうことはないのか
ということじゃないかなぁ.

この回答への補足

いろいろありがとうございます。

クライアント側で送信データ(1)、(2)、(3)のどれか、または全部があるとすると、
1.送信データ(2)、(3)が
  ある場合、送信データ(1)+ETB
  ない場合、送信データ(1)+ETX
  送信データ(1)を送信

2.送信データ(3)が
  ある場合、送信データ(2)+ETB
  ない場合、送信データ(2)+ETX
  送信データ(2)を送信

3.送信データ3+ETX
  送信データ(3)を送信

サーバー側の受信が完了したかを確認せず、
送信処理が正常終了したら、つぎの送信データを送信していました。

それを、
1.サーバーに送信データ(1)送信
2.送信データ(1)の制御文字がETBなら、クライアントに対し、ACKを送信
3.クライアントでACKを受信後、送信データ(2)を送信
4.送信データ(2)の制御文字がETBなら、クライアントに対し、ACKを送信
5.クライアントでACKを受信後、送信データ(3)を送信

に修正したら、正常に送受信するようになりました。
まだ、穴があるかもしれませんが、とりあえず、このままいこうかな?!
と思います。
なにか、不備等がありましたら、指摘のほど、宜しくお願いします。

補足日時:2012/03/20 00:42
    • good
    • 0
この回答へのお礼

大変長い間、ご回答を頂きましてありがとうございます。

お礼日時:2012/03/22 11:51

>受信データの受信長の最終桁で判断しています。



受信データの最終桁以外に制御文字が入っているかもしれませんよ。

この回答への補足

例えば100バイトデータ+制御文字で
送信した場合、受信時に数回で受信しても
最後の受信時の最終文字は制御文字が
入ってくるものだと思っていますが、
違うのでしょうか?

補足日時:2012/03/18 16:27
    • good
    • 0

>まさに案2のように作成したつもりなんですが…



受信データの先頭から順番に制御文字かどうか調べているコードが、質問文に含まれてませんが?

どこでそれをしているつもりですか?

この回答への補足

先頭から順番に制御文字はチェックしていません。
rb[read_len-1] == ETX
受信データの受信長の最終桁で判断しています。

補足日時:2012/03/18 14:54
    • good
    • 0

No1です。



>末尾が終了文字でない場合、ループを抜けないようにしているつもりですが、

何の末尾?
送信時の末尾と受信時の末尾は一般に一致しないと書いたのですが、わかりませんか?

例えば、送信側で100バイトのデータを用意して、100バイト目に終了を示すコードを入れて送ったとして、受信側で一回のreadシステムコールで50バイトしか受信できなかった場合、50バイト目を見ても無意味ですよね?

可変長のデータであれば、
案1: 先頭にデータ長を示すものを入れて送り、受信側では最初にデータ長をみて必要なバイト数だけ受け取るまでreadを繰り返す
案2:データに含まれ得ないバイト(またはバイト列)を終わりの印にする。例えばデータが0x20~0xFF だとすると、お書きのようなETXとかETBなど0x00~0x1Fの範囲の制御文字を使う。この場合、受信データの先頭から見ていって、制御文字が出てくるまでreadを繰り返す

この回答への補足

ありがとうございます。

データの末尾が送信時と受信時に違う場合が
あることはわかります。
ですから、終了文字まで繰り返すようにしてある
つもりです。まさに案2のように作成したつもり
なんですが…

何が悪いのでしょうか?

補足日時:2012/03/18 13:47
    • good
    • 0

もしかして、例えば100バイト送信すると、受信側で一度に100バイト受け取れると思っていませんか?



一度の受信は何バイトか決まっていませんので、受信データ長を見て、100バイトに満たなければ繰り返し受信してデータを継ぎ足さないといけませんよ。

つまり、何か、受信データの末尾を見ているようですが、それは送信データの末尾とは限らないと言うことです。

この回答への補足

ご回答ありがとうございます。

全ての受信が終了するまで、データを継ぎ足しはしています。
 → rsbuf[(*leng)++] = rb[i];
一文字づつ、別の項目への対比をしています。

データ長が可変のため受信データの末尾で、終了確認をしています。
無限ループのなかで、末尾が終了文字でない場合、
ループを抜けないようにしているつもりですが、繰り返されていないのでしょうか?

補足日時:2012/03/18 10:51
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!