一、理论
FTP服务器
客户端:
1. 获取服务器的文件 get xxx
2. 展示服务器有哪些文件 ls
3. 进入服务器某文件夹 cd xx
4. 上传文件到服务器 put xx
1. lls查看客户端本地文件
2. lcd进入客户端xxx文件夹
服务器:
初始化,创建子进程,对接,解析指令,执行指令,返回指令的结果
一个子进程负责一条链接通道
二、编程实战
ftpServer.c
#include<stdio.h>
#include<error.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include <unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PWD 0
#define LS 1
#define GET 2
#define IFGO 3
#define LLS 4
#define LCD 5
#define CD 6
#define QUIT 7
#define PUT 8
#define DOFILE 9
struct Msg
{
int type;
char cmd[1024];
char readbuf[128];
};
int msg_cmd(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;//字符串比较
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(strstr(cmd,"cd")!=NULL) return CD;//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串
if(strstr(cmd,"get")!=NULL) return GET;
if(strstr(cmd,"put")!=NULL) return PUT;
}
char *getDir(char *cmd)//分割字符串
{
char *p;
p=strtok(cmd," ");
p=strtok(NULL," ");
return p;
}
void msg_handler(struct Msg msg,int fd)
{
int ret;
int f_fd;
FILE *file;
char *dir=NULL;
char readbuf[128]={0};
printf("cmd:%s\n",msg.cmd);//打印客户端的指令
ret=msg_cmd(msg.cmd);//客户端的字符串转换成整数
switch(ret)
{
case LS:
case PWD:
file=popen(msg.cmd,"r");//对于服务器,直接运行,ls列出当前文件,pwd列出当前路径
fread(&msg.cmd,sizeof(msg.cmd),1,file);//读出执行的结果
write(fd,&msg,sizeof(msg));//写入socket通道fd,并发送给客户端
break;
case CD:
dir=getDir(msg.cmd);
chdir(dir);
break;
case PUT:
dir=getDir(msg.cmd);
f_fd=open(dir,O_RDWR|O_CREAT,0666);
write(f_fd,&msg.readbuf,sizeof(msg.readbuf));
close(f_fd);
break;
case GET:
dir=getDir(msg.cmd);
if(access(dir,F_OK)==-1)
{
strcpy(msg.cmd,"file not exists!");
write(fd,&msg,sizeof(msg));
}else{
msg.type=DOFILE;
f_fd=open(dir,O_RDWR);//打开文件
read(f_fd,readbuf,sizeof(readbuf));//读文件
strcpy(msg.cmd,readbuf);//拷贝文件
close(f_fd);
write(fd,&msg,sizeof(msg));//发文件
}
break;
case QUIT:
printf("client quit !\n");
exit(-1);
}
}
int main(int args,char *argv[])
{
int s_fd;
int c_fd;
int n_read;
int n_write;
int n_bind;
if(args!=3)
{
perror("input");
exit(-1);
}
struct Msg msg;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
if((s_fd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(-1);
}
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
if((n_bind=bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr)))==-1)
{
perror("bind");
exit(-1);
}
listen(s_fd,10);
int len=sizeof(struct sockaddr_in);
while(1)
{
if((c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&len))==-1)
{
perror("accept");
exit(-1);
}
printf("client connected : %s\n",inet_ntoa(c_addr.sin_addr));
if(fork()==0)
{
while(1){
memset(&msg.cmd,0,sizeof(msg.cmd));
if((n_read=read(c_fd,&msg,sizeof(msg)))==0)
{
printf("client quit !\n");
exit(-1);
}else if(n_read>0)
{
msg_handler(msg,c_fd);
}
}
}
}
return 0;
}
ftpClient.c
#include<stdio.h>
#include<error.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PWD 0
#define LS 1
#define GET 2
#define IFGO 3
#define LLS 4
#define LCD 5
#define CD 6
#define QUIT 7
#define PUT 8
#define DOFILE 9
struct Msg
{
int type;
char cmd[1024];
char readbuf[128];
};
int msg_cmd(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("lls",cmd)) return LLS;
if(!strcmp("quit",cmd)) return QUIT;
if(strstr(cmd,"cd")!=NULL) return CD;
if(strstr(cmd,"get")!=NULL) return GET;
if(strstr(cmd,"put")!=NULL) return PUT;
if(strstr(cmd,"lcd")!=NULL) return LCD;
return -1;
}
char *getDir(char *cmd)
{
char *p;
p=strtok(cmd," ");
p=strtok(NULL," ");
return p;
}
int msg_handler(struct Msg msg,int fd)
{
int ret;
int f_fd;
char readbuf[128]={0};
char *dir=NULL;
ret=msg_cmd(msg.cmd);
switch(ret)
{
case LS:
case PWD:
case GET:
write(fd,&msg,sizeof(msg));//往服务器发指令,就ok
break;
case LLS:
system("ls");
break;
case LCD:
dir=getDir(msg.cmd);
chdir(dir);
break;
case CD:
write(fd,&msg,sizeof(msg));
break;
case PUT:
strcpy(readbuf,msg.cmd);
dir=getDir(readbuf);
if(access(dir,F_OK)==-1)
{
printf("file not exists!\n");
}else{
f_fd=open(dir,O_RDWR);
read(f_fd,&msg.readbuf,sizeof(msg.readbuf));
close(f_fd);
write(fd,&msg,sizeof(msg));
}
break;
case QUIT:
strcpy(msg.cmd,"quit");
write(fd,&msg,sizeof(msg));
exit(-1);
}
return ret;
}
void msg_handler_server(struct Msg msg,int fd)
{
struct Msg msgget;
int n_read;
int f_fd;
char *dir;
if((n_read=read(fd,&msgget,sizeof(msgget)))==0)//读取服务器回传的数据
{
printf("server quit\n");
exit(-1);
}else if(msgget.type==DOFILE)
{
dir=getDir(msg.cmd);
f_fd=open(dir,O_RDWR|O_CREAT,0600);
write(f_fd,&msgget.cmd,sizeof(msgget.cmd));
close(f_fd);
putchar('>');
fflush(stdout);
}else{//输出结果
printf("------------------------------\n");
printf("\n%s\n",msgget.cmd);
printf("------------------------------\n");
putchar('>');
}
}
int main(int args,char *argv[])
{
int c_fd;
int n_read;
int n_write;
int c_con;
struct Msg msg;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(args!=3)
{
perror("input");
exit(-1);
}
if((c_fd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(-1);
}
c_addr.sin_family=AF_INET;
c_addr.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
if((c_con=connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)))==-1){
perror("connect");
exit(-1);
}
int mark=0;
int ret;
while(1)
{
memset(&msg.cmd,0,sizeof(msg.cmd));
if(mark==0) printf(">");
gets(msg.cmd);//获取用户不断的输入
if(strlen(msg.cmd)==0)//为了让代码格式好看点
{
if(mark==1)
{
printf(">");
}
}
mark=1;//为了让代码格式好看点
ret=msg_handler(msg,c_fd);
if(ret>IFGO)
{
putchar('>');
fflush(stdout);
continue;
}
if(ret==-1)
{
printf("command out\n");
putchar('>');
fflush(stdout);
continue;
}
msg_handler_server(msg,c_fd);
}
return 0;
}
CLC@Embed_Learn:~/ftp$ gcc ftpServer.c -o s
CLC@Embed_Learn:~/ftp$ gcc ftpClient.c -o c
CLC@Embed_Learn:~/ftp$ ./s 127.0.0.1 8888
CLC@Embed_Learn:~/ftp$ ./c 127.0.0.1 8888
支持上述指令,自行可验证