本小节我们通过C语言在Linux系统上编写简易FTP服务器代码。
什么是FTP服务器?
FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务。 FTP是File Transfer Protocol(文件传输协议)。顾名思义,就是专门用来传输文件的协议。简单地说,支持FTP协议的服务器就是FTP服务器。
所设计的简易FTP服务器代码包含服务器端server.c和客户端client.c。服务器通过fork函数生成多个进程方式连接多个客户端。客户端也通过fork函数同时进行接收数据处理和发送数据处理。
通过客户端输入指令实现以下功能:
1、获取服务器的文件:scp XXX (XXX表示文件名,可包含路径)
2、上传文件到服务器:load XXX(XXX表示文件名,可包含路径)
3、显示服务器当前文件夹路径:spwd
4、显示客户端当前文件夹路径:lpwd
5、显示服务器中任意路径的文件:sls 路径名 (当路径为空时,显示当前路径的文件)
6、显示客户端中任意路径的文件:lls 路径名 (当路径为空时,显示当前路径的文件)
7、客户端退出:quit
spwd、lpwd、sls、lls指令都是通过popen函数来执行shell命令。
scp、load指令原理相同,先是打开并读取文件,向接收文件端发送数据包头(本项目是字符串”file startxxx”,xxx为文件名),再发文件内容,最后发数据包尾(本项目是字符串”file exit”)。
具体实现原理不详细阐述,可看代码细细体会。
1、服务器端
//文件server.c #include#include #include #include #include #include #include #include #include #include int main(int argc,char* argv[]) { if(argc!=3) { printf("arg error!n"); exit(-1); } int sockfd,c_fd; struct sockaddr_in s_addr,c_addr; socklen_t c_size; pid_t pid1; char* c_ip; char read_buf[1024]=""; char write_buf[1024]=""; int read_size=0; int write_size=0; memset(&s_addr,0,sizeof(struct sockaddr_in)); memset(&c_addr,0,sizeof(struct sockaddr_in)); s_addr.sin_family=AF_INET; s_addr.sin_port=htons(atoi(argv[2])); inet_aton(argv[1],&(s_addr.sin_addr)); sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd==-1) { printf("socket create fail!n"); } if(bind(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1) { printf("bind fail!n"); exit(-1); } else { printf("server:%s %dn",argv[1],ntohs(s_addr.sin_port)); } if(listen(sockfd,5)==-1) { printf("listen fail!n"); } c_size=sizeof(struct sockaddr_in); while(1) { if((c_fd=accept(sockfd,(struct sockaddr*)&c_addr,&c_size))==-1) { printf("ip+port get errorn"); perror("why"); close(c_fd); } else { c_ip=inet_ntoa(c_addr.sin_addr); printf("connect from:%s %dn",c_ip,ntohs(c_addr.sin_port)); if((pid1=fork())<0) { printf("fork fail!n"); close(c_fd); } else if(pid1==0) { FILE* fp; int fd1; char ret[1024]; int nread; char* str_loop; char* str_info=malloc(1024); int file_size; int count; char* file_loop; int file_flag=0; int n_write; char *char_p; int this_index=0; int index=0; while(1) { memset(read_buf,' ',1024); if((read_size=read(c_fd,read_buf,1024))==-1) { printf("read error!n"); } strncpy(read_buf,read_buf,read_size); if(strcmp(read_buf,"file exit")==0) { file_flag=0; close(fd1); printf("file load finish!n"); } else if(file_flag==1) { n_write=write(fd1,read_buf,read_size); if(n_write==-1) { printf("write fail!n"); perror("why"); } } else if((file_loop=strstr(read_buf,"file start"))!=NULL) { file_flag=1; file_loop+=strlen("file start"); printf("loading file:%sn",file_loop); char_p=file_loop; this_index=0; index=0; while(1) { if(*char_p== ' ') break; else if(*char_p=='/') this_index=index+1; index++; char_p++; } char_p=file_loop; while(1) { if(*(char_p+this_index)==' ') { *char_p=' '; break; } *char_p=*(char_p+this_index); char_p++; } fd1=open(file_loop,O_RDWR|O_CREAT|O_TRUNC,0600); } else { //printf("%sn",read_buf); } if((str_loop=strstr(read_buf,"sls"))!=NULL) { str_loop+=3; memset(ret,' ',1024); if(read_size!=3) { sprintf(str_info,"ls %s",str_loop); fp=popen(str_info,"r"); } else { fp=popen("ls","r"); } nread=fread(ret,1,1024,fp); strncpy(ret,ret,nread); if(write(c_fd,ret,strlen(ret))==-1) { printf("write error!n"); } pclose(fp); } if(strcmp(read_buf,"spwd")==0) { memset(ret,' ',1024); fp=popen("pwd","r"); nread=fread(ret,1,1024,fp); strncpy(ret,ret,nread); if(write(c_fd,ret,strlen(ret))==-1) { printf("write error!n"); } pclose(fp); } if((str_loop=strstr(read_buf,"scp"))!=NULL) { str_loop+=3; printf("finding file:%sn",str_loop); memset(ret,' ',1024); fp=popen("ls","r"); nread=fread(ret,1,1024,fp); strncpy(ret,ret,nread); pclose(fp); memset(ret,' ',1024); fd1=open(str_loop,O_RDWR); if(fd1==-1) { printf("dont find file!n"); if(write(c_fd,"fail",strlen("fail"))==-1) { printf("write error!n"); } } else { file_size=lseek(fd1,0,SEEK_END); lseek(fd1,0,SEEK_SET); count=(file_size/1023)+1; nread=0; memset(str_info,' ',1024); sprintf(str_info,"file start%s",str_loop); if(write(c_fd,str_info,strlen(str_info))==-1) { printf("write error!n"); } usleep(100000); while(count--) { memset(ret,' ',1024); nread=read(fd1,ret,1023); if(nread==-1) printf("read fail!n"); else { strncpy(ret,ret,nread); if(write(c_fd,ret,strlen(ret))==-1) { printf("write error!n"); } } } usleep(100000); if(write(c_fd,"file exit",strlen("file exit"))==-1) { printf("write error!n"); } printf("file load finish!n"); } close(fd1); } if(strcmp(read_buf,"fail")==0) { printf("file load fail!n"); } if(strcmp(read_buf,"quit")==0) { printf("client quit!n"); if(write(c_fd,"quit",strlen("quit"))==-1) { printf("write error!n"); } close(c_fd); exit(0); } } } } } close(sockfd); return 0; }
2、客户端
//文件client.c #include三、运行代码#include #include #include #include #include #include #include #include #include int main(int argc,char* argv[]) { pid_t pid; if(argc!=3) { printf("arg error!n"); exit(-1); } int client_fd; int read_size=0; int write_size=0; struct sockaddr_in client_addr; char write_buf[1024]; char read_buf[1024]; memset(&client_addr,0,sizeof(struct sockaddr_in)); client_fd=socket(AF_INET,SOCK_STREAM,0); client_addr.sin_family=AF_INET; client_addr.sin_port=htons(atoi(argv[2])); inet_aton(argv[1],&(client_addr.sin_addr)); int flag=connect(client_fd,(struct sockaddr*)&client_addr,sizeof(struct sockaddr)); if(flag==-1) { printf("connect fail!n"); exit(-1); } printf("connect:%s %s success!n",argv[1],argv[2]); if((pid=fork())==-1) { printf("fork fail!n"); } else if(pid>0) { FILE* fp; char *char_p; int fd1; char ret[1024]; int nread; char* str_loop; char* str_info=malloc(1024); int file_size; int count; while(1) { memset(write_buf,' ',1024); char_p=write_buf; while(1) { scanf("%c",char_p); if(*char_p=='n') { *char_p=' '; break; } else if(*char_p==' ') continue; else char_p++; } if((str_loop=strstr(write_buf,"load"))!=NULL) { str_loop+=4; printf("finding file:%sn",str_loop); fd1=open(str_loop,O_RDWR); if(fd1==-1) { if(write(client_fd,"fail",strlen("fail"))==-1) { printf("write error!n"); } printf("dont find file!n"); } else { file_size=lseek(fd1,0,SEEK_END); lseek(fd1,0,SEEK_SET); count=(file_size/1023)+1; nread=0; memset(str_info,' ',1024); sprintf(str_info,"file start%s",str_loop); if(write(client_fd,str_info,strlen(str_info))==-1) { printf("write error!n"); } usleep(100000); while(count--) { memset(ret,' ',1024); nread=read(fd1,ret,1023); if(nread==-1) printf("read fail!n"); else { strncpy(ret,ret,nread); if(write(client_fd,ret,strlen(ret))==-1) { printf("write error!n"); } } } usleep(100000); if(write(client_fd,"file exit",strlen("file exit"))==-1) { printf("write error!n"); } printf("file load finish!n"); close(fd1); } } else if((str_loop=strstr(write_buf,"lls"))!=NULL) { str_loop+=3; memset(ret,' ',1024); if(strlen(write_buf)!=2) { sprintf(str_info,"ls %s",str_loop); fp=popen(str_info,"r"); } else { fp=popen("ls","r"); } nread=fread(ret,1,1024,fp); strncpy(ret,ret,nread); printf("%s",ret); pclose(fp); } else if(strcmp(write_buf,"lpwd")==0) { memset(ret,' ',1024); fp=popen("pwd","r"); nread=fread(ret,1,1024,fp); strncpy(ret,ret,nread); printf("%s",ret); pclose(fp); } else { if(!((strstr(write_buf,"scp")!=NULL)||(strstr(write_buf,"sls")!=NULL)||(strstr(write_buf,"spwd")!=NULL)||(strstr(write_buf,"quit")!=NULL))) printf("this command dont exist!n"); else { if(write(client_fd,write_buf,strlen(write_buf))==-1) { printf("write error!n"); } } } if(strcmp(write_buf,"quit")==0) { //printf("write quit!n"); break; } } } else { char* file_loop; int file_flag=0; int fd1; int n_write; char *char_p; int this_index=0; int index=0; while(1) { memset(read_buf,' ',1024); if((read_size=read(client_fd,read_buf,1024))==-1) { printf("read quit!n"); } strncpy(read_buf,read_buf,read_size); if(strcmp(read_buf,"file exit")==0) { file_flag=0; close(fd1); printf("file load finish!n"); } else if(file_flag==1) { n_write=write(fd1,read_buf,read_size); if(n_write==-1) { printf("write fail!n"); perror("why"); } } else if((file_loop=strstr(read_buf,"file start"))!=NULL) { file_flag=1; file_loop+=strlen("file start"); printf("loading file:%sn",file_loop); char_p=file_loop; this_index=0; index=0; while(1) { if(*char_p== ' ') break; else if(*char_p=='/') this_index=index+1; index++; char_p++; } char_p=file_loop; while(1) { if(*(char_p+this_index)==' ') { *char_p=' '; break; } *char_p=*(char_p+this_index); char_p++; } fd1=open(file_loop,O_RDWR|O_CREAT|O_TRUNC,0600); } else if(strcmp(read_buf,"fail")==0) { printf("file load fail!n"); } else if(strcmp(read_buf,"quit")==0) { //printf("read quit!n"); break; } else { printf("%s",read_buf); } } } close(client_fd); return 0; }
1、客户端
sh@ubuntu:~/Desktop/client$ ./client 192.168.31.219 8888 //运行client IP 端口号 connect:192.168.31.219 8888 success! lls //显示客户端当前文件夹的文件 client client.c page2.txt lpwd //显示客户端当前路径 /home/sh/Desktop/client sls //显示服务器当前文件夹的文件 page1.txt server server.c spwd //显示服务器当前路径 /home/sh/Desktop/ftp scp page1.txt //获取服务器page1.txt文件 loading file:page1.txt file load finish! load page2.txt //上传客户端page2.txt文件 finding file:page2.txt file load finish! scp /home/sh/Desktop/page3.txt //获取服务器/home/sh/Desktop路径的page3.txt文件 loading file:/home/sh/Desktop/page3.txt file load finish! quit //客户端退出
2、服务器端
sh@ubuntu:~/Desktop/ftp$ ./server 192.168.31.219 8888 //运行server IP 端口号 server:192.168.31.219 8888 connect from:192.168.31.219 47854 finding file:page1.txt //查找文件 file load finish! loading file:page2.txt //接收文件 file load finish! finding file:/home/sh/Desktop/page3.txt //查找文件 file load finish! client quit! //客户端退出四、总结
本节所设计的FTP服务器与系统自带的FTP服务器相比,比较简单。小伙伴们还可以自行添加功能,比如用户密码机制等,还可以试试用交叉编译生成执行文件在树莓派运行,实现不同平台之间的文件传输。



