2

私は C++ で HTTP サーバーに取り組んでいますが、現在はテキスト ファイルの要求に対して機能しますが、jpeg などを取得しようとすると、ファイルの一部しか送信されません。問題は、 fgets(buffer, 2000, returned_file) を使用すると、実際にバッファに入れるよりもファイル位置インジケータをインクリメントするように見えることです。なぜこれが起こるのでしょうか?すべてのコードを以下に示します。レスポンスコードが200のときに発生するwhile(true)ループで問題が発生しています。

// Interpret the command line arguments
unsigned short port = 8080;

if ( (argc != 1) && (argc != 3) && (argc != 5) ) {
  cerr << "Usage: " << argv[0];
  cerr << " -p <port number> -d <base directory>" << endl;
  return 1;
}
else {
  for (int i = 1; i < argc; ++i) {
    if (strcmp(argv[i], "-p") == 0)
      port = (unsigned short) atoi(argv[++i]);
    else if (strcmp(argv[i], "-d") == 0)
      base_directory = argv[++i];
  }
}
// if base_directory was not given, set it to current working directory
if ( !base_directory ) {
  base_directory = getcwd(base_directory, 100);
}

// Create TCP socket
int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_sock < 0) {
  cerr << "Unable to create TCP socket." << endl;
  return 2;
}

// Create server socket
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons( port );
server.sin_addr.s_addr = INADDR_ANY;

// Bind the socket
if (bind(tcp_sock, (sockaddr*)&server, sizeof(server)) < 0) {
  cerr << "Unable to bind TCP socket." << endl;
  return 3;
}

// Listen for a connection request on TCP port
listen(tcp_sock, 5);

// Create HTTP_Request object and start a while loop of accepting connections
char buffer[2000];
int bytes_recv = 0;
int recv_len = 0;
string error_reply;

HTTP_Response* response;

while (true) {
  int acc_tcp_sock = accept(tcp_sock, NULL, NULL);
  if (acc_tcp_sock == -1) {
    cerr << "Unable to open TCP connection with client." << endl;
  }
  do {
    // may want to do just one recv
    recv_len = recv( acc_tcp_sock, buffer + bytes_recv,
      2000 - bytes_recv, 0 );
    bytes_recv += recv_len;
  } while (false);
  bytes_recv = 0;
  // may want to see if this causes a memory leak
  HTTP_Request* request = HTTP_Request::Parse(buffer, 2000);

  response = handle_request(request); // function to handle the request

  // Put response header into buffer
  response->Print( buffer, 2000 );

  // if 200 and GET then send header with file
  if ( response->Get_code() == 200 ) {
    // send response header
    if ( send( acc_tcp_sock, buffer, strlen(buffer), 0 ) < 0 ) {
      cerr << "Unable to send response header to client." << endl;
    }
    if ( method == "GET" ) {
      // send file
      while ( true ) {
        fgets( buffer, 2000, returned_file );
        if ( feof( returned_file ) ) break;
        if ( send( acc_tcp_sock, buffer, strlen(buffer), 0 ) < 0 ) {
          cerr << "Unable to send file in response to client." << endl;
        }
      }
    }
    fclose( returned_file ); // close file
  }
  else {
    if ( method == "GET" ) {
      error_reply = buffer + error_page;
      if ( send( acc_tcp_sock, error_reply.c_str(), error_reply.length(), 0 ) < 0 ) {
        cerr << "Unable to send response to client." << endl;
      }
    }
    else {
      if ( send( acc_tcp_sock, buffer, strlen(buffer), 0 ) < 0 ) {
        cerr << "Unable to send respone header to client." << endl;
      }
    }
  }

  close( acc_tcp_sock ); // close the connection
}

return 0;
4

4 に答える 4

6

fgets()ビットごとに存続する必要があるバイナリ データの読み取りには使用しないでください。レコード区切りの変換は必要ありません。そのように読むと、システムによってはそれがテキストであると想定される場合があります。さらに言えば、改行とレコード区切り文字はまったく意味がないため、それらをスキャンする fgets()` 関数は、せいぜい紛らわしい非効率であり、最悪の場合、まったくバイナリ対応ではありません。

を使用するfread(3)か、生のシステム コール (非 Unix ではとにかく Posix API) read(2) を使用します。これにより、一定量のビット単位のデータが読み取られ、読み取った量がわかります。(どのバイナリ対応 API を使用するかについて: 通常、データは行などの小さな単位で処理するため、データをバッファリングすることをお勧めします。ただし、ファイル全体をある場所から別の場所に移動する場合、バッファリングすると速度が低下します。この場合、を使用する方が簡単で高速read()です。)

strlen()バイナリデータもできません。API 呼び出しからのバイト数を使用するだけです。

于 2009-11-27T19:16:15.690 に答える
5

strlen を使用すると、バイナリ ファイルが壊れませんか?

send( acc_tcp_sock, buffer, strlen(buffer), 0 )
于 2009-11-27T19:15:35.783 に答える
3

バイナリ ファイルに NULL バイト ( '\0') が含まれている可能性が非常に高いです。でデータを読み取るとfgets、バッファに配置される場合がありますが、送信するとすべて\0が失われます(strlen呼び出しによりこれが保証されます)。

freadしたがって、データを読み取るために使用する必要があります。実際に読み取られたバイト数が返されるため、strlen を使用する必要はまったくありません。ファイルをバイナリモードで開くことを忘れないでください!

于 2009-11-27T19:17:35.013 に答える
0

より良いのは、ファイルのメモリへのマッピングについて読んでください。そうすれば、ファイルの内容を読み取るためにバッファを管理する必要がなくなり、そのバッファを に渡すことができ、sendファイルサイズを 1 つの方法で取得できます。

本当にファイルからバイトを読み取りたい場合は、バイナリ ファイルの読み取り (正しい MIME タイプまたはapplication/octet-stream、読み取りバイト数をバッファーに格納する) と、ファイルをテキストとして開く ( text/*mime-types、strlen を使用できます) を区別する必要があります。

于 2009-11-27T19:47:33.390 に答える