1

コードのこの部分は、ファイルを受信するときにクライアントによって使用されます。

void do_retr_cmd(int f_sockd){
  int fd;
  ssize_t nread = 0;
  uint32_t fsize, fsize_tmp, total_bytes_read, size_to_receive;
  char *filename = NULL, *conferma = NULL, *filebuffer = NULL;
  char buf[256], dirp[256], t_buf[256];

  memset(dirp, 0, sizeof(dirp));
  memset(buf, 0, sizeof(buf));
  memset(t_buf, 0, sizeof(t_buf));
  printf("Write the name of file to download: ");
  fgets(dirp, BUFFGETS, stdin)
  filename = NULL;
  filename = strtok(dirp, "\n");
  sprintf(buf, "RETR %s", dirp);
  if(send(f_sockd, buf, strlen(buf), 0) < 0){
    perror("Errore durante l'invio del nome del file");
    onexit(f_sockd, 0, 0, 1);
  }
  fsize = 0;
  recv(f_sockd, t_buf, sizeof(t_buf), 0)
  fsize = atoi(t_buf);
  fd = open(filename, O_CREAT | O_WRONLY, 0644);
  fsize_tmp = fsize;
  filebuffer = (char *)malloc(fsize);
  total_bytes_read = 0;
  nread = 0;
  for(size_to_receive = fsize; size_to_receive > 0;){
    nread = read(f_sockd, filebuffer, size_to_receive);
    if(nread < 0){
      perror("read error on retr");
      onexit(f_sockd, 0, 0, 1);
    }
    if(write(fd, filebuffer, nread) != nread){
      perror("write error on retr");
      onexit(f_sockd, 0, 0, 1);
    }
    size_to_receive -= nread;
  }
  close(fd);
  fflush(stdout);
  fflush(stdin);
  memset(buf, 0, sizeof(buf));
  recv(f_sockd, buf, 21, 0)
  printf("%s", buf);
  memset(buf, 0, sizeof(buf));
  memset(t_buf, 0, sizeof(t_buf));
  memset(dirp, 0, sizeof(dirp));
  free(filebuffer);
}

そして、コードのこの部分は、ファイルを送信するときにサーバーによって使用されます。

void do_server_retr_cmd(f_sockd, m_sockd){
  int fd, rc;
  uint32_t fsize, size_to_send;
  char *filename = NULL, *other = NULL;
  char buf[512], t_buf[256];
  off_t offset;
  struct stat fileStat;

  memset(buf, 0, sizeof(buf));
  memset(t_buf, 0, sizeof(t_buf));
  recv(f_sockd, buf, sizeof(buf), 0)
  other = NULL;
  filename = NULL;
  other = strtok(buf, " ");
  filename = strtok(NULL, "\n");

  if(strcmp(other, "RETR") == 0){
    printf("Ricevuta richiesta RETR\n");
  } else /* do something */

  fd = open(filename, O_RDONLY);

  memset(&fileStat, 0, sizeof(fileStat));
  fileStat.st_size = 0;
  fstat(fd, &fileStat)
  fsize = fileStat.st_size;
  snprintf(t_buf, 255, "%" PRIu32, fsize);
  send(f_sockd, t_buf, sizeof(t_buf), 0)
  offset = 0;
  for (size_to_send = fsize; size_to_send > 0; ){
    rc = sendfile(f_sockd, fd, &offset, size_to_send);
    if (rc <= 0){
      perror("sendfile");
      onexit(f_sockd, m_sockd, fd, 3);
    }
    size_to_send -= rc;
  }
  close(fd);
  fflush(stdout);
  fflush(stdin);
  memset(buf, 0, sizeof(buf));
  strcpy(buf, "226 File transfered\n");
  send(f_sockd, buf, strlen(buf), 0)
  memset(buf, 0, sizeof(buf));
  memset(t_buf, 0, sizeof(t_buf));
}

--> エラー チェックは省略されています <--
この 2 つのコードには大きな問題があります。メイン プログラムを起動するとき、次のように記述する必要 があり
ます 。stdout (端末上) に表示されます。なぜこの奇妙な行動をとったのか理解できません。PS: 自分のコードが醜いことはわかっていますが、C 初心者です! 私は Ubuntu amd64 で開発し、GCC-4.6.3 (C 言語) を使用しています。retr
Write the filename to download:




4

3 に答える 3

3

TCP 接続は、信頼できる双方向のバイト ストリームをsend()提供しますが、「アプリケーション メッセージ」の境界は保持されませんrecv()send()1 つにrecv()(そして、最後に送信したチャンクの一部を受け取ることができます)。良いことは、送信したバイトを送信した順序で受信することです。

サーバーコードの行recv(f_sockd, buf, sizeof(buf), 0);は、ここでファイル名を読み取ることを前提としていますが、実際には256、クライアントが送信したものを最大バイトまで取得できます。

ベア TCP の上に何らかのアプリケーション レベルのプロトコルを課す必要があります。非常に単純な方法は、次の形式でファイル コンテンツの前にテキスト ヘッダーを送信することです。

file-size file-name\n

したがって、サーバーは最初の改行を検索し、最初のスペースで行を分割し、予想されるバイト数とそれらのバイトを保存するファイル名を取得できます。その改行の後の受信バッファーの残りを無視せずに、ファイルに保存します。これにより、その接続を複数のファイル転送に再利用することもできます。

お役に立てれば。

于 2012-08-29T12:15:26.680 に答える
1

以前のバージョンの私の答えは完全には正しくありませんでしたが、これが奇妙な振る舞いを見ている理由です。ファイルサイズを次のように送信します

snprintf(t_buf, 255, "%" PRIu32, fsize);

その後、

recv(f_sockd, t_buf, sizeof(t_buf), 0)

ただし、実際にsizeof(t_buf)バイトを読み取ることは保証されていません。次にatoi、誤ったサイズが返されることがあり、ファイルの残りの部分はステータスメッセージとして扱われ、最後に(最初のヌル文字まで)出力されます。

recv要求したすべてのデータを一度に返すことができない場合があるため、その戻り値を確認し、次の呼び出しを繰り返す必要がありますrecv

size_t to_recv = sizeof(t_buf);
size_t rcvd = 0;
while (to_recv > 0) {
    ssize_t r = recv(f_sockd, t_buf + rcvd, sizeof(t_buf) - rcvd, 0);
    if (r < 0) {
        //error
    }
    else {
        to_recv -= r;
        rcvd += r;
    }
}

明らかに、予想されるデータの量を知るか、他の回答で提案されているように、より良いプロトコルを考え出す必要があります(たとえば、サイズを読み取ったときにターミネータを探して判断します)。

于 2012-08-29T12:17:12.403 に答える
1
recv(f_sockd, buf, 21, 0)
printf("%s", buf);

printf何を受信し、何を出力するかを制御するための実際のプロトコルが実装されていないため、大量のランダムなジャンクが出力されます。たとえば、printf印刷するバイト数をどのように知るのでしょうか?

于 2012-08-29T12:16:39.297 に答える