Domanda

Sto lavorando su un server HTTP in c ++, e ora funziona per le richieste di file di testo, ma quando provo a ottenere un jpeg o qualcosa del genere, viene inviata solo una parte del file. Il problema sembra essere che quando uso fgets (buffer, 2000, return_file) sembra aumentare l'indicatore di posizione del file molto più di quanto non finisca per essere inserito nel buffer. Perché dovrebbe succedere? Ho messo tutto il mio codice qui sotto. Il problema si verifica nel ciclo while (true) che si verifica quando il codice di risposta è 200. Grazie a chiunque risponda.

// 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;
È stato utile?

Soluzione

Non utilizzare fgets () per leggere i dati binari che devono sopravvivere bit per bit. Non si desidera la traduzione del separatore di record e alcuni sistemi potrebbero assumere il testo se lo si legge in questo modo. In ogni caso, newline e separatori di record sono completamente insignificanti, quindi la funzione fgets () `di scansionarli è nella migliore delle ipotesi un'inefficienza confusa e nella peggiore dei casi semplicemente non binaria.

Usa fread (3) , o meglio ancora, usa la chiamata di sistema non elaborata (Posix API comunque, su non Unix) read (2). Questo leggerà una certa quantità di dati bit per bit e ti dirà quanto letto. (Riguardo a quale API con capacità binaria da usare: normalmente, ci viene consigliato di bufferizzare i dati perché in genere li elaboriamo in piccole unità come le linee. Tuttavia, quando si sposta un intero file da un posto a un altro buffer solo ti rallenta. In questo caso è più semplice e veloce usare read () .)

Inoltre non puoi strlen () dati binari. Devi solo usare il conteggio dei byte dalla chiamata API.

Altri suggerimenti

Non utilizzeresti l'interruzione strlen sui file binari?

send( acc_tcp_sock, buffer, strlen(buffer), 0 )

È molto probabile che i tuoi file binari contengano byte NULL ( '\ 0' ). Quando leggi i dati con fgets , possono essere collocati nel buffer, ma quando li trasmetti dopo aver perso \ 0 (la tua chiamata strlen assicura questo).

Quindi, devi usare fread per leggere i dati. Restituisce un numero di byte effettivamente letti, quindi non è necessario utilizzare strlen. E non dimenticare di aprire il file in modalità binaria!

Meglio, leggi come mappare i file in memoria, in questo modo non devi gestire i buffer per leggere il contenuto del file, e puoi passare quel buffer a send e ottieni le dimensioni del file in un modo .

Se vuoi davvero leggere i byte dal file, devi distinguere la lettura dei file binari (con il tipo mime corretto o application / octet-stream , archiviando il numero di byte letti con buffer) e aprendo i file come testo ( text / * mime-types e puoi usare strlen).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top