バイナリファイルの転送

解決


caps  2005-07-11 19:50:58  No: 58251  IP: 192.*.*.*

現在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-11 20:43:13  No: 58252  IP: 192.*.*.*

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

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

編集 削除
caps  2005-07-11 21:10:49  No: 58253  IP: 192.*.*.*

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

編集 削除
dairygoods  2005-07-12 09:30:51  No: 58254  IP: 192.*.*.*

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

はい。

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

失礼、sendのことです。

編集 削除
caps  2005-07-12 17:02:02  No: 58255  IP: 192.*.*.*

ファイルを送る側のプログラムを
    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-12 17:11:42  No: 58256  IP: 192.*.*.*

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

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

編集 削除
capx  2005-07-12 17:28:24  No: 58257  IP: 192.*.*.*

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

編集 削除
KING・王  2005-07-12 17:39:48  No: 58258  IP: 192.*.*.*

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

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

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

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

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

編集 削除
とみぞぅ  2005-07-12 18:10:20  No: 58259  IP: 192.*.*.*

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

編集 削除
caps  2005-07-12 18:19:43  No: 58260  IP: 192.*.*.*

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

編集 削除
caps  2005-07-12 18:29:47  No: 58261  IP: 192.*.*.*

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

編集 削除
caps  2005-07-12 18:35:35  No: 58262  IP: 192.*.*.*

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

編集 削除
dairygoods  2005-07-12 19:33:33  No: 58263  IP: 192.*.*.*

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

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

編集 削除
caps  2005-07-12 19:59:39  No: 58264  IP: 192.*.*.*

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

編集 削除
KING・王  2005-07-13 08:07:00  No: 58265  IP: 192.*.*.*

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

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

編集 削除
caps  2005-07-13 16:11:45  No: 58266  IP: 192.*.*.*

返信おくれてすみません。
送信側のコードは
  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-13 16:44:26  No: 58267  IP: 192.*.*.*

> 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 00:07:09  No: 58268  IP: 192.*.*.*

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

編集 削除
caps  2005-07-21 16:04:05  No: 58269  IP: 192.*.*.*

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

編集 削除
とおり  2005-07-25 07:58:19  No: 58270  IP: 192.*.*.*

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

編集 削除
とおり  2005-07-25 08:01:02  No: 58271  IP: 192.*.*.*

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

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

編集 削除
caps  2005-07-25 14:54:35  No: 58272  IP: 192.*.*.*

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

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-25 15:57:06  No: 58273  IP: 192.*.*.*

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

編集 削除
caps  2005-07-25 16:00:41  No: 58274  IP: 192.*.*.*

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

編集 削除