Linux C编程连载(4)-基于TCP/IP的文件传输系统

本文介绍了一种使用LinuxC编程实现的基于TCP/IP协议的文件传输系统,详细阐述了其设计目的、设计方案、设计流程及测试过程。系统支持任意类型文件的传输,采用二进制读写方式确保文件的正确性,通过分包操作有效传输大文件,确保了文件传输的可靠性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【更新】

2012-08-15,改进Makefile

2012-08-07,补充Makefile

【设计目的】

通过Linux C编程,设计一个基于TCP/IP的文件传输系统,实现网络文件的收发。

【设计环境】

VMware WorkStation 6.0.2+Fedora 10

【设计方案】

(1)文件读写

任意文件都可以二进制的方式进行读写,为了实现任意文件类型的传输,在读写文件的过程中,必须采用二进制的读写方式。

(2)传输协议

       为了保证数据的正确性,文件传输需要采用一种可靠的传输协议。UDP协议实现比较简单,但UDP面向无连接,传输过程中,会出现丢包的情况,导致数据发送失败。故采用面向连接的TCP/IP协议,防止传输过程中的数据丢失的情况。

(3)大文件的传输

对于比较大的文件,应该进行分包操作,以防止占用过多的内存,导致文件发送失败。

【设计流程】

如图1所示,服务器程序作为文件的发送方。首先,服务器端输入要发送的文件。然后,创建一个流式套接字(SOCK_STREAM),进行绑定。绑定成功后,执行监听,当有客户发送连接请求,执行Accept(),接收来自客户端的请求。

连接建立后,首先服务器向客服端发送的文件的文件名及扩展名等信息。信息发送完毕,服务器方将待发送的数据读入缓冲区,通过套接字将数据发送出去。发送完成后退出,并显示发送完成的信息。

 

图1 服务器流程图

       如图2所示,客户端程序完成文件的接收操作。首先,创建一个流式套接字。套接字创建成功后,对该套接字进行绑定。绑定成功后,向服务器方发送连接请求。连接成功后,首先,接收服务器发送的文件信息。接收成功后,开始数据的接收。文件接收完毕,显示文件已接收完成。

 

图2 客户端流程图

【设计测试】

为了验证设计的正确性,在Fedora 10平台上对可执行文件进行了回环测试。测试了.txt,.doc,.jpg,.pdf四种类型的文件,测试源文件如图3所示。

 

图3 测试源文件

(1).txt文件测试

       如图4所示,服务器端执行./fileserver命令,程序提示输入需要发送的文件。输入1.txt,此时服务器进入监听状态,等待客户端的连接。

       在客户端执行./fileclient 127.0.0.1,建立连接。此时,服务器端开始发送文件。并显示读取的文件大小为50 Bytes,小于缓冲区(4096 Bytes)大小,发送一次就可以了,不需要分包操作。发送完成,服务器端显示发送完成,客户端显示接收到来至服务器的文件。

 

图4 .txt文件测试

(2).doc文件测试

       如图5所示,服务器端执行./fileserver命令,程序提示输入需要发送的文件。输入test.doc,此时服务器进入监听状态,等待客户端的连接。

       在客户端执行./fileclient 127.0.0.1,建立连接。此时,服务器端开始发送文件。并显示读取的文件大小为19.5 KByte,大于缓冲区(4096 Bytes)大小,需要分包发送,图示进行了5次分包,前4次包的大小为4096 Bytes,最后一次为3584 Bytes。发送完成,服务器端显示发送完成,客户端显示接收到来至服务器的文件。

 

图5 .doc文件测试

(3).pdf文件测试

       如图6所示,服务器端执行./fileserver命令,程序提示输入需要发送的文件。输入test.pdf,此时服务器进入监听状态,等待客户端的连接。

       在客户端执行./fileclient 127.0.0.1,建立连接。此时,服务器端开始发送文件。并显示读取的文件大小为23.8 KByte,大于缓冲区(4096 Bytes)大小,需要分包发送,图示进行了6次分包,前5次包的大小为4096 Bytes,最后一次为3993 Bytes。发送完成,服务器端显示发送完成,客户端显示接收到来至服务器的文件。

 

图6 .pdf文件测试

(4).jpg文件测试

       如图7所示,服务器端执行./fileserver命令,程序提示输入需要发送的文件。输入test.jpg,此时服务器进入监听状态,等待客户端的连接。

       在客户端执行./fileclient 127.0.0.1,建立连接。此时,服务器端开始发送文件。并显示读取的文件大小为7.11 KByte,大于缓冲区(4096 Bytes)大小,需要分包发送,图示进行了2次分包,第1次包的大小为4096 Bytes,第2次为3189 Bytes。发送完成,服务器端显示发送完成,客户端显示接收到来至服务器的文件。

 

图7 .jpg文件测试

接收到的文件如图8所示,实验证实文件接收准确无误,没有发生丢包的情况,由于采取了分包的操作,能够传送较大的文件。

 

图8 接收文件夹

【结论】

实验证实,该网络文件系统由于采用二进制读写方式,从而能发送任意类型的文件。由于采用了分包操作,能发送比较大的文件。采用TCP/IP协议进行传输,从而保证了文件传输的可靠性。

 

【代码清单】

common.h

[html]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <string.h>  
  5. #include <sys/types.h>  
  6. #include <sys/stat.h>  
  7. #include <sys/socket.h>  
  8. #include <netinet/in.h>  
  9.   
  10. #define PORT 6000  
  11. #define LISTENQ 20  
  12. #define BUFFSIZE 4096  
  13. #define FILE_NAME_MAX_SIZE 512  


fileclient.c

[html]  view plain copy
  1. #include "common.h"  
  2.   
  3. int main(int argc, char **argv[])  
  4. {  
  5.     int clientfd;  
  6.   
  7.     if(argc!=2)  
  8.     {  
  9.         fprintf(stderr,"Usage:./fileclient <IP_Address>\n");  
  10.         exit(1);  
  11.     }  
  12.     struct sockaddr_in clientaddr;  
  13.     bzero(&clientaddr,sizeof(clientaddr));    
  14.   
  15.     clientaddr.sin_family=AF_INET;  
  16.     clientaddr.sin_addr.s_addr=htons(INADDR_ANY);  
  17.     clientaddr.sin_port=htons(0);  
  18.       
  19.     clientfd=socket(AF_INET,SOCK_STREAM,0);  
  20.       
  21.     if(clientfd<0)     
  22.     {  
  23.         perror("socket");  
  24.         exit(1);  
  25.     }  
  26.   
  27.     if(bind(clientfd,(struct sockaddr*)&clientaddr,sizeof(clientaddr))<0)  
  28.     {  
  29.         perror("bind");  
  30.         exit(1);  
  31.     }  
  32.   
  33.     struct sockaddr_in svraddr;  
  34.     bzero(&svraddr,sizeof(svraddr));  
  35.     if(inet_aton(argv[1],&svraddr.sin_addr)==0)  
  36.     {  
  37.         perror("inet_aton");  
  38.         exit(1);  
  39.     }  
  40.     svraddr.sin_family=AF_INET;  
  41.     svraddr.sin_port=htons(PORT);  
  42.       
  43.     socklen_t svraddrlen=sizeof(svraddr);  
  44.     if(connect(clientfd,(struct sockaddr*)&svraddr,svraddrlen)<0)      
  45.     {  
  46.         perror("connect");  
  47.         exit(1);  
  48.     }  
  49.       
  50.     //recv file imformation  
  51.     char buff[BUFFSIZE];  
  52.     char filename[FILE_NAME_MAX_SIZE];  
  53.     int count;  
  54.     bzero(buff,BUFFSIZE);  
  55.   
  56.     count=recv(clientfd,buff,BUFFSIZE,0);  
  57.     if(count<0)  
  58.     {  
  59.         perror("recv");  
  60.         exit(1);  
  61.     }  
  62.     strncpy(filename,buff,strlen(buff)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buff));  
  63.   
  64.     printf("Preparing recv file : %s from %s \n",filename,argv[1]);   
  65.   
  66.     //recv file  
  67.     FILE *fd=fopen(filename,"wb+");  
  68.     if(NULL==fd)  
  69.     {  
  70.         perror("open");  
  71.         exit(1);  
  72.     }  
  73.     bzero(buff,BUFFSIZE);  
  74.   
  75.     int length=0;  
  76.     while(length=recv(clientfd,buff,BUFFSIZE,0))  
  77.     {  
  78.         if(length<0)  
  79.         {  
  80.             perror("recv");  
  81.             exit(1);  
  82.         }  
  83.         int writelen=fwrite(buff,sizeof(char),length,fd);  
  84.         if(writelen<length)  
  85.         {  
  86.             perror("write");  
  87.             exit(1);  
  88.         }  
  89.         bzero(buff,BUFFSIZE);  
  90.     }  
  91.     printf("Receieved file:%s from %s finished!\n",filename,argv[1]);  
  92.     fclose(fd);  
  93.     close(clientfd);  
  94.     return 0;  
  95. }  

fileserver.c

[html]  view plain copy
  1. #include "common.h"  
  2.   
  3. int main(int argc, char **argv[])  
  4. {  
  5.     //Input the file name  
  6.     char filename[FILE_NAME_MAX_SIZE];  
  7.     bzero(filename,FILE_NAME_MAX_SIZE);  
  8.     printf("Please input the file name you wana to send:");  
  9.     scanf("%s",&filename);  
  10.     getchar();  
  11.   
  12.     //Create socket  
  13.     int sockfd,connfd;  
  14.     struct sockaddr_in svraddr,clientaddr;  
  15.     bzero(&svraddr,sizeof(svraddr));  
  16.       
  17.     svraddr.sin_family=AF_INET;  
  18.     svraddr.sin_addr.s_addr=htonl(INADDR_ANY);  
  19.     svraddr.sin_port=htons(PORT);  
  20.   
  21.     sockfd=socket(AF_INET,SOCK_STREAM,0);  
  22.     if(sockfd<0)  
  23.     {  
  24.         perror("socket");  
  25.         exit(1);  
  26.     }  
  27.   
  28.     //bind    
  29.     if(bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr))<0)  
  30.     {  
  31.         perror("bind");  
  32.         exit(1);  
  33.     }  
  34.   
  35.     //listen  
  36.     if(listen(sockfd,LISTENQ)<0)  
  37.     {  
  38.         perror("listen");  
  39.         exit(1);  
  40.     }  
  41.   
  42.     while(1)  
  43.     {  
  44.         socklen_t length=sizeof(clientaddr);  
  45.   
  46.         //accept  
  47.         connfd=accept(sockfd,(struct sockaddr*)&clientaddr,&length);  
  48.         if(connfd<0)  
  49.         {  
  50.             perror("connect");  
  51.             exit(1);  
  52.         }  
  53.       
  54.         //send file imformation  
  55.         char buff[BUFFSIZE];  
  56.         int count;  
  57.         bzero(buff,BUFFSIZE);  
  58.         strncpy(buff,filename,strlen(filename)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(filename));  
  59.         count=send(connfd,buff,BUFFSIZE,0);  
  60.         if(count<0)  
  61.         {  
  62.             perror("Send file information");  
  63.             exit(1);  
  64.         }  
  65.           
  66.         //read file   
  67.         FILE *fd=fopen(filename,"rb");  
  68.         if(fd==NULL)  
  69.         {  
  70.             printf("File :%s not found!\n",filename);  
  71.         }  
  72.         else   
  73.         {  
  74.             bzero(buff,BUFFSIZE);  
  75.             int file_block_length=0;  
  76.             while((file_block_length=fread(buff,sizeof(char),BUFFSIZE,fd))>0)  
  77.             {  
  78.                 printf("file_block_length:%d\n",file_block_length);  
  79.                 if(send(connfd,buff,file_block_length,0)<0)  
  80.                 {  
  81.                     perror("Send");  
  82.                     exit(1);  
  83.                 }  
  84.                 bzero(buff,BUFFSIZE);     
  85.             }  
  86.             fclose(fd);  
  87.             printf("Transfer file finished !\n");  
  88.         }  
  89.         close(connfd);  
  90.     }  
  91.     close(sockfd);  
  92.     return 0;  
  93. }  

 

 

【更新】2012-08-08

Makefile

[html]  view plain copy
  1. PROGS = fileserver fileclient  
  2. all:$(PROGS)  
  3. .PHONY:all  
  4. fileserver:  
  5.         gcc -g fileserver.c common.h -o fileserver  
  6. fileclient:  
  7.         gcc -g fileclient.c common.h -o fileclient  
  8.   
  9. clean:  
  10.         rm $(PROGS)  
  11. distclean:  
  12.         rm *~ $(PROGS)  


【改进版本 2012-08-15】

[html]  view plain copy
  1. PROGS = fileserver fileclient  
  2. temp = $(wildcard *~)  
  3. all:$(PROGS)  
  4. .PHONY:all  
  5. fileserver:  
  6.         gcc -g fileserver.c common.h -o fileserver  
  7. fileclient:  
  8.         gcc -g fileclient.c common.h -o fileclient  
  9.   
  10. clean:  
  11.         rm $(temp) $(PROGS)  


[html]  view plain copy
  1. SOURCE=$(wildcard *.c)  
  2. PROGS=$(patsubst %.c,%,$(SOURCE))  
  3. all:$(PROGS)  
  4.   
  5. clean:  
  6.         rm $(PROGS)  
  7. distclean:  
  8.         rm *~ $(PROGS)  
  9.                         

 

【改进版本 2012-08-15】

[html]  view plain copy
  1. SOURCE=$(wildcard *.c)  
  2. PROGS=$(patsubst %.c,%,$(SOURCE))  
  3. temp = $(wildcard *~)  
  4. all:$(PROGS)  
  5.   
  6. clean:  
  7.         rm $(temp) $(PROGS)  

*晦涩难懂,不推荐。

*改进后的Makefile版本,可自适应的删除临时文件:若临时文件存在,则删除临时文件;若临时文件不存在,则不执行删除临时文件。


 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.youkuaiyun.com/tandesir

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值