Передача двоичных файлов через Socket с использованием TCP - PullRequest
2 голосов
/ 11 января 2012

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

Давайте посмотрим код:

// Client-side code to send file name
void sendFileName(
    int sd,         /*Socket Descriptor*/
    char *fname)    /*Array Containing the file name */
{       
    int n , byteswritten=0 , written ;
    char buffer[1024];
    strcpy(buffer , fname);
    n=strlen(buffer);
    while (byteswritten<n)
    {
        written=write(sd , buffer+byteswritten,(n-byteswritten));
        byteswritten+=written;
    }
    printf("File name : %s sent to server \n",buffer);
}

В этом коде я пишу имя файла через сокет, а сервер прочитает имя из сокета, которое выглядит следующим образом:

// Server-side code to read file name from client
while ((n = read((int)connfd, (fname + pointer), 1024)) > 0)
{
    pointer=pointer+n;
}

Ну, проблема в том, что я должен закрыть конец записи на стороне клиента после отправки имени файла, которое будет сегментом FIN для кода на стороне сервера, чтобы прекратить чтение с сервера.

ЕслиЯ закрываю конец чтения как:

shutdown(sd,SHUT_WR);       //Closing write end at client side

Как я могу записать (т.е. отправить) содержимое файла через сокет на сервер, чтобы он мог читать из сокета?

Примечание: что я сделалдолжен был добавить имя файла с содержимым файла со стороны клиента и добавить специальный символ к содержимому (для уведомления об окончании имени файла), а затем содержимое файла.

На стороне клиента,

void readWriteFile(
   int sd,                      /*Socket Descriptor */
   int fd,                      /*File Descriptot */
   char *fname)                 /*File Name  */
{
   char buffer[1050];
   int n;
   int len = 0;
   char *tmp = (char *)malloc(sizeof (char) * (strlen(fname) + 2));
   strcpy(tmp, fname);          //Copying the file name with tmp 
   strcat(tmp, "/");            //Appending '/' to tmp
   len = strlen(tmp);
   memset(buffer, '\0', sizeof (buffer));
   strcpy(buffer, tmp);         //Now copying the tmp value to buffer

   while ((n = read(fd, buffer + len, 1024)) > 0)
   {
      if (write(sd, buffer, n) != n)
      {
         printf("File write Error \n");
         exit(0);
      }
      len = 0;
   }
   printf("File sent successfully \n");
}

А на стороне сервера

   char fname[50], buffer[1024];
   int n, fd;
   int i;
   int start = 0;

   while ((n = read((int)connfd, buffer, 1024)) > 0) // Reading from socket
   {
      if (!start)
      {
          /* This 'if' loop will executed almost once i.e. until 
             getting the file name */
         for (i = 0; i < 1024; i++)
         {
            /* Since '/' is the termination character for file name */
            if (buffer[i] == '/')
            {
               start = 1;       // Got the file name
               break;
            }

            fname[i] = buffer[i]; //Storing the file name in fname
         }

         fname[i] = '\0';

         /* Creating the file in the server side */
         fd = open(fname, O_WRONLY | O_CREAT, S_IRWXU);

         if (fd < 0)
         {
            perror("File error");
            exit(0);
         }

         /* Here writing the buffer content to the file after 
            the (buffer+i+1), because after this address only, we 
            can get the original file content */

         write(fd, buffer + i + 1, n);
      }
      else
      {
         write(fd, buffer, n);
      }
   }
   printf("%s received successful \n", fname);

Этот код отлично работает для изображений, исполняемых и текстовых файлов.Но если я отправляю любой аудиофайл, он не воспроизводится на стороне сервера.Размер остается размером.Но мне интересно, почему это происходит с аудио файлами.Что-то не так в логике?Я еще не пробовал видеофайлы.

1 Ответ

8 голосов
/ 11 января 2012

Почему вы говорите: «Я должен закрыть конец записи на стороне клиента после отправки имени файла»? Вы еще не закончили отправку своих данных, поэтому наверняка еще не хотите закрывать сокет?

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

  • Отправьте имя файла, оканчивающееся байтом NUL, затем отправьте содержимое.
  • Отправка двух 32-разрядных целых чисел в сетевом порядке байтов (с прямым порядком байтов), содержащих длину имени файла и длину содержимого файла соответственно. Затем отправьте имя файла, затем содержимое файла.
  • Сериализует как имя файла, так и содержимое файла в структурированный формат документа, такой как JSON или XML. Отправьте сериализованную версию. (Вероятно, излишний для вашего варианта использования.)

Закройте сокет после того, как вы отправили всю структуру, какой бы вы ни решили.

При любом из этих способов получатель обладает всей информацией, необходимой ему для однозначного восстановления исходного имени и содержимого файла.

ОБНОВЛЕНИЕ : Вы добавили намного больше к своему вопросу, это почти другой вопрос ...

Ваш код на стороне сервера (тот, который читает имя файла и содержимое файла) содержит много ошибок.

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

В коде другого размера наблюдается явное переполнение буфера, когда вы считываете 1024 байта в буфер, в котором доступно только 1024 - len байтов.

Вам нужно исправить все это, прежде чем вы сможете ожидать, что что-то сработает.

...