転送エラー


caps  2005-08-03 08:49:22  No: 58498

現在、VC++.net、winsock2.0をつかって、コンソール上で動く、バイナリファイルを転送するプログラムを作成しています。
サーバ側(ファイルを送るほう)とクライアント(受け取るほう)をつくって、クライアントが接続するIPを127.0.0.1で実験してみると、うまくファイルを転送することができました。ですが、ほかの人のPCと接続してみると(IPはかえています)、接続はできて、チャットのように文字の送受信はできるのですが、ファイルの転送がうまくできません。うまくできないというのは、30kバイトぐらいのjpgを転送するのは成功したのですが、1Mをこえるbmpを転送すると、相手(クライアント)のPCからエラー音がでて、プロンプト上におそらく画像の内容をしめしているかのような、文字がでます。
プログラムは、ファイル情報をいれたヘッダーを先に送信してその後に、中身をおくるようにしています。ファイルのサイズが1M以上の場合、1Mずつ配列にいれていくようにしています。
これが、サーバ側のプログラムです。
  while(header2.fsize>0){
    if(header2.fsize<=1000000){
      res=fread(data,sizeof(char),header2.fsize,fp2);
      send(c,(char *)&data,header2.fsize,0);
      if(res==SOCKET_ERROR) error("ファイル送るエラー2\n");
      break;
    }
    else{
      fread(data,sizeof(char),1000000,fp2);
      res=send(c,(char *)&data,1000000,0);
      if(res==SOCKET_ERROR) error("ファイル送るエラー2\n");
      header2.fsize=header2.fsize-1000000;
    }
  }
  fclose(fp2);
  printf("ファイル送信完了\n");
これがクライアント側のプログラムです。
  while(header.fsize>0){
  if(header.fsize<=1000000){
    nFileSize=header.fsize;
    pFileBuffer=(char *)calloc(nFileSize,sizeof(char));
    res=recv(finger, pFileBuffer, nFileSize, 0);
    if(res==SOCKET_ERROR) {error("ファイル受信エラー2\n"); break;}
        fwrite(pFileBuffer,sizeof(char),nFileSize,fp);
  break;
  }
  else{
    pFileBuffer=(char *)calloc(1000000,sizeof(char));
    res=recv(finger,pFileBuffer,1000000,0);
    if(res==SOCKET_ERROR){error("ファイル受信エラー2\n");break;}
    fwrite(pFileBuffer,sizeof(char),1000000,fp);
    header.fsize=header.fsize-1000000;
    }
  }
  fclose(fp);
アドバイスよろしくお願いいたします。


KING・王  2005-08-03 16:52:22  No: 58499

クライアント側のrecv()で、1回で1,000,000バイトのデータを受信していますが、
本当に1回で受信できていますか?

たしかrecv()の仕様で、送信側のパケットが分割されたりすると、
受信側では、指定したバイト数以下しか受信できない状態でも、recv()から
制御が戻ったと思います。

recv()の戻り値(res)が、実際に受信できたバイト数となるので、
一度戻り値の値を確認してみて下さい。

#外していたら、ごめんなさい。


YuO  2005-08-03 19:25:01  No: 58500

どこでどういう値になっているか,ログを吐くなりして調べてみてはどうでしょうか。

KING・王さんが書かれているほかにも,
・fread
・fwrite
・send
などの戻り値をちゃんと使う必要があります。
# どれも,指定量の読み書きを行うとは限らない。


caps  2005-08-04 08:17:42  No: 58501

返信ありがとうございます。
戻り値をすべてしらべてみたところ、1000000バイト以上のファイルを送ったときに受信側のrecvはちゃんと1000000という数字を返していたので、正常に受信できているとおもわれます。2,359,350 バイトのファイルを送ったところ、最後のsendは359350をかえしていたので、1000000おくって、1000000おくって、最後にのこったものをすべておくれているので、正常におくれているとおもわれます。fwriteも1000000受信したときは、1000000を返していました。
受信側の2359350バイトのファイルをおくるときに、デバッガをしようしてfwriteが終わった瞬間にとめて、ファイルがつくられているフォルダの中のファイルの容量をプロパティでみると、最初が999424バイトで、次が1998848バイト、fwriteの位置をかえて(止める位置をかえて)次が2359296バイトで、そのあとに続行を押すと、2359350バイトとなります。これは正しいのでしょうか?自分の考えでは1000000バイト、2000000バイト、2359350バイトとなるとおもったのですが・・。ちなみに、プロパティではサイズ:というところをみています。


YuO  2005-08-04 10:15:05  No: 58502

1000000バイトという中途半端なサイズで書き込むからでしょう。
VC++の実装を詳しく調べたわけではないですが,
内部のバッファリングが2KB(=2048バイト)や4KB(=4096バイト)単位であれば,
そのような結果が出ます。

ちなみに,ファイルのバッファリングは,
効率上の問題から2の累乗で行われていることが多いです。
例えば,NTFSでは通常クラスタサイズが4KBですから(2GB以上のディスクのデフォルト値>KB314878),
その倍数でバッファリングすることが効率的になります。
# ライブラリだけでなく,ドライバがバッファリングを行う可能性もありうる。


caps  2005-08-05 00:06:33  No: 58503

2の累乗である。4096でやってみました。ファイルサイズは正常に4096,8192・・・とふえていくようになったのですが、やはり相手にはうまくおくれませんでした。
気づいたことがあるのですが、送信側で、上のプログラムのwhileの下側のsendを実行しおわったら、受信側ではrecvでデータをうけとって、そのあと1行のプログラムを実行して(エラー処理)、fwriteを実行しています。この、うけとって、1行のプログラムを実行して書き込むという作業の間に、送信側ではwhileによって何度もsendが実行されているのではないかとおもわれます。
これを改善するために、受信側で書き込みが完了したらaという文字列を送信側におくって、送信側はaという文字列がきたら、send文以下を実行するみたいにしてみようとおもったのですが、このプログラムはチャット機能もつけているので、文字列受信専用のスレッドをたてていて、whileでまっている状態にしているので、そっちのスレッドのほうで文字列をうけとってしまいます。なので、この方法では実現できないとおもわれます。
どのような方法で送信側のsendをとめておけばいいものでしょうか?


KING・王  2005-08-05 03:25:51  No: 58504

少し疑問に思いましたが、この通信に使用しているのは、TCPですか、UDPですか?

UDPならrecv()されないまま、次々にsend()すると、途中でデータが失われるのは、
仕様だと思われます。

TCPにすれば、大丈夫だったと思いますが。。。

#最近ネットワーク関係のプログラムから遠ざかっているので、識者の方々、フォローをお願いします。


caps  2005-08-05 04:05:45  No: 58505

TCPで通信しています。
では、ほかのどこかに原因があるのでしょうか?
このあたりしか、原因はかんがえられないのですが。
それから、自分でやったときには正常にできることが、気になります。なぜほかの人との通信ではうまくいかないのか・・


isshi  2005-08-05 05:53:39  No: 58506

>戻り値をすべてしらべてみたところ、1000000バイト以上のファイルを送った
>ときに受信側のrecvはちゃんと1000000という数字を返していたので、正常に
>受信できているとおもわれます。
これを確認したときにもエラー音(?)は出たのでしょうか?
そもそもエラー音とは何ですか?ソースのどこを実行中に鳴るのですか?

KING・王さん、YuOさんが指摘しているように、WinSock APIの戻り値を
ちゃんと使ってますか?
> res=recv(finger,pFileBuffer,1000000,0);
> if(res==SOCKET_ERROR){error("ファイル受信エラー2\n");break;}
> fwrite(pFileBuffer,sizeof(char),1000000,fp);
res が 1000000 になるとはかぎりません。

 fwrite(pFileBuffer,sizeof(char),res,fp);
とするべきです。


caps  2005-08-07 03:14:17  No: 58507

isshiさんがおっしゃられたように戻り値をつかってみても、うまく送信できませんでした。なぜか途中で止まってしまいます。
送信側はこのようにしてみました。
  while(header2.fsize>0){
  if(header2.fsize<=524288){
    res=fread(data,sizeof(char),header2.fsize,fp2);
    res=send(c,(char *)&data,res,0);
    if(res==SOCKET_ERROR) error("ファイル送るエラー2\n");
      break;
    }
    else{
      res=fread(data,sizeof(char),524288,fp2);
      printf("%d\n",res);
      res=send(c,(char *)&data,res,0);
      printf("%d\n",res);
      if(res==SOCKET_ERROR) error("ファイル送るエラー2\n");
      header2.fsize=header2.fsize-524288;
        
      }
    
    }
受信側はこのようにしてみました。
while(header.fsize>0){
  if(header.fsize<=524288){
    nFileSize=header.fsize;
    pFileBuffer=(char *)calloc(nFileSize,sizeof(char));
    res=recv(finger, pFileBuffer, nFileSize, 0);

    if(res==SOCKET_ERROR) {error("ファイル受信エラー2\n");                       break;}
      
        res=fwrite(pFileBuffer,sizeof(char),res,fp);

       
    break;
  }
  else{
    pFileBuffer=(char *)calloc(524288,sizeof(char));
    res=recv(finger,pFileBuffer,524288,0);
    if(res==SOCKET_ERROR){error("ファイル受信エラー2\n");break;}
    res=fwrite(pFileBuffer,sizeof(char),res,fp);
printf("%d\n",res);
    header.fsize=header.fsize-524288;
    }
で、分かったことがひとつあります。それは、ほかの人とファイルの送受信をおこなったときに、失敗したときに受信側にできる、ファイルの容量が違うということです。
もし、どこかまちがっていたら、普通、同じところでとまるので、できるファイルは同じのような気がするのですが・・。
ちなみに、15kバイトぐらいの小さいファイルのやり取りはできます。1Mをこえるとダメみたいです。
アドバイスよろしくおねがいします。


isshi  2005-08-07 05:45:16  No: 58508

> header.fsize=header.fsize-524288;
 header.fsize = header.fsize - res;
とする。

send の戻り値も 524288 となるとは限りません。

また、
> if(header.fsize<=524288){
としていますが、524288以下かどうかで場合分けする必要はありません。
else 節のコードでどちらの場合にも対応できます。
送信側も同じです。

>もし、どこかまちがっていたら、普通、同じところでとまるので、できるフ
>ァイルは同じのような気がするのですが・・。
send や recv の戻り値が毎回同じとは限りません。
今回のソースでは、まだ適切に戻り値を使っていない個所があるため
そのような現象が出ているのだと思います。

あと、前から気になってましたが、calloc に対応する free が
ありませんが、大丈夫ですか?


caps  2005-08-08 05:33:33  No: 58509

分かりました。訂正してみます。
callocをfreeするのはわすれていました。
524288以下で場合わけしなくていいのですか?もしelseのほうだけだと、freadで、524288よりもファイル容量が少ない場合も524288バイトよみこんでしまうのではないでしょうか?これでは、受信したファイルの容量が元のものより大きくなる可能性がありませんか?


isshi  2005-08-08 08:10:38  No: 58510

>もしelseのほうだけだと、freadで、524288よりもファイル容量が少ない場合
>も524288バイトよみこんでしまうのではないでしょうか?
recv の第3引数はバッファの最大サイズを指定しているだけで、受信するデータのサイズを
指定しているのではありません。
recv が指定したバッファをフルに使うとは限りませんし、受信データが少なければ、
残りのバッファは使われないだけです。
だから、戻り値をちゃんと使ってくださいと言っているのです。


isshi  2005-08-08 17:51:44  No: 58511

すみません。送信側についてうそを言っていましたので訂正します。

>send の戻り値も 524288 となるとは限りません。
これは非ブロッキングモード(nonblocking)の場合でした。
今回はおそらくブロッキングモードと思われるので、今のソースのままでよいようです。

>送信側も同じです。
これも私の勘違いでした。送信側は今のままで良いです。
(ただし、明らかに if else ブロックがずれている部分がありますので
そこは修正が必要です。)


YuO  2005-08-08 18:45:05  No: 58512

> >send の戻り値も 524288 となるとは限りません。
> これは非ブロッキングモード(nonblocking)の場合でした。
> 今回はおそらくブロッキングモードと思われるので、今のソースのままでよいようです。

えーっと,この話のソースはどこでしょうか。

http://msdn.microsoft.com/library/en-us/winsock/winsock/send_2.asp
によると,sendの戻り値はlenより小さいことがある,としか書いていません。

blocking modeで戻り値はlenと等しい,ということが仕様として書かれているのでなければ,
戻り値はlen未満であることを想定してコードを書くべきではないでしょうか。

http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/lame-list.html
の項目20も参考にどうぞ。


isshi  2005-08-08 19:18:21  No: 58513

>えーっと,この話のソースはどこでしょうか。
申し訳ありません。
MSDNライブラリ2005年1月リリースですが、見ていたところが Windows CE 5.0
の send でした。そこには、
>If no error occurs, this function returns the total number of bytes 
>sent, which can be less than the number indicated by len for 
>nonblocking sockets.
と書いてありました。
もっともblockingモードのときにlenと等しくなるとは書いてありませんが。

>blocking modeで戻り値はlenと等しい,ということが仕様として書かれているのでなければ,
>戻り値はlen未満であることを想定してコードを書くべきではないでしょうか。
はい、私はそのように書いておりましたが、確認のためMSDNを見たところ間違って
CE のところを見てしまいました。

>http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/lame-list.html
>の項目20も参考にどうぞ。
これも以前見たことがあります。

ということで、私の 2005/08/08(月) 08:51:44 の発言は無視してください。
失礼しました。


isshi  2005-08-08 19:34:51  No: 58514

>MSDNライブラリ2005年1月リリースですが、見ていたところが Windows CE 5.0
>の send でした。
ネット上にもありましたのでURLを載せておきます。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecomm5/html/wce50lrfsend.asp


caps  2005-08-08 22:36:33  No: 58515

みなさん返信ありがとうございます。
送信側と受信側に送信、受信するたびに、容量を表示させてみると、ごくたまに、524288よりも小さい容量が表示されることがありました。
送信側をこのようにしました。
  while(header2.fsize!=0){
    res=fread(data,sizeof(char),524288,fp2);
    //printf("%d\n",res);
    res=send(c,(char *)&data,res,0);
    //printf("%d\n",res);
    if(res==SOCKET_ERROR) error("ファイル送るエラ2\n");
    header2.fsize=header2.fsize-res;
  }
受信側をこのようにしました。
  while(header.fsize!=0){
    pFileBuffer=(char *)calloc(524288,sizeof(char));
    res=recv(finger,pFileBuffer,524288,0);
    if(res==SOCKET_ERROR){error("ファイル受信エラー2\n");break;}
    res=fwrite(pFileBuffer,sizeof(char),res,fp);
    //printf("%d\n",res);
    header.fsize=header.fsize-res;
    free(pFileBuffer);
  }
これで、1M,2Mぐらいのa.bmpというファイルを正常に送信、受信できるようになりました。ですが、27Mぐらいのa.mp3というファイルを送信すると、(タスクマネージャーで確認すると)9Mぐらいまでは正常に送受信できているのですが、それからまったくの無通信状態(回線使用率0)になってしまいました。
これはやはりファイルのサイズが原因なのでしょうか?拡張子が原因とはかんがえられませんよね?


とおり  2005-08-08 22:54:11  No: 58516

>これはやはりファイルのサイズが原因なのでしょうか?拡張子が原因とはかんがえられませんよね?

あなたが正常/異常を判断しているのは、ファイルに保存してからなのですか?
送受信そのものはネットワークとメモリ上だけの話で、拡張子とは全く無関係だと思いますが。
ファイルへの読み書きに問題があるのでしたら、根本的に別件の話です。
今回のように原因が自分でよくわからないような場合には、複数の要素をいっぺんに見ようと
しては駄目です。
混乱するだけ。
ファイルへの読み書きという要素を排除して、通信だけを見て下さい。
(ファイル読み書きに問題があったりして…)


dairygoods  2005-08-08 23:03:19  No: 58517

>    while(header2.fsize!=0){
>        res=fread(data,sizeof(char),524288,fp2);
>        //printf("%d\n",res);
>        res=send(c,(char *)&data,res,0);
>        //printf("%d\n",res);
>        if(res==SOCKET_ERROR) error("ファイル送るエラ2\n");
>        header2.fsize=header2.fsize-res;
>    }

ファイルからは、524288バイト読み込めたけど、
524288バイトsendできなかった場合、
次のループでどうなるか良く考えてみてください。


caps  2005-08-08 23:16:06  No: 58518

返信ありがとうございます。
正常というのはファイルを受信できて、そのファイルの中身も送っているものとまったく同じであるということです。わかりにくい表現ですいません。
今回おこった異常は、9Mまでしか相手が受信できずに、受信側に本来約28Mのファイルが9Mとなって保存されていました。当然、中身は見ることや聞くことはできません。
2Mぐらいのbmpを送信した時、同じファイル容量のファイルが作成され、受信したほうの中身が元とまったく同じなので、ファイル書き込みの問題ではないとかんがえられます。9Mぐらい送信した後に突然、回線使用率が0になるので、通信面で問題があるのだとおもいます。


caps  2005-08-08 23:37:56  No: 58519

dairygoodsさん、それはfreadの戻り値が524288で、sendの戻り値が0の場合はどうなるかということですよね?そうなると、header2.fsize-0となるので、header2.fsizeの値はかわらずに、freadで524288分ファイルポインタが移動しているので、この部分のファイルは送信されずに、次のファイルポインタがさしている部分を送信してしまうのでしょうか?ということは、freadの戻り値をrres,sendの戻り値をsresとすると、
if(rres!=sres){
  a=rres-sres;
  fseek(fp,a,SEEK_CUR);
 }
をsendの後にいれればいいのでしょうか?そしてheader2.fsize-sresとするのでしょうか?


とおり  2005-08-09 02:45:20  No: 58520

>if(rres!=sres){
>  a=rres-sres;
>  fseek(fp,a,SEEK_CUR);
> }
>をsendの後にいれればいいのでしょうか?そしてheader2.fsize-sresとするのでしょうか?

発想がそっちの方向に行ってしまうのか…
それでもいいんでしょうけど(バイト数とかの細かいコーディングは見てないですが)、
seekしてファイルポインタを戻してreadし直すよりは、一度読み込んだ内容を全部送りきる
までsendを繰り返して、次のreadをしないという方向の方がわかりやすいかと。


とおり  2005-08-09 02:48:52  No: 58521

補足。
もちろん何度もsendを繰り返す際、そこまでに何バイト送り終わったかを記憶しておいて、
sendに渡すポインタは、未送信データの先頭を指すように調整するわけですよ。
(調整なんて大層な話でも何でもないが^^)


dairygoods  2005-08-09 04:49:06  No: 58522

まとめて一度にやろうとせずに、
「指定サイズだけ必ず全部 send する関数」と
「指定サイズだけ必ず全部 recv する関数」を
作ると混乱しないかと思います。


caps  2005-08-09 09:33:42  No: 58523

while(header2.fsize!=0){
  rres=fread(data,sizeof(char),524288,fp2);
  //printf("%d\n",res);
  sres=send(c,(char *)&data,rres,0);
  while(rres!=sres){
    res=send(c,(char *)&data,rres-sres,0);
    //printf("%d\n",res);
    if(res==SOCKET_ERROR) error("ファイル送る2\n");
      sres=sres+res;
  }
  header2.fsize=header2.fsize-rres;
}
このようにしようとおもうのですが、6行目のsendの第二引数がこれではいけませんよね?どのようにすればいいですか?


isshi  2005-08-09 19:25:42  No: 58524

data の型は何ですか?
char data[N] と仮定すると、そもそも (char*)&data という書き方がおかしいです。
  send(c, data, ...);
あるいは、
  send(c, &data[0], ...);
とすべきです。

n バイト送信済みであれば、
  send(c, &data[n], ...);
とすればよい。


caps  2005-08-09 20:53:40  No: 58525

アドバイスどうもありがとうございます。
そのようにしてみたのですが、やはり上でかいたような状態になってしまいました。
受信側もこのようにかえてみました。
while(header.fsize!=0){
  pFileBuffer=(char *)calloc(524288,sizeof(char));
  rres=recv(finger,pFileBuffer,524288,0);
  if(rres==SOCKET_ERROR){error("ファイル受信エラー2\n");break;}
  fres=fwrite(pFileBuffer,sizeof(char),rres,fp);
  while(rres!=fres){
    res=fwrite(&pFileBuffer[fres],sizeof(char),rres-fres,fp);
    fres=fres+res;
  }
  header.fsize=header.fsize-fres;
  free(pFileBuffer);
  }
回線使用率が0になる原因をしらべたいので、ローカル以外の人と同じVisual C++.netをつかってデバッガを利用したいのですが、そのようなことはできるのでしょうか?


KING・王  2005-08-09 21:29:23  No: 58526

> 回線使用率が0になる原因をしらべたいので、ローカル以外の人と同じVisual > C++.netをつかってデバッガを利用したいのですが、そのようなことはできるのでしょうか?

少し意味がわかりかねますが、送信側PCと受信側PCの両方でVCを立ち上げ、
それぞれのソースをステップ実行しながら、デバッグしては同でしょか?
(この場合、送信側、受信側のそれぞれにVCがインストールされている必要があります。)

送信側でsend()をステップ実行した後、受信側でrecv()をステップ実行するなど、
送受信の両方をステップ実行していき、どこの時点で、希望の動作と異なる動作になるか、
デバッグするのが、解決への一番の近道のような気がします。

また、ファイルへのログ出力などを仕込み、各地点での変数の値を書き出すなどしながら、
どのような動作をしているのか見てみてはどうでしょうか?

少し気になったのですが、正しく送信できないという1Mのファイルの送信側、
受信側でそれぞれステップ実行して、希望どうりの動作をしているか、確認してみましたか。
(というか、どの時点で希望と異なるか確認してみましたか?)


とおり  2005-08-09 21:40:23  No: 58527

原因とは無関係だと思いますが、とりあえず、↑のコードに対する気になった点を。

(1)
fwriteは、エラー時にrresより少ない値を返すだけなので、whileループで回す意味はありません。
===== MSDN抜粋
fwrite は、実際に書き込まれた完全な項目の数を返します。この数は、エラーが発生した場合、count より少なくなります。また、エラーが発生した場合、ファイル位置インジケータは不定となります。
=====

(2)
各種ループ継続条件を"!="でされていますが、より安全には"<"で行うべきかと。
何かの誤算でsize値がぴったり一致にならず、実は目標値を飛び越えてしまっている可能性もあります。

(2)
recvエラー時、callocで確保した領域がリークします。

(3)
(2)とも関連しますが、外側ループの外側でcalloc/freeをすれば毎回確保/解放をしなくて済みます。
ついでに、問題点(2)も解消。


caps  2005-08-23 23:11:57  No: 58528

同じVisualC++.netをいれて、ほかの人とデバッグしながらやったのですけど、うまくデバッグできませんでした。やはり別の人とではできないようです。
で、とおりさんの意見を参考にして、送信側を
    while(header2.fsize<0){
        fres=fread(data,sizeof(char),524288,fp2);
        //printf("%d\n",res);
        sres=send(c,data,fres,0);
        while(fres!=sres){
          res=send(c,&data[sres],fres-sres,0);
          //printf("%d\n",res);
          if(res==SOCKET_ERROR) error("ファイル送るエラー2\n");
          sres=sres+res;
          
        }
        header2.fsize=header2.fsize-fres;
      
      }
    fclose(fp2);
このように変更して、受信側を
  fp=fopen(header.filename,"wb");
      pFileBuffer=(char *)calloc(524288,sizeof(char));
  while(header.fsize<0){

    rres=recv(finger,pFileBuffer,524288,0);
    if(rres==SOCKET_ERROR){error("ファイル受信エラー2\n");break;}
    fres=fwrite(pFileBuffer,sizeof(char),rres,fp);
    while(rres>fres){
      res=fwrite(&pFileBuffer[fres],sizeof(char),rres-fres,fp);
      fres=fres+res;
    }

    //printf("%d\n",res);
    header.fsize=header.fsize-fres;
    
  
   
  }
    free(pFileBuffer);

  fclose(fp);
このように変更しました。(1)でおっしゃられているfwriteの修正法がいまいちわからないのですが、アドバイスよろしくおねがいします。
それから、もしよろしければMSNメッセでアドバイスいただけないでしょうか?


isshi  2005-08-24 00:42:36  No: 58529

> while(header2.fsize<0){
> while(header.fsize<0){
不等号が逆。

受信側の
>while(rres>fres){
>  res=fwrite(&pFileBuffer[fres],sizeof(char),rres-fres,fp);
>  fres=fres+res;
>}
は、いらない。


caps  2005-08-24 02:33:01  No: 58530

そのように変更しました。
ところが、今度は前まで送信できていたものまでおくれなくなりました。ちなみに自分で送受信したらできます。
受信側のfwriteが失敗したときのエラー処理はどうすればいいのですか?
エラーがおこったときにfwriteはどのような値を返すかわからないので、何項目書き込み完了しているのかがわかりませんよね?
受信側の
header.fsize=header.fsize-fres;
は、もしfwriteでエラーがおきていたらfresという値をサイズから引いても意味がないですよね?


caps  2005-08-24 02:42:43  No: 58531

あ、fwriteは書き込みが完了している項目数を返すのですね。
間違っていました。

たとえばファイルサイズが20で、recvの戻り値が10で、fwriteの戻り値が8だとすると、ファイルサイズが20-8で12となり、次にrecvで受信してpFilebufferに上書きされてしまうので、最初に書き込めなかった2をとばして、次の項目を書き込むことになるのですか?
このようにfwriteが失敗したときのエラー処理はどのようにすればいいのですか?


KING・王  2005-08-24 02:44:06  No: 58532

> 同じVisualC++.netをいれて、ほかの人とデバッグしながらやったのですけど、> うまくデバッグできませんでした。やはり別の人とではできないようです。

普通、そんなことはありません。
バージョンは異なりますが、VC6SP5で、私は2台のPCを使用して、一方のPCで送信側プログラムをステップ実行し、
もう一方のPCで受信側プログラムをステップ実行して、TCPで正しくデータが送信されているかなどを
確認したことがあります。

うまくデバッグできないとは、どのように上手くいかなかったのですか?


caps  2005-08-24 05:31:21  No: 58533

すみません。プログラム的にまちがっているところがあったので、うまくできなかっただけでした(デバッグ)。
で、別のパソコンで実験してみましたところ、2.5Mぐらいのファイルを送受信しているときに受信側でfwriteの戻り値が本来は524288が正常な値なのですが、521632になったりすることもありましたが、そのときrresをみてみると、同じ値である521632になっていたので、エラー処理は正常に動作しているとおもわれます。
ちなみに送信側は、エラー処理のwhileの中の
res=send(c,&data[sres],fres-sres,0);をブレイクポイントにしていました。
ですが、こっちでとまることはなかったので、一度もこのwhileの中は実行されていない、つまり読み込んだ値と同じだけ送れていました。
次に、20Mぐらいのファイルを送受信すると、さっきと同じように524288ではない値がfwriteの戻り値としてでていましたが、rresと同じ値だったので、正常にうごいていたとおもわれます。
それから、20回ぐらい受信側のブレイクポイントでとまって、次に続行ボタンをおすと、受信側でも送信側でもブレイクポイントでとまらなくなりました。(ブレイクポイントの部分を実行しなくなりました)
よって、受信側のwhileを抜けたのだと考えられます。しかし、whileをぬけたらすぐにfileという文字を表示さすようにしていたのですが、fileという文字が表示されていなかったのでどうなってしまったのか・・・?
そして、この状態で何秒かまっていると、今度は送信側のほうのブレイクポイントでとまりました。
みてみると、sendの戻り値が-1になっていました。
なぜ途中でwhileからぬけてしまったのでしょうか?


isshi  2005-08-24 05:47:12  No: 58534

>ファイル情報をいれたヘッダーを先に送信してその後に、中身をおくるようにしています。
とありますが、ヘッダーの送受信には問題はないのですか?
データ本体のときと同様に、send, recv の戻り値をちゃんと使ってますか?

受信側の while が抜けたということですが、どうやって確認しましたか?
確認のための表示はされなかったのですよね?
面倒かもしれませんが、ステップ実行で1行ずつ何がおきているのか
確認してください。


caps  2005-08-24 06:58:27  No: 58535

ヘッダーの送受信は、送信側が
    fseek( fp2, 0L, SEEK_END );//  ファイルポインタを最後尾へ移動
    header2.fsize = ftell( fp2 );//  ファイルサイズを取得
    fseek( fp2, 0L, SEEK_SET );//  ファイルポインタを先頭へ移動
    res=send(c,(char *)&header2,sizeof(header2),0);
    if(res==SOCKET_ERROR) error("ファイル送るエラー\n");
受信側が
  res=recv(finger, (char *)&header, sizeof(header), 0);
  if(res==SOCKET_ERROR) {error("ファイル受信エラー\n"); break;}
  fp=fopen(header.filename,"wb");
      pFileBuffer=(char *)calloc(524288,sizeof(char));
どちらもこの後に中身を送るための上のほうにもかいているようにwhile文をかいています。
受信側のwhileはぬけていないかもしれません。fcloseでとまるようにしてみたら、とまらなかったので。で、recvの後にif(header.fsize==0) printf("a");という意味のないプログラムを追加してそこでとまるようにしてみたら、送信側ではsendを二回実行したのに対し受信側ではさきほど作ったブレイクポイントで一度しかとまりませんでした。そして送信側のsendの戻り値は524288だったのに対し、一度とまったときの受信側のrecvの戻り値は386952という数字になっていました。
よってこれはsendでは524288おくれていることになっているけど、recvは386952しかおくれていなくて、recvのところで実行がとまっているのではないかとおもわれます。
524288送信したのに386952しか受信できていないなんていうことがありえるのですか?


isshi  2005-08-24 07:17:24  No: 58536

>524288送信したのに386952しか受信できていないなんていうことがありえるのですか?
ありえます。1回で受信できるかもしれないし、10回に分けて受信するかもしれません。
また、分割された最後のデータに次の send のデータが結合されて受信するかもしれません。

>res=send(c,(char *)&header2,sizeof(header2),0);
>res=recv(finger, (char *)&header, sizeof(header), 0);
これらも1回で send, recv できるとは限りません。


caps  2005-08-24 08:28:46  No: 58537

もし、sendの戻り値だけrecvで受信できない場合はどうすればいいのですか?


YuO  2005-08-24 08:59:54  No: 58538

> もし、sendの戻り値だけrecvで受信できない場合はどうすればいいのですか?

再度recvすればよいです。

どうも,TCPというプロトコルがわかっていないように思えるのですが……。
前にも挙げたサイトを再度あげておきます。
この,「まさか、そんな、とても信じら れないような間違い。」を犯していませんか?
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/lame-list.html#item20


とおり  2005-08-24 10:24:30  No: 58539

>> もし、sendの戻り値だけrecvで受信できない場合はどうすればいいのですか?
>再度recvすればよいです。

今までのrecvを回すwhile文とかも全て、全部受信仕切れるまで何度もrecvを繰り返す
ためにやったんですよね??
ずぅーっと↑の方で、それは理解したのではないのですか?
そして、確実にあるバイト数送受信できる関数を用意すべき、という話になりましたよね。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加