ftp服务器使用说明
- 运行服务端
./server 本机IP地址 端口号(使用ifconfig查看本机IP 地址) - 运行客户端 ./client 服务器IP地址 相同端口号
运行成功后可执行以下指令,实现类似云盘功能
指令说明
- ls —— 查看服务端当下目录
- lls —— 查看客户端当下目录
- pwd —— 查看服务端当前路径
- quit —— 客户端退出连接
- cd name —— 令服务器进入到name路径下下
- get name —— 从服务器上下载name文件
- put name ——将客户端name文件上传到服务器
一、头文件defin.h——存储指令及数据
#define ls 1
#define pwd 2
#define cd 3
#define get 4
#define put 5
#define lls 6
#define quit 7
struct Msg
{
char com[100];//存储指令
char buf[1024];//存储数据
};
二、服务器server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "defin.h"
#include "sys/types.h"
#include <unistd.h>
#include "sys/socket.h"
#include <arpa/inet.h>
void ErrorReason(char *err,int count,int Errnum);
int Msg_Process(int SonId,char *Cmd);
int JudgeCmd(char *Cmd);
char *MsgPerform(int SonId,int Cmd,struct Msg *msg);
char *comcut(struct Msg *msg);
int count=0;
int main(int argc,char **argv)
{
if(argc!=3)
{
printf("error argc\n");
exit(-1);
}
int cp1;//用于辅助程序算法而设的标志位
int soc=socket(AF_INET,SOCK_STREAM,0);//创建套接字
ErrorReason("socket",soc,-1);
struct sockaddr_in soc_init;
struct in_addr soc_initaddr;
memset(&soc_init,0,sizeof(struct sockaddr_in));
memset(&soc_initaddr,0,sizeof(struct in_addr));
soc_init.sin_family=AF_INET;
soc_init.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&soc_initaddr);
soc_init.sin_addr.s_addr=soc_initaddr.s_addr;
cp1=bind(soc,(struct sockaddr *)&soc_init,sizeof(struct sockaddr));//把地址和端口号组合赋给socket
ErrorReason("bind",cp1,-1);
cp1=listen(soc,10);//监听连接请求,最大连接数10
ErrorReason("listen",cp1,-1);
struct sockaddr_in soc_on;
int len=sizeof(struct sockaddr_in);
int acc;
pid_t id;
struct Msg strdata;//用于存储指令和数据的结构体
int sizlen;//指针,文件等的长度
int strcmd;//存储宏定义后的指令
char *pdata=NULL;//存储指令执行后的数据
memset(&strdata,0,sizeof(struct Msg));
while(1)
{
count++;//用于区分客户端而设的标志位
acc=accept(soc,(struct sockaddr *)&soc_on,(socklen_t *)&len);//等待客户端接入
ErrorReason("accept",acc,-1);
id=fork();//创建子进程与客户端完成对接
ErrorReason("fork",id,-1);
if(id==0)//创建子进程与客户端完成对接
{
while(1)
{
memset(strdata.com,'\0',sizeof(strdata.com));
sizlen=recv(acc,(void *)(strdata.com),sizeof(strdata.com),0);//阻塞直到客户端向服务端发送数据
if(sizlen>0)//数据正常处理
{
strcmd=Msg_Process(acc,strdata.com);//返回可执行的命令
if(strcmd==0)//如果命令有问题,则进入
{
send(acc,(void *)"error cmd",strlen("error cmd"),0);
continue;
}
printf("1:%s\n",strdata.com);
pdata=MsgPerform(acc,strcmd,&strdata);//处理命令,返回命令执行后的数据
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
if(strcmp(strdata.com,"lls"))
{
len=send(acc,(void *)pdata,strlen(pdata),0);//向客户端发送数据
}
}else
if(sizlen==0)//数据有问题处理
{
printf("recv\n");
break;
}
}
}
}
close(soc);
close(acc);
return 0;
}
int Msg_Process(int SonId,char *Cmd)//处理客服端发送的命令,返回优化后的命令
{
char *strcmd=Cmd;
int runcmd;
runcmd=JudgeCmd(strcmd);//返回优化后的指令
return runcmd;
}
int JudgeCmd(char *Cmd)//优化命令
{
char *p=NULL;
if(!strcmp(Cmd,"ls")) return ls;else
if(!strcmp(Cmd,"pwd")) return pwd;else
if(strstr(Cmd,"cd")!=NULL) return cd;else
if(strstr(Cmd,"get")!=NULL) return get;else
if(strstr(Cmd,"put")!=NULL) return put;else
if(strcmp(Cmd,"lls")==0) return lls;else
if(strcmp(Cmd,"quit")==0) return quit;else
return 0;
}
char *MsgPerform(int SonId,int Cmd,struct Msg *msg)//执行命令,返回命令结果
{
struct Msg *macom=msg;
long len;//指针,文件等的长度
int cp1;//用于辅助程序算法而设的标志位
char *str=NULL;//用于返回可执行的命令(字符串)
char str1[100]={'\0'};//用于返回可执行的命令(字符串)
char data[1024]={'\0'};
char *retudata=NULL;//执行命令后的数据存储区
FILE *fp=NULL;//打开文件用的文件描述符
switch(Cmd)
{
case ls:
case pwd://执行服务端shll指令
{
if(!strcmp((macom->com),"ls"))
{
fp=popen("/bin/ls","r");//建立管道IO
}else
if(!strcmp((macom->com),"pwd"))
{
fp=popen("/bin/pwd","r");//建立管道IO
}
if(fp==NULL)
{
perror("popen");
return "popen error";
}
fread(data,sizeof(char),sizeof(data),fp);//读执行shll指令得到的数据
fclose(fp);
retudata=data;
len=strlen(retudata);
return retudata;//返回执行指令得到的数据
}break;
case cd://服务端跳转文件夹
{
strcpy(str1,comcut(macom));//分割macom->com字符串,返回可执行命令
cp1=chdir(str1);//进入路径为str的文件夹
if(cp1==-1)
{
return "error cmd";
}
getcwd(data,sizeof(data));//把当前目录赋值给data
retudata=data;
len=strlen(retudata);
return retudata;//返回当前目录(字符串)
}break;
case get://从服务器上下载文件到客户端
{
strcpy(str1,comcut(macom));//分割macom->com字符串,返回可执行命令
fp=fopen(str1,"r");//打开str文件
if(fp==NULL)
{
return "error cmd";
}
fseek(fp,0,SEEK_END);//把光标移动到文件流尾
len=ftell(fp);//取得文件流光标所在位置,读取文件大小
fseek(fp,0,SEEK_SET);//把光标移动到文件流首
len=fread((void *)data,sizeof(char),len,fp);//读取文件内容
retudata=data;
fclose(fp);
return retudata;//返回读取到的文件数据
}break;
case put://将客户端文件上传到服务器
{
memset(macom->buf,'\0',sizeof(macom->buf));
recv(SonId,(void *)(macom->buf),sizeof(macom->buf),0);
if(!(strcmp(macom->buf,"error")))//进入数据错误处理
{
return "error cmd";
}
strcpy(str1,(macom->com)+4);//拷贝com指令后面的文件名(字符串)
fp=fopen(str1,"w+");//创建文件str1
if(fp==NULL)
{
return "error cmd";
}
len=strlen(macom->buf);
fwrite((void *)(macom->buf),sizeof(char),len,fp);//写入数据到文件
fclose(fp);
return "put OK";
}break;
case lls://查看客户端当下目录
{
return "lls";
}break;
case quit://客户端退出(子进程退出)
{
printf("%dProcess exits\n",count);//服务端输出提示哪个进程退出,count为标志位
exit(3);//子进程退出
}break;
default:return "error com";
}
}
char *comcut(struct Msg *msg)//分割字符串,后处理字符串
{
int i=0;
int j=0;//用于辅助程序算法而设的标志位
char *p=NULL;//处理分割字符串用的辅助位
char *comaddr[10]={"NULL"};//存储分割字符串后的数据而设的指针数组
char str[100]={'\0'};
char str1[300]={'\0'};//存储命令处理后的数据
p=strtok(msg->com," ");//分割字符串
while(p!=NULL)//处理分割字符串
{
comaddr[i]=p;
p=strtok(NULL," ");
i++;
}
for(j=0;j<i-1;j++)//重修拼合指针数据的字符串
{
if(!strcmp(comaddr[0],"cd"))
{
sprintf(str,"/%s",comaddr[j+1]);
}else
if(!(strcmp(comaddr[0],"get")))
{
sprintf(str,"%s",comaddr[j+1]);
}
strcpy(str1,strcat(str1,str));
}
p=str1;
return p;
}
void ErrorReason(char *err,int count,int Errnum)//处理错误函数
{
if(count==Errnum)
{
perror(err);
exit(-1);
}
return;
}
客户端client.c
#include <stdio.h>
#include "defin.h"
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
void ErrorReason(char *err,int count,int Errnum);
int Msg_Process(int SonId,struct Msg *Cmd);
int JudgeCmd(struct Msg *Cmd);
struct Msg *PutCom(int SonId,int Cmd,struct Msg *msg);
struct Msg *GetBuf(int SonId,int Cmd,struct Msg *msg);
int main(int argc,char **argv)
{
if(argc!=3)
{
printf("error argc\n");
exit(-1);
}
int cp1;
int soc=socket(AF_INET,SOCK_STREAM,0);//创建套接字
ErrorReason("socket",soc,-1);
struct sockaddr_in soc_in;
struct in_addr in_a;
memset(&soc_in,0,sizeof(struct sockaddr_in));
memset(&in_a,0,sizeof(struct in_addr));
soc_in.sin_family=PF_INET;
soc_in.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&in_a);
soc_in.sin_addr.s_addr=(in_a.s_addr);
cp1=connect(soc,(struct sockaddr *)&soc_in,sizeof(struct sockaddr_in));//发起连接指定的网络地址
ErrorReason("connect",cp1,-1);
struct Msg strdata;
struct Msg *getdata=(struct Msg *)malloc(sizeof(struct Msg));//用于存储服务端发送的数据
int len;//用于计算指针、字符串长度等
int strcmd;
while(1)
{
memset(strdata.com,'\0',sizeof(strdata.com));
fgets(strdata.com,sizeof(strdata.com),stdin);//获取命令
len=strlen(strdata.com);
*(strdata.com+len-1)='\0';
strcmd=Msg_Process(soc,&strdata);//返回可执行的命令
if(strcmd==0)//如果命令有问题,则进入
{
printf("error cmd\n");
continue;
}
PutCom(soc,strcmd,&strdata);//向服务端发送命令
memset(getdata->buf,'\0',sizeof(getdata->buf));
getdata=GetBuf(soc,strcmd,&strdata);//返回命名执行后的数据(返回服务端发送过来的的数据)
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
if(strcmd==lls)
{
system("/bin/ls");
}else
{
printf("main:%s\n",getdata->buf);
}
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
close(soc);
return 0;
}
int Msg_Process(int SonId,struct Msg *Cmd)//返回可执行的命令
{
struct Msg *p=Cmd;
int runcmd;
runcmd=JudgeCmd(p);
return runcmd;
}
int JudgeCmd(struct Msg *Cmd)//优化命令
{
struct Msg *p=Cmd;
if(!strcmp(p->com,"ls")) return ls; else
if(!strcmp(p->com,"pwd")) return pwd; else
if(strstr(p->com,"cd")!=NULL) return cd; else
if(strstr(p->com,"get")!=NULL) return get; else
if(strstr(p->com,"put")!=NULL) return put; else
if(!strcmp(p->com,"lls")) return lls; else
if(!strcmp(p->com,"quit")) return quit;else
return 0;
}
struct Msg *PutCom(int SonId,int Cmd,struct Msg *msg)//向服务端发送命令
{
struct Msg *p=msg;
send(SonId,(void *)p->com,strlen(p->com),0);//向服务端发送命令
return p;
}
struct Msg *GetBuf(int SonId,int Cmd,struct Msg *msg)//返回命名执行后的数据(返回服务端发送过来的的数据)
{
FILE *fd=NULL;//用于打开文件用的文件描述符
char str[50]={'\0'};//用于存储继续处理后的命令
int len;//用于计算长度
struct Msg *getdata=msg;//用于存储服务端发送回的数据
memset(getdata->buf,0,sizeof(getdata->buf));
switch(Cmd)//进入相关指令进行工作
{
case ls:
case pwd://执行服务端shll指令
{
recv(SonId,(void *)getdata->buf,sizeof(getdata->buf),0);
len=strlen(getdata->buf);
}break;
case cd://服务端跳转文件夹
{
recv(SonId,(void *)getdata->buf,sizeof(getdata->buf),0);
len=strlen(getdata->buf);
}break;
case get://从服务器上下载文件到客户端
{
recv(SonId,(void *)getdata->buf,sizeof(getdata->buf),0);
len=strlen(getdata->buf);
if(!strcmp(getdata->buf,"error cmd"))
{
break;
}
len=strlen(getdata->buf);
strcpy(str,(getdata->com)+4);
fd=fopen(str,"w+");
if(fd==NULL)
{
perror("fopen error");
break;
}
fwrite((void *)(getdata->buf),sizeof(char),len,fd);
fclose(fd);
memset(getdata->buf,'\0',sizeof(getdata->buf));
strcpy(getdata->buf,"get OK");
}break;
case put://将客户端文件上传到服务器
{
strcpy(str,(getdata->com)+4);
len=strlen(str);
fd=fopen(str,"r");
if(fd==NULL)
{
usleep(100);
send(SonId,(void *)"error",strlen("error"),0);
recv(SonId,(void *)getdata->buf,sizeof(getdata->buf),0);
return getdata;
}
fseek(fd,0,SEEK_END);//将光标移动到文件流尾处
len=ftell(fd);//取得文件流光标所在位置
fseek(fd,0,SEEK_SET);
fread(getdata->buf,sizeof(char),len,fd);
len=strlen(getdata->buf);
send(SonId,(void *)getdata->buf,len,0);
fclose(fd);
memset(getdata->buf,'\0',sizeof(getdata->buf));
usleep(100);
recv(SonId,(void *)getdata->buf,sizeof(getdata->buf),0);
memset(getdata->buf,'\0',sizeof(getdata->buf));
strcpy(getdata->buf,"put OK");
}break;
case lls://查看客户端当下目录
{
strcpy(getdata->buf,"/bin/ls");
}break;
case quit://客户端退出(子进程退出)
{
strcpy(getdata->buf,"quit");
}break;
}
return getdata;
}
void ErrorReason(char *err,int count,int Errnum)//处理错误函数
{
if(count==Errnum)
{
perror(err);
exit(-1);
}
return;
}
运行结果:运行服务端成功后,可接入多个客户端
读者可亲自实验ftp服务器开发的乐趣