在 Unix 上使用套接字在 C 中发送和接收文件(服务器/客户端)

Posted

技术标签:

【中文标题】在 Unix 上使用套接字在 C 中发送和接收文件(服务器/客户端)【英文标题】:Sending and Receiving a file (Server/Client) in C using socket on Unix 【发布时间】:2012-06-29 01:31:21 【问题描述】:

首先,感谢大家阅读本文并提供帮助,我非常感激。 其次,我很抱歉,但我还是这个网站的新手,英语不是我的母语,所以我可能会做一些格式和语言错误。我提前道歉。 另外,我的C知识不是很好,但我愿意学习和提高。 现在,手头的事情:

我需要做的是创建一个客户端和一个服务器,并让服务器监听传入的连接。 然后我让客户端向服务器发送一个很大的文本文件(我知道它只会是一个文件)。 然后服务器将对文件执行一些操作(它将在发送的文件上运行一个脚本,在输出中生成另一个名为“output.txt”的文件)。 然后服务器需要将 output.txt 文件发送回客户端。

现在,我有点了解如何制作客户端和服务器(我阅读了 beej 指南和本网站上的其他一些内容),即使我可能犯了一些错误。我需要一些帮助服务器接收文件,然后调用脚本并将另一个文件发送回客户端。 现在我所做的是服务器和客户端......我真的不知道如何继续。 顺便说一句,我使用我在这个网站和互联网上找到的东西制作了这些文件,我希望它们不会太乱,因为我不是一个好的程序员

这是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> 
#include <pthread.h>


#define SOCKET_PORT "50000"
#define SOCKET_ADR "localhost"
#define filename "/home/aryan/Desktop/input.txt"


void error(const char *msg)

    perror(msg);
    exit(0);



int main()

/* Making the client */
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;

char buffer[256];

portno = atoi(SOCKET_PORT);

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) 
    error("ERROR opening socket");

server = gethostbyname(SOCKET_ADR);

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");

/* Time to send the file */
FILE *pf;
unsigned long fsize;

pf = fopen(filename, "rb");
if (pf == NULL) 

    printf("File not found!\n");
    return 1;

else 

    printf("Found file %s\n", filename);

    fseek(pf, 0, SEEK_END);
    fsize = ftell(pf);
    rewind(pf);

    printf("File contains %ld bytes!\n", fsize);
    printf("Sending the file now");


while (1) 

    // Read data into buffer.  We may not have enough to fill up buffer, so we
    // store how many bytes were actually read in bytes_read.
    int bytes_read = fread(buffer, sizeof(char),sizeof(buffer), pf);
    if (bytes_read == 0) // We're done reading from the file
        break;

    if (bytes_read < 0) 
    
        error("ERROR reading from file"); 
    

    // You need a loop for the write, because not all of the data may be written
    // in one call; write will return how many bytes were written. p keeps
    // track of where in the buffer we are, while we decrement bytes_read
    // to keep track of how many bytes are left to write.
    void *p = buffer;
    while (bytes_read > 0) 
    
        int bytes_written = write(sockfd, buffer, bytes_read);
        if (bytes_written <= 0) 
        
            error("ERROR writing to socket\n");
        
        bytes_read -= bytes_written;
        p += bytes_written;
    
       

printf("Done Sending the File!\n");
printf("Now Closing Connection.\n");

fclose(pf);
close(sockfd);
return 0;

这是server.c:

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

#define SOCKET_PORT 50000
#define filename "/home/aryan/Desktop/output.txt"


void error(const char *msg)

    perror(msg);
    exit(1);


void* client_thread_proc(void* arg)

char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
FILE *fp;

int thisfd = (int)arg;
printf("Server %d: accepted = %d\n", getpid(), thisfd);

if (thisfd < 0) 
 
    printf("Accept error on server\n");
    error("ERROR on accept"); 
    return NULL;


printf("Connection %d accepted\n", thisfd);

fp = fopen(filename, "a+b");
if (fp == NULL) 

    printf("File not found!\n");
    return NULL;

else 

    printf("Found file %s\n", filename);


/* Time to Receive the File */
while (1)

    bzero(buffer,256);
    n = read(thisfd,buffer,255);
    if (n < 0) error("ERROR reading from socket");

    n = fwrite(buffer, sizeof(char), sizeof(buffer), fp);
    if (n < 0) error("ERROR writing in file");

    n = write(thisfd,"I am getting your file...",25);
    if (n < 0) error("ERROR writing to socket");
 /* end child while loop */

fclose(fp);

return NULL;


void serve_it(int Client)

    void* arg = (void*)Client;
    pthread_t new_thread;
    pthread_create( &new_thread, NULL, &client_thread_proc, arg);


/* Making Server */
int main()

int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
FILE *fp;

signal (SIGCHLD, SIG_IGN);

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) 
    error("ERROR opening socket");

bzero((char *) &serv_addr, sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(SOCKET_PORT);

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

listen(sockfd,5);

clilen = sizeof(cli_addr);

while (1)

    printf("Server %d accepting connections\n", getpid());

    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

    serve_it(newsockfd);
  // serving loop


close(sockfd);   
return 0; 

我想要一些关于如何继续的指示... 如何使脚本继续从客户端到服务器接收到的文件? 如何将新文件发送回同一客户端? 如果您能帮助我解决代码中的错误,我将不胜感激。

谢谢大家,对长时间阅读感到抱歉。祝你有美好的一天!

【问题讨论】:

【参考方案1】:

第一个错误:

#define filename = "Home\\Desktop\\input.txt"

应该是

#define filename "Home\\Desktop\\input.txt"

否则预处理器会插入

= "Home\Desktop\input.txt"

不是

"Home\\Desktop\\input.txt"

你所期望的。

第二个错误:

int bytes_read = read(filename /* THAT IS WRONG, FILE HANDLE MUST BE HERE */, buffer, strlen(buffer) /* also WRONG, must be MAX_BYTES_IN_BUFFER or something */ );

这里你必须从“pf”中读取(你已经用 fopen() 调用打开了它)并且最后一个参数必须是你想要读取的字节数。因此,如果您执行 strlen(buffer) 并且缓冲区在程序运行时开始时包含一些垃圾,您将遇到崩溃。 strlen() 只能为有效的以零结尾的字符串调用,它不是数组的大小!

编辑:详细的服务器循环:

while (1)

    printf("Server %d accepting connections\n", getpid());

    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

    serve_it(newsockfd);
  // serving loop

serve_it():

void serve_int(int Client)

    void* arg = (void*)Client;
    pthread_t new_thread;
    pthread_create( new_thread, NULL, &client_thread_proc, arg);


void* client_thread(void* arg)

    int thisfd = (int)arg;
    printf("Server %d: accepted = %d\n", getpid(), thisfd);

    if (thisfd < 0) 
     
        printf("Accept error on server\n");
        error("ERROR on accept"); 
        return NULL;
    

    printf("Connection %d accepted\n", thisfd);

    fp = fopen(filename, "a+b");
    if (fp == NULL) 
    
        printf("File not found!\n");
        return NULL;
    
    else 
    
        printf("Found file %s\n", filename);
    

    /* Time to Receive the File */
    while (1)
    
        bzero(buffer,256);
        n = read(thisfd,buffer,255);
        if (n < 0) error("ERROR reading from socket");

        n = fwrite(buffer, sizeof(buffer), 1, fp);
        if (n < 0) error("ERROR writing in file");

        n = write(thisfd,"I am getting your file...",25);
        if (n < 0) error("ERROR writing to socket");
     /* end child while loop */

    return NULL;

【讨论】:

mmmm 当你添加你的答案时,你有点复制了 server.c ......你能把它清理干净吗?我正在迷失什么该保留什么不该保留……我的脑袋要爆炸了。 基本上问题是 client_thread_proc 重复了两次,一次在 main() 中,一次作为函数声明(如果你理解正确,那是一个很大的 if)。但如果它是一个函数,为什么不直接调用它呢?如果不是,为什么要宣布 2 次?我迷路了:P kk :) thx 让我感到困惑 :P 现在它给了我 /tmp/ccG381x5.o: In function serve_it': server.c:(.text+0x21a): undefined reference to pthread_create' collect2: ld 返回 1 exit status... 为什么不能这项工作一次:P 我还编辑了 server.c 以复制我此时拥有的文件。再次感谢! 哦,对不起,我不知道该怎么做...我使用 gcc -o server server.c 来编译它,如果我尝试 gcc -lpthread server server.c 它不会工作,也不 gcc -o -lpthread server server.c 我正在阅读有关线程的教程,我尝试使用 gcc -lpthread server.c 进行编译,但仍然给我一个错误:gcc -lpthread server.c /tmp/ccBduiJx.o: In function serve_it': server.c:(.text+0x21a): undefined reference to pthread_create' collect2: ld 返回 1 个退出状态

以上是关于在 Unix 上使用套接字在 C 中发送和接收文件(服务器/客户端)的主要内容,如果未能解决你的问题,请参考以下文章

关于通过(unix-)套接字发送/接收大量数据的另一个困惑

unix 套接字数据报:服务器接收到无效字符

C中的服务器/套接字编程:数据未正确发送/接收?

Unix - 如何向多个进程发送消息?

C Socket Send() 直到程序终止才发送

使用数据包套接字在同一网络接口上发送和接收时无法接收包