一、FTP服务器
本小节我们通过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 <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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 %d\n",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 error\n");
perror("why");
close(c_fd);
}
else
{
c_ip=inet_ntoa(c_addr.sin_addr);
printf("connect from:%s %d\n",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,'\0',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:%s\n",file_loop);
char_p=file_loop;
this_index=0;
index=0;
while(1)
{
if(*char_p== '\0')
break;
else if(*char_p=='/')
this_index=index+1;
index++;
char_p++;
}
char_p=file_loop;
while(1)
{
if(*(char_p+this_index)=='\0')
{
*char_p='\0';
break;
}
*char_p=*(char_p+this_index);
char_p++;
}
fd1=open(file_loop,O_RDWR|O_CREAT|O_TRUNC,0600);
}
else
{
//printf("%s\n",read_buf);
}
if((str_loop=strstr(read_buf,"sls"))!=NULL)
{
str_loop+=3;
memset(ret,'\0',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,'\0',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:%s\n",str_loop);
memset(ret,'\0',1024);
fp=popen("ls","r");
nread=fread(ret,1,1024,fp);
strncpy(ret,ret,nread);
pclose(fp);
memset(ret,'\0',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,'\0',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,'\0',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 <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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,'\0',1024);
char_p=write_buf;
while(1)
{
scanf("%c",char_p);
if(*char_p=='\n')
{
*char_p='\0';
break;
}
else if(*char_p==' ')
continue;
else
char_p++;
}
if((str_loop=strstr(write_buf,"load"))!=NULL)
{
str_loop+=4;
printf("finding file:%s\n",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,'\0',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,'\0',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,'\0',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,'\0',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,'\0',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:%s\n",file_loop);
char_p=file_loop;
this_index=0;
index=0;
while(1)
{
if(*char_p== '\0')
break;
else if(*char_p=='/')
this_index=index+1;
index++;
char_p++;
}
char_p=file_loop;
while(1)
{
if(*(char_p+this_index)=='\0')
{
*char_p='\0';
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服务器相比,比较简单。小伙伴们还可以自行添加功能,比如用户密码机制等,还可以试试用交叉编译生成执行文件在树莓派运行,实现不同平台之间的文件传输。