文件传输服务器/客户使用套接字

我正在尝试在服务器和客户端之间进行文件传输,但工作非常糟糕。 基本上需要发生的是:
1)客户端发送一个txt文件到服务器(我称之为“quotidiani.txt”)
2)服务器将其保存在另一个txt文件(“receive.txt”)
3)服务器上运行一个脚本,修改它并保存其他名称(“output.txt”)
4)服务器将文件发送回客户端(保存在同一个套接字上),名称(final.txt)

问题是,第一个文件(quotidiani.txt)是只读了一小部分,然后有一些错误。 我希望有人帮助我理解和纠正我的错误。

这是我的代码:

client.c:

#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/wait.h> #include <sys/socket.h> #include <signal.h> #include <ctype.h> #include <arpa/inet.h> #include <netdb.h> #define PORT 20000 #define LENGTH 512 void error(const char *msg) { perror(msg); exit(1); } int main(int argc, char *argv[]) { /* Variable Definition */ int sockfd; int nsockfd; char revbuf[LENGTH]; struct sockaddr_in remote_addr; /* Get the Socket file descriptor */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno); exit(1); } /* Fill the socket address struct */ remote_addr.sin_family = AF_INET; remote_addr.sin_port = htons(PORT); inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); bzero(&(remote_addr.sin_zero), 8); /* Try to connect the remote */ if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1) { fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno); exit(1); } else printf("[Client] Connected to server at port %d...ok!\n", PORT); /* Send File to Server */ //if(!fork()) //{ char* fs_name = "/home/aryan/Desktop/quotidiani.txt"; char sdbuf[LENGTH]; printf("[Client] Sending %s to the Server... ", fs_name); FILE *fs = fopen(fs_name, "r"); if(fs == NULL) { printf("ERROR: File %s not found.\n", fs_name); exit(1); } bzero(sdbuf, LENGTH); int fs_block_sz; while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0) { if(send(sockfd, sdbuf, fs_block_sz, 0) < 0) { fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno); break; } bzero(sdbuf, LENGTH); } printf("Ok File %s from Client was Sent!\n", fs_name); //} /* Receive File from Server */ printf("[Client] Receiveing file from Server and saving it as final.txt..."); char* fr_name = "/home/aryan/Desktop/progetto/final.txt"; FILE *fr = fopen(fr_name, "a"); if(fr == NULL) printf("File %s Cannot be opened.\n", fr_name); else { bzero(revbuf, LENGTH); int fr_block_sz = 0; while((fr_block_sz = recv(sockfd, revbuf, LENGTH, 0)) > 0) { int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr); if(write_sz < fr_block_sz) { error("File write failed.\n"); } bzero(revbuf, LENGTH); if (fr_block_sz == 0 || fr_block_sz != 512) { break; } } if(fr_block_sz < 0) { if (errno == EAGAIN) { printf("recv() timed out.\n"); } else { fprintf(stderr, "recv() failed due to errno = %d\n", errno); } } printf("Ok received from server!\n"); fclose(fr); } close (sockfd); printf("[Client] Connection lost.\n"); return (0); } 

server.c

 #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/wait.h> #include <sys/socket.h> #include <signal.h> #include <ctype.h> #include <arpa/inet.h> #include <netdb.h> #define PORT 20000 #define BACKLOG 5 #define LENGTH 512 void error(const char *msg) { perror(msg); exit(1); } int main () { /* Defining Variables */ int sockfd; int nsockfd; int num; int sin_size; struct sockaddr_in addr_local; /* client addr */ struct sockaddr_in addr_remote; /* server addr */ char revbuf[LENGTH]; // Receiver buffer /* Get the Socket file descriptor */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) { fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor. (errno = %d)\n", errno); exit(1); } else printf("[Server] Obtaining socket descriptor successfully.\n"); /* Fill the client socket address struct */ addr_local.sin_family = AF_INET; // Protocol Family addr_local.sin_port = htons(PORT); // Port number addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct /* Bind a special Port */ if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 ) { fprintf(stderr, "ERROR: Failed to bind Port. (errno = %d)\n", errno); exit(1); } else printf("[Server] Binded tcp port %d in addr 127.0.0.1 sucessfully.\n",PORT); /* Listen remote connect/calling */ if(listen(sockfd,BACKLOG) == -1) { fprintf(stderr, "ERROR: Failed to listen Port. (errno = %d)\n", errno); exit(1); } else printf ("[Server] Listening the port %d successfully.\n", PORT); int success = 0; while(success == 0) { sin_size = sizeof(struct sockaddr_in); /* Wait a connection, and obtain a new socket file despriptor for single connection */ if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1) { fprintf(stderr, "ERROR: Obtaining new Socket Despcritor. (errno = %d)\n", errno); exit(1); } else printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr)); /*Receive File from Client */ char* fr_name = "/home/aryan/Desktop/receive.txt"; FILE *fr = fopen(fr_name, "a"); if(fr == NULL) printf("File %s Cannot be opened file on server.\n", fr_name); else { bzero(revbuf, LENGTH); int fr_block_sz = 0; while((fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) > 0) { int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr); if(write_sz < fr_block_sz) { error("File write failed on server.\n"); } bzero(revbuf, LENGTH); if (fr_block_sz == 0 || fr_block_sz != 512) { break; } } if(fr_block_sz < 0) { if (errno == EAGAIN) { printf("recv() timed out.\n"); } else { fprintf(stderr, "recv() failed due to errno = %d\n", errno); exit(1); } } printf("Ok received from client!\n"); fclose(fr); } /* Call the Script */ system("cd ; chmod +x script.sh ; ./script.sh"); /* Send File to Client */ //if(!fork()) //{ char* fs_name = "/home/aryan/Desktop/output.txt"; char sdbuf[LENGTH]; // Send buffer printf("[Server] Sending %s to the Client...", fs_name); FILE *fs = fopen(fs_name, "r"); if(fs == NULL) { fprintf(stderr, "ERROR: File %s not found on server. (errno = %d)\n", fs_name, errno); exit(1); } bzero(sdbuf, LENGTH); int fs_block_sz; while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs))>0) { if(send(nsockfd, sdbuf, fs_block_sz, 0) < 0) { fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno); exit(1); } bzero(sdbuf, LENGTH); } printf("Ok sent to client!\n"); success = 1; close(nsockfd); printf("[Server] Connection with Client closed. Server will wait now...\n"); while(waitpid(-1, NULL, WNOHANG) > 0); //} } } 

一些评论没有特别的顺序:

  • 你错过了一个机会,经常知道确切的错误:

     if(listen(sockfd,BACKLOG) == -1) { printf("ERROR: Failed to listen Port %d.\n", PORT); return (0); } 

    这块应该肯定包括一个perror("listen")或类似的东西。 当错误细节将通过errno报告时,始终在每个错误处理块中包含perror()strerror() 。 具有确切的失败原因将节省您编程的时间,并在事情未能如预期的那样工作时为您和您的用户节省时间。

  • 您的错误处理需要进一步的标准化:

     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) { printf("ERROR: Failed to obtain Socket Descriptor.\n"); return (0); } 

    这不应该return 0因为这将发信号给外壳程序运行完成没有错误。 您应该return 1 (或使用EXIT_SUCCESSEXIT_FAILURE )来发出异常退出信号。

      else printf("[server] server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr)); /*Receive File from Client */ 

    在这个前面的块中,你已经得到了一个错误条件,但仍然继续执行。 这是获取非常不良行为的快速方法。 这应该重新启动主服务器循环或退出子进程或类似的东西。 (取决于是否保留多进程服务器。)

     if(!fork()) { 

    前面的块忘记占fork() 失败fork()可以并且确实失败 – 特别是在大学中常见的共享宿主环境中 – 所以你应该为fork()的完整,复杂的三个可能的返回值做准备:failure,child,parent。

  • 看来你正在使用fork()不分青红皂白地; 您的客户端和服务器都非常简单,而且它们的运行方式意味着它们不能同时用于为多个客户端服务。 您应该坚持每个过程一个进程,至少在算法完全调试之前,您可以找出一些方法同时运行多个客户机。 我希望这是你现在遇到的问题的根源。

  • 你需要使用函数来封装细节; 编写连接到服务器的函数,发送文件的函数,写入文件的函数等。编写一个函数来处理复杂的部分写入。 (我特别建议从Unix环境书的源代码中的高级编程中 lib/writen.c写函数,如果你写的功能是正确的,你可以在客户端和服务器端重新使用它们。 (像把它们放在utils.c然后像gcc -o server server.c utils.c一样编译程序)

    每个做一件事的小函数将允许你一次只关注少量的代码, 为每个代码写一点测试,这将帮助你缩小哪些代码段仍然需要改进。

一个讨论点似乎在这里失踪,所以我想在这里提到它。

让我们很快理解TCP的数据传输。 有三个步骤a)连接建立,b)数据传输,c)连接终止

现在,客户端通过TCP套接字将文件发送到服务器。

服务器对该文件进行一些处理并将其发送回客户端。

现在所有的3个步骤都需要完成。 连接建立通过调用connect完成。 数据读取/写入是通过recv / send在这里完成的,连接终止是通过关闭完成的。

这里的服务器正在使用recv在循环中读取数据。 现在,当循环结束 ? 当recv返回0或错误时可能小于0。 当recv返回0? – >当对方关闭连接时。 (当TCP FIN分段被这边收回时)。

所以在这个代码中,当客户端发送文件的时候,我已经使用了一个关闭函数,它从客户端发送FIN segement,服务器的recv现在可以返回0,程序继续。(一半关闭,因为客户端也需要随后读取数据)。

(只是为了理解,请注意TCP的连接建立是一个3路握手和连接终止是一个4路握手。)

同样,如果您忘记关闭服务器端的连接套接字,客户端的recv也会永远阻塞。 我认为这是使用ctrl c来停止客户端的原因,有时你已经提到过。

你可以pl。 参考任何标准的网络书籍或rfc http://www.ietf.org/rfc/rfc793.txt了解更多关于TCP

我已经粘贴了修改后的代码,也很少添加一些评论,

希望这个解释会有所帮助。

修改客户端代码:

  while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0) { if(send(sockfd, sdbuf, fs_block_sz, 0) < 0) { fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno); exit(1); } bzero(sdbuf, LENGTH); } /*Now we have sent the File's data, what about server's recv? Recv is blocked and waiting for data to arrive or if the protocol stack receives a TCP FIN segment ..then the recv will return 0 and the server code can continue */ /*Sending the TCP FIN segment by shutdown and this is half way close, since the client also needs to read data subsequently*/ shutdown(sockfd, SHUT_WR); printf("Ok File %s from Client was Sent!\n", fs_name);