Мой http-сервер в C ++ не отправляет все файлы обратно правильно

StackOverflow https://stackoverflow.com/questions/1810277

  •  05-07-2019
  •  | 
  •  

Вопрос

Я работаю на HTTP-сервере в c ++, и сейчас он работает для запросов текстовых файлов, но при попытке получить jpeg или что-то, только часть файла отправляется. Кажется, проблема в том, что когда я использую fgets (buffer, 2000, return_file), он, похоже, увеличивает индикатор положения файла гораздо больше, чем он фактически помещается в буфер. Почему это случилось? Я положил весь свой код ниже. Проблема возникает в цикле while (true), который возникает, когда код ответа равен 200. Спасибо всем, кто отвечает.

// 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;
Это было полезно?

Решение

Не используйте fgets () для чтения двоичных данных, которые должны выживать побитно. Вам не нужен перевод разделителя записей, и некоторые системы могут предположить, что это текст, если вы читаете его таким образом. В этом отношении символы новой строки и разделители записей абсолютно бессмысленны, поэтому функция сканирования их fgets () в лучшем случае сбивает с толку неэффективность, а в худшем случае просто не поддерживает двоичные данные.

Используйте fread (3) или, что еще лучше, используйте системный вызов raw (в любом случае Posix API для не-Unix) read (2). Это будет считывать определенное количество побитовых данных и сообщать вам, сколько они прочитали. (Что касается того, какой API с поддержкой двоичного кода использовать: обычно мы советуем буферизовать данные, потому что мы обычно обрабатываем их небольшими единицами, например строками. Однако при перемещении всего файла из одного места в другое просто буферизуется замедляет работу. В этом случае проще и быстрее просто использовать read () .)

Вы также не можете strlen () двоичные данные. Вы должны просто использовать количество байтов из вызова API.

Другие советы

Не будет ли использовать strlen break для двоичных файлов?

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

Весьма вероятно, что ваши двоичные файлы содержат NULL-байты ( '\ 0' ). Когда вы читаете данные с помощью fgets , они могут быть помещены в буфер, но при передаче всех данных после потери \ 0 (ваш вызов strlen обеспечивает это).

Итак, вам нужно использовать fread для чтения данных. Он возвращает количество байтов, которые фактически были прочитаны, поэтому вам не нужно использовать strlen вообще. И не забудьте открыть файл в двоичном режиме!

Лучше прочитайте о отображении файлов в память, чтобы вам не приходилось управлять буферами для чтения содержимого файла, и вы можете передать этот буфер в send , и вы получите размер файла одним способом .

Если вы действительно хотите читать байты из файла, то вам следует различать чтение двоичных файлов (с правильным типом mime или application / octet-stream , сохранение количества прочитанных байтов с буфером) и открытие файлов. как текст ( text / * mime-types, и вы можете использовать strlen).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top