Отправка аудиофайла по протоколу TCP с использованием сокетов на C

#c #sockets #audio #wav

#c #сокеты #Аудио #wav

Вопрос:

Отправка обычного файла .txt работает отлично. Но если я попытаюсь отправить файл .wav, генерируемый выходной файл будет лишь частью размера входного файла (и он ничего не воспроизводит). Я перепробовал практически все, что смог найти в Google, возможно, это как-то связано с тем, что я не читал .wav-файл правильно. Но я читаю и записываю по одному символу за раз, поэтому я не знаю, почему это должно быть проблемой. Кроме того, это делается через localhost. Любая помощь будет принята с благодарностью, спасибо!

server.c

 /* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

#define BUFFER_SIZE 32

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno, n;
     socklen_t clilen, servlen;

     char buffer[BUFFER_SIZE];
     FILE *fp;
     struct sockaddr_in serv_addr, cli_addr;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port providedn");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");

     bzero((char *) amp;serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);

     if (bind(sockfd, (struct sockaddr *) amp;serv_addr, sizeof(serv_addr)) < 0) 
              error("ERROR on binding");

     listen(sockfd, 5);

     clilen = sizeof(cli_addr);
     servlen = sizeof(serv_addr);
     newsockfd = accept(sockfd, 
                 (struct sockaddr *) amp;cli_addr, 
                 amp;clilen);
     if (newsockfd < 0) 
          error("ERROR on accept");

     bzero(buffer, BUFFER_SIZE);
     n = read(newsockfd, buffer, BUFFER_SIZE);
     if (n < 0) error("ERROR reading from socket");

     printf("Received file name: %sn", buffer);

     fp = fopen(buffer, "rb"); 
     if (fp == NULL) 
         printf("File open failed!n"); 
     else
         printf("File successfully opened!n");

    while (1) {
        bzero(buffer, BUFFER_SIZE);
        char ch;
        int i;

        for (i = 0; i < BUFFER_SIZE; i  ) { 
            ch = fgetc(fp); 
            buffer[i] = ch; 
            if (ch == EOF) 
                break; 
        }
        n = write(newsockfd, buffer, BUFFER_SIZE);
        if (n < 0) 
            error("ERROR writing to socket");

        if (ch == EOF)
            break;
    }
    printf("File sending complete...n");

    if (fp != NULL)  
        fclose(fp);
     close(newsockfd);
     close(sockfd);

     return 0; 
}
 

client.c

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

#define BUFFER_SIZE 32

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[BUFFER_SIZE];
    FILE *out;
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname portn", argv[0]);
       exit(0);
    }

    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");

    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such hostn");
        exit(0);
    }

    bzero((char *) amp;serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)amp;serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);

    if (connect(sockfd,(struct sockaddr *) amp;serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");

    printf("Enter the file name: ");
    bzero(buffer, BUFFER_SIZE);
    fgets(buffer, BUFFER_SIZE - 1, stdin);
    buffer[strlen(buffer) - 1] = '';

    n = write(sockfd, buffer, strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");

    out = fopen("out.wav", "wb");
    while (1) {
        bzero(buffer, BUFFER_SIZE);
        int i, j;

        n = read(sockfd, buffer, BUFFER_SIZE);
        if (n < 0) 
            error("ERROR reading from socket");

        char ch; 
        for (i = 0; i < BUFFER_SIZE; i  ) {
            ch = buffer[i];  
            if (ch == EOF) 
                break; 
            j = (int)ch;
            fputc(j, out);
        }
        if (ch == EOF)
            break;
    }
    printf("File write complete... You can now use the output file!!n");

    if (out != NULL)  
        fclose(out);
    close(sockfd);

    return 0;
}
 

Комментарии:

1. ch = buffer[i]; if (ch == EOF) Это выглядит неправильно. read никогда не сохранится EOF в буфере. На самом деле это невозможно, поскольку EOF это int не a char .

2. char ch; ch = fgetc(fp); if (ch == EOF) Аналогично, это тоже неправильно. fgetc возвращает int . Это важно, как EOF int и .

3. for (i = 0; i < BUFFER_SIZE; i ) . Это тоже неправильно. Вы предполагаете, что каждый read получит полный BUFFER_SIZE . Это распространенное недоразумение. Для потоковых протоколов, таких как TCP, каждое чтение может принимать переменное количество байтов. Необходимо использовать возвращаемое значение read , а не просто предполагать BUFFER_SIZE . Как бы то ни было, вы, вероятно, записываете в файл непреднамеренные нули.

4. @kaylum Я имею в виду, что вы можете быть правы. Но я просто попытался напечатать внутри этого оператора if, чтобы посмотреть, соответствует ли он чему-либо (ch == EOF когда-либо становится true), и это действительно так. Кроме того, если это было проблемой, разве отправка файлов .txt также не должна работать? Спасибо!

5. И я прошу прощения, если это показалось мне покровительственным. Никто не знает уровень знаний человека по другую сторону клавиатуры. После некоторого использования SO вы видите много людей, которые все еще изучают основы и указывают даже на простые вещи, которые часто помогают им. Многие люди действительно думают, что прохождение одного теста означает, что их код не содержит ошибок (они даже говорят это явно).

Ответ №1:

Чтобы определить окончание передачи, вы должны проверить возвращаемое значение read() , а не проверять наличие EOF в прочитанных данных. Я также взял на себя смелость реструктурировать и упростить ваш код.

Рассмотрим следующие (непроверенные) изменения:

server.c:

 // ...
while (1) {
    size_t num_read = fread(buffer, 1, BUFFER_SIZE, fp);
    if (num_read == 0) // end of file.
        break;
         

    n = write(newsockfd, buffer, num_read);
    if (n < 0) // Error
        error("ERROR writing to socket");
    else if (n == 0) // Could handle this too
        break;
}
// ...
 

client.c:

 // ...
while (1) {
    n = read(sockfd, buffer, BUFFER_SIZE);
    if (n < 0) 
        error("ERROR reading from socket");
    else if (n == 0) // Socket closed. Transfer is complete (or borked)
        break;

    fwrite(buffer, 1, n, out); // Could check fwrite too.
}
// ...
 

Комментарии:

1. Попробовал, код теперь выглядит намного, намного проще, и проблема, похоже, устранена. Большое спасибо!