使用 C 中的套接字通过 TCP 发送音频文件

Posted

技术标签:

【中文标题】使用 C 中的套接字通过 TCP 发送音频文件【英文标题】:Sending an audio file over TCP using sockets in C 【发布时间】:2020-11-26 21:23:42 【问题描述】:

发送一个普通的 .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 provided\n");
         exit(1);
     
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");

     bzero((char *) &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 *) &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 *) &cli_addr, 
                 &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: %s\n", 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 port\n", 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 host\n");
        exit(0);
    

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

    if (connect(sockfd,(struct sockaddr *) &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] = '\0';

    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;

【问题讨论】:

ch = buffer[i]; if (ch == EOF) 这看起来不对。 read 永远不会将 EOF 存储到缓冲区中。事实上它不能因为EOFint 而不是char char ch; ch = fgetc(fp); if (ch == EOF) 同样,这也是错误的。 fgetc 返回一个 int。这很重要,因为 EOFint for (i = 0; i &lt; BUFFER_SIZE; i++)。那也是错误的。您假设每个read 都会收到完整的BUFFER_SIZE。这是一个常见的误解。对于 TCP 等基于流的协议,每次读取可能会收到可变数量的字节。需要使用read 的返回值而不是仅仅假设BUFFER_SIZE。事实上,您很可能会将意外的零写入文件。 @kaylum 我的意思是,你可能是对的。但是我只是尝试在 if 语句中打印,以查看它是否匹配某些内容(ch == EOF 永远为真),并且确实如此。此外,如果这是一个问题,发送 .txt 文件是否也不起作用?谢谢! 如果它给人以光顾的印象,我深表歉意。不知道键盘另一侧的人的专业水平。在使用 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.

// ...

【讨论】:

试过了,现在代码看起来简单多了,问题似乎已经解决了。非常感谢!

以上是关于使用 C 中的套接字通过 TCP 发送音频文件的主要内容,如果未能解决你的问题,请参考以下文章

从内存中打开 PNG 文件并通过 TCP 套接字发送

通过 tcp 套接字流式传输 PCM 音频

通过 TCP 套接字将音频写入服务器

通过c中的套接字发送结构

C ++中的TCP服务器套接字打开失败

C ++ Win套接字睡眠错误?