バイナリファイルの転送

解決


caps  2005-07-12 04:50:58  No: 58251

現在Winsockを使って、バイナリファイルの転送を行うコンソールプログラムを作っています。自分はヘッダにファイル情報を書き込んで、そのヘッダを送信し次に中身を送信するという方法を使っています。ですが、うまく送信することができません。
送信側は、
FILE *fp;
char header[256]={0};
long size;
              fp=fopen(filename,"r");
    fseek(fp,0,SEEK_END);
    size=ftell(fp);
    fseek(fp,0,SEEK_SET);
    sprintf(header,"%d",size);
    send(c,(char *)&header,sizeof(header),0);
    send(c,(char *)&fp,size,0);
このようなプログラムにしてみました。受信側は
int nFileSize;
char *pFileBuffer;

  recv(a, header, sizeof(header), 0);
  nFileSize = atoi(header);
  pFileBuffer=(char *)calloc(nFileSize,sizeof(char));
  recv(a, pFileBuffer, nFileSize, 0);
このように書いています。このプログラムでどのへんがまちがっているのでしょうか?
ご教授ください。よろしくおねがいします。


dairygoods  2005-07-12 05:43:13  No: 58252

ファイルポインタ(fp)を送っても
ファイルの中身を送ったことにはなりません。

ファイルからバッファに読み込み、バッファからソケットに書き出します。


caps  2005-07-12 06:10:49  No: 58253

返信ありがとうございます。
読み込むということは、
size=fread(data,sizeof(char),5000,fp);こういうことですか?
それから、ソケットに書き出すとはどのような意味なのでしょうか?
勉強不足ですみません。
よろしくおねがいします。


dairygoods  2005-07-12 18:30:51  No: 58254

> 読み込むということは、
> size=fread(data,sizeof(char),5000,fp);こういうことですか?

はい。

> それから、ソケットに書き出すとはどのような意味なのでしょうか?

失礼、sendのことです。


caps  2005-07-13 02:02:02  No: 58255

ファイルを送る側のプログラムを
    size=fread(data,sizeof(char),5000,fp);
    sprintf(header,"%d",size);
    send(c,(char *)&header,sizeof(header),0);
    send(c,(char *)&data,size,0);
    fclose(fp);
このようにしてみました。受け取る側のプログラムはそのままです。
これで実行してみたのですが、受信側のフォルダにははいっていませんでした。
受信側のプログラムも変更しなければならないところがあるのでしょうか?
受信側は受信したデータをどこに保存するなどの処理をおこなわなければならないのでしょうか?アドバイスよろしくおねがいします。


dairygoods  2005-07-13 02:11:42  No: 58256

> 受信側は受信したデータをどこに保存するなどの処理をおこな
> わなければならないのでしょうか?アドバイスよろしくおねがいします。

そうですね。
どこかに保存する処理を実行しないと、どこにも保存されません。


capx  2005-07-13 02:28:24  No: 58257

お早い返信ありがとうございます。
受信側は前のプログラムからこれを追加しました。
    fp=fopen("test.txt","wb+");
    fwrite(pFileBuffer,sizeof(char),(int)header,fp);
    fclose(fp);
これで実行してみましたが、フォルダにははいっていませんでした。どこがまちがっているのでしょうか?


KING・王  2005-07-13 02:39:48  No: 58258

とりあえず、受信側のプログラムで、recv()を行った際に、送信されたデータがただしく受信できているか、
デバッガなどで、確認してみては、どうでしょうか?

1回目のrecv()で、headerの中に、送信した(send())した値が入っていますか?
また、2回目のrecv()で、pFileBufferの中の値は、送信した値が入っていますか?

それぞれ、どの時点でおかしいのか、デバッガで確認してみて下さい。

送信側と受信側の両方で、デバッガを実行させながら、受信側でrecv()をステップ実行させておいて、
送信側でsend()を実行すると、受信側でrecv()から制御が戻るなど、してデバッグするのが、
面倒ですが、確実だと思います。

#以前、socket()での通信プログラムを作成した際には、2台を用いて、
#両手で、それぞれのPCのステップ実行のキーを操作した経験が・・・


とみぞぅ  2005-07-13 03:10:20  No: 58259

ethreal等で送受信の内容を確認してみてはいかがでしょう?


caps  2005-07-13 03:19:43  No: 58260

今デバッガを実行させてみたんですけど、送信側のほうではdataに"あいうえお  フフフフフフフフ"とかかれています。ちなみに、送ろうとしているテキストファイルの中身はあいうえおです。これはファイルの最後にヌル文字がはいっていないからだとおもいます。で、受信側のほうをみてみると、pFileBufferに"0x00428b00"このようになっています。サイズをみてみると、どっちも20でしたので、正常におくれているとおもわれます。
pFileBufferを何かに変換しなければならないのでしょうか?


caps  2005-07-13 03:29:47  No: 58261

すみません。見間違えでした。pFileBufferにはあいうえおォォォォォォ・・・とかかれていました。このォォォォォとかフフフフフフとか以外は正常におくれているはずなんですけど、フォルダを見てみると、test.txtというファイルはつくられているのですが、サイズが0で中身は空っぽです。これはたぶんfopen("test.txt","wb+")これによってtest.txtが作成されただけの状態で、かきこめてないということのような気がするのですが、どうでしょうか?


caps  2005-07-13 03:35:35  No: 58262

とみぞぅさん返信ありがとうございます。
自分はVC++.netを使っていて、デバッガを利用して内容をみてみましたら、上記のようになりました。もしよろしければアドバイスよろしくおねがいします。


dairygoods  2005-07-13 04:33:33  No: 58263

> fp=fopen("test.txt","wb+");
> fwrite(pFileBuffer,sizeof(char),(int)header,fp);
> fclose(fp);

header は、文字列へのポインタですよね?
これをintにキャストしたらとんでもない値になって、
どんな風に書き込まれるか予想もつかないです。


caps  2005-07-13 04:59:39  No: 58264

すみません。そこはnFileSizeですね。
それをなおしたんですけど、やはりファイルには書き込まれていません。
やはりフフフフとかォォォォとかの文字化けが何か影響しているのでしょうか?
そこ以外はサイズも正常におくれてますし、書き込みのエラーなどもでていないのでこの方法であっているとおもうのですが・・・


KING・王  2005-07-13 17:07:00  No: 58265

受信側でpFileBufferには、正しく値が格納されているようなので、
問題を少し間単にするために、fwrite()の第3引数を固定値(マジックナンバー)で指定し、
fwrite()を実行してみて、また、fwrite()の戻り値なども確認されてはどうでしょうか?

dairygoodsさんが指摘された点をどのように修正されたのか、分かりかねますので、
そのあたりも、本当に正しく修正されているのか、よければコードを提示してみてください。


caps  2005-07-14 01:11:45  No: 58266

返信おくれてすみません。
送信側のコードは
  FILE *fp;
  char data[100000];
  char filename[1024];
  long size;
  char header[256]={0};
    fp=fopen(filename,"rb");
    if(fp==NULL){
      printf("ファイルのオープン失敗\n");
    }
    else{
      printf("ファイルオープンできた\n");
    }
  
    size=fread(data,sizeof(char),5000,fp);
    
    sprintf(header,"%d",size);
    res=send(c,(char *)&header,sizeof(header),0);
    if(res==SOCKET_ERROR) error("ファイル送るエラー\n");
    res=send(c,(char *)&data,size,0);
    if(res==SOCKET_ERROR) error("ファイル送るエラー2\n");
    fclose(fp);
このようにしていて、受信側は
FILE *fp;
int nFileSize;
char *pFileBuffer;
char header[256] = {0};
  res=recv(c, header, sizeof(header), 0);
  if(res==SOCKET_ERROR) {error("ファイル受信エラー\n"); break;}
  nFileSize = atoi(header);
  pFileBuffer=(char *)calloc(nFileSize,sizeof(char));

  res=recv(c, pFileBuffer, nFileSize, 0);
  if(res==SOCKET_ERROR) {error("ファイル受信エラー2\n"); break;}
      fp=fopen("test.txt","wb");
        res=fwrite(pFileBuffer,sizeof(pFileBuffer),nFileSize,fp);
        fclose(fp);
このようにしました。KINGさんがおっしゃったように、fwriteの第3引数のnFileSizeを20にしてやってみましたが、ファイルはフォルダの中に作られているのですが、0バイトになっていました。戻り値は20になっていました。
ファイルが作られているのは、fopenでひらこうとしているファイルがないから、勝手に空のファイルを作っただけだとおもいます。
受信側であいうえおかきくけこォォォォ・となっているので、一応送信はできているとおもいます。たぶん受信側がまちがっているのかと・・。
みなさんアドバイスよろしくおねがいします。


dairygoods  2005-07-14 01:44:26  No: 58267

> fp=fopen("test.txt","wb");
> res=fwrite(pFileBuffer,sizeof(pFileBuffer),nFileSize,fp);
> fclose(fp);

fwrite(pFileBuffer,sizeof(char),nFileSize,fp);
ですね。念のため。

で、これを変えても上手く書き込めないとなると、
この3行だけ考えれば、ソケットも書き込む内容も関係なく
writeしたサイズのファイルが作成されるはずですから、
サイズ0というのは不思議ですね...

実は違うファイルを見ているとか、
この後、別の処理で、サイズ0で上書きしてしまっているとか、
ないですか?


caps  2005-07-14 09:07:09  No: 58268

解決することができました。
dairygoodsさん、KING・王さん、とみぞぅさん本当にありがとうございました。
受信側をwhileでまわしていて、breakeさせる処理をかいていなかったため、どんどん上書きされていたようです。こんな初歩的なミスでご迷惑をおかけいたしましてどうもすみません。
ヘッダファイルに名前の情報を加えた構造体にしてやってみても正常に動きました。
また機会があればよろしくおねがいします。


caps  2005-07-22 01:04:05  No: 58269

前回の問題は解決したのですが、新たな問題がでました。
今、掲示板で新規投稿できないようなので、ここにかかせてもらうことにしました。
上のようなプログラムで、データ転送はできるようになったのですが、
  char data[1000000];
と宣言しているので、サイズが1M以上のものを扱うことができません。
      char data[10000000];
とすると、実行したときに強制終了になってしまいます。
もっと大きなファイルを扱いたいときはどうすればいいのでしょうか?これ以上配列はふやせないみたいだし。
アドバイスよろしくお願いいたします。


とおり  2005-07-25 16:58:19  No: 58270

どうせ、1M配列をstackに確保してるんじゃないの?
(stack領域には比較的小さなサイズ制限があります。設定可能ですが)
mallocやnewで、ヒープから確保して下さい。


とおり  2005-07-25 17:01:02  No: 58271

間違えた。やってるのは"10M"配列確保か。

あるいは、1度に全てのデータを配列に格納せずに、徐々にdiskに
書き出すなりして、stack上のメモリ量は一定量に抑えるとか。


caps  2005-07-25 23:54:35  No: 58272

返信ありがとうございます。
送る側のプログラムは

char data[1000000];
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;
    fseek(fp2,1000000L,SEEK_CUR);
    }
    }
    fclose(fp2);
    printf("ファイル送信完了\n");
  }
このようにしています。
ですが、1Mをこえる画像を送ると、サイズ2Mぐらいのものが受信側では4Mぐらいになっています。そして、画像の真ん中のところが黒くなっています(送信できていないのか?)。真ん中以外のところはちゃんと画像がみれます。
デバッガでしらべてみたのですが、2Mのものでやってみると、最後に、上側のif文のfreadが返す値をresに代入するところで、resが0となっています。header2.fsizeは正常にへっていっています。


みい  2005-07-26 00:57:06  No: 58273

freadでファイルポインタ移動しませんでしたっけ?
(fread使わないからよく憶えてないですけど)
移動するんだったらfseekしたら読込位置がおかしくなると思います。


caps  2005-07-26 01:00:41  No: 58274

ありがとうございました。解決しました。なるほど、freadはポインタ移動してくれるんですね。
話かわりますけど、この掲示板なかなか復活しませんねえ。


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

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






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