一、项目介绍
1、本项目基于Linux平台,主要结构分为ftp客户端和服务端,实现的功能和Linux开源的ftp服务器类似,客戶端通过网络IP地址和端口号,远程获取服务端磁盘上的文件夹内容,支持查看服务端文件内容并下载所需文件,或者查看本地文件内容并上传文件到服务器等功能,服务端主要为接受客户端接入信息和获取操作指令。
2、 ftp服务器实现思路:通过Socket通信建立套接字,当收到客户端接入的时候,创建子进程对接连接,子进程启动后分析来自客户端的指令。比如收到get file的指令,是客户端想要获取file文件,先用strstr函数进行字符串分割,获取到文件名,再判断文件是否存在,如果文件存在,就读取文件內容,再将內容通过套接字发给客户端,客户端收到数据后,创建文件,并将收到的数据写入文件,完成文件的远程下载。
3、ftp客户端实现思路:通过Socket通信建立套接字连接服务器,连接成功进入密码的验证,密码输入正确就进入命令行的输入,将输入的命令通过函数处理来判断是否发送到服务端处理,比如输入put file的指令,是客户端想要上传file文件,直接通过套接字将输入内容完整的发送到服务端进行处理,完成文件的远程上传;如输入lls指令,是客户想查看本地目录,直接调用system函数实现即可,不用发送到服务端。
涉及技术:网络编程、文件操作、字符串运用、进程操作、数据算法。(升级版:可通过共享内存和信号量等进程间通信处理服务端和客户端的信息交互)
二、基本功能
客户端密码验证进入ftp云盘;
客户端
ls:查看服务端当前路径下的所有文件;
lls:查看客户端当前路径下的所有文件;
cd xx:服务端进入xx路径;
lcd xx:客户端进入xx路径;
pwd:查看服务端当前路径;
lpwd:查看客户端当前路径;
get xx:从服务端当前路径获取xx文件到客户端当前路径上;
put xx:将客户端当前路径xx文件发送到服务端当前路径;
cat xx: 查看服务端中文件的具体内容
cad xx: 查看客户端中文件的具体内容
rm xx: 删除客户端中的文件(可自行拓展删除服务器文件等指令要求)
quit:断开服务器端连接,退出;
服务端
1、获取接入的客户端的IP地址( 可支持多个客户端同时接入)
2、不断监听客户端的指令( 接收指令,方便后台处理)。
3、在接收上面客户端的指令后,去执行指令。( 核心操作)
三、代码实现
服务端:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include "config1.h"
int get_cmd_type(char *cmd)//指令处理
{
if(!strcmp("ls",cmd)) return LS;//判断与客户端发送的指令是否相同
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("pwd",cmd)) return PWD;
if(strstr(cmd,"cd") !=NULL) return CD;
if(strstr(cmd,"get") !=NULL) return GET;
if(strstr(cmd,"put") !=NULL) return PUT;
if(strstr(cmd,"cat") !=NULL) return CAT;//判断客户端发送的指令是否存在某些字符
return 100;
}
char *getDesdir(char *cmsg)//字符串的分割
{
char *p;
p = strtok(cmsg," ");
p = strtok(NULL," ");
return p;
}
void msg_handler(struct Msg msg,int fd)
{
char dataBuf[1024] ={0};
char catBuf[1024] ={0};
char *file =NULL;
char *file1 =NULL;
int fdfile;
int fdcat;
printf("cmd:%s\n",msg.data);//获取客户端的输入指令
int ret = get_cmd_type(msg.data);
//指令处理
switch(ret){
case LS:
case PWD:
msg.type =0;
FILE *r =popen(msg.data,"r");//调用popen运行并获取运行结果
fread(msg.data,sizeof(msg.data),1,r);//读取运行结果
write(fd,&msg,sizeof(msg));//写在套接字,发给客户端
break;
case CD:
msg.type =1;
char *dir = getDesdir(msg.data);//分割客户端指令
printf("dir: %s\n",dir);
chdir(dir);//调用chadir改变当前目录,进入所需目录
break;
case CAT:
file1 =getDesdir(msg.data);
if(access(file1,F_OK == -1)){
strcpy(msg.data,"NO This File!");
write(fd,&msg,sizeof(msg));
//判断查看的服务端文件是否存在
}else{
//存在就打开它
fdcat =open(file1,O_RDWR);
read(fdcat,catBuf,sizeof(catBuf));//读取它
close(fdcat);
strcpy(msg.data,catBuf);
write(fd,&msg,sizeof(msg));//写在套接字,发给客户端
}
break;
case GET:
file =getDesdir(msg.data);
if(access(file,F_OK == -1)){
strcpy(msg.data,"NO This File!");//文件不存在提醒
write(fd,&msg,sizeof(msg));
}else{
msg.type=DOFILE;
fdfile =open(file,O_RDWR);
read(fdfile,dataBuf,sizeof(dataBuf));//文件存在读取它
close(fdfile);
strcpy(msg.data,dataBuf);
write(fd,&msg,sizeof(msg));//写在套接字,发给客户端
}
break;
case PUT:
fdfile =open(getDesdir(msg.data),O_RDWR|O_CREAT,0666);
//打开客户端上传文件,没有就创建它
write(fdfile,msg.secondBuf,strlen(msg.secondBuf));
close(fdfile);
break;
case QUIT:
printf("client quit!\n");//客户端退出提醒
exit(-1);
}
}
int main(int argc,char **argv)
{
int s_fd;
int c_fd;
int n_read;
char readBuf[128];
struct Msg msg;//定义指令和数据处理结构体,在config.h头文件
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc !=3){
printf("param is not good\n");
exit(-1);
}//主函数参数判断
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
s_fd=socket(AF_INET,SOCK_STREAM,0);//建立套接字
if(s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family=AF_INET;//设置协议族为tcp协议
s_addr.sin_port=htons(atoi(argv[2]));//设置端口号
inet_aton(argv[1],&s_addr.sin_addr);//设置IP地址
//2.bind 绑定套接字添加信息
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen 设置套接字为监听模式
listen(s_fd,10);
//4.accept
int clen=sizeof(struct sockaddr_in);
while(1){
c_fd =accept(s_fd,(struct sockaddr *)&c_addr,&clen);/接收客户端的连接请求,返回描述符c_fd
if(c_fd == -1){
perror("accept");
}
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//获取接入客户端IP地址
//5.read
if(fork() ==0){
//有客户端接入创建子进程处理它
while(1){
memset(msg.data,0,sizeof(msg.data));
n_read=read(c_fd,&msg,sizeof(msg));
if(n_read == 0){
printf("client out\n");//无数据提醒客户端退出
break;
}else if(n_read >0){
msg_handler(msg,c_fd);//有数据进行函数处理
}
}
}
}
close(c_fd);
close(s_fd);
return 0;
}
客户端主函数:
int main(int argc,char **argv)
{
int c_fd;
char passward[16]={0};//存储密码
struct sockaddr_in c_addr;
struct Msg msg;
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(argc !=3){
printf("param is not good\n");
exit(-1);
}
//1.socket
c_fd=socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -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);
//2.connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(-1);
}
printf("connect...\n");
while(1){
memset(passward,0,sizeof(passward));//清空密码
printf("please input passward:");
gets(passward);//密码的输入
if((strcmp(passward, "ftp") != 0)){
printf("passward error!!!\n");
}
else break;//密码正确跳出循环,错误不断提醒用户输入
}
printf("Welcome to zyf FTP Netdis!\n");
printf("--------------------------------\n");
int mark= 0;
while(1){
memset(msg.data,0,sizeof(msg.data));
if(mark == 0)
printf(">");
gets(msg.data);//输入指令
if(strlen(msg.data) == 0){
if(mark == 1){
printf(">");
}
continue;
}
mark =1;//格式调节,可忽略
int ret = cmd_handler(msg,c_fd);//处理输入指令
if(ret >IFGO){
putchar('>');
fflush(stdout);
continue;//客户端自身可以实现端,eg:lls指令
}
if(ret ==-1){
printf("command not found!\n");
putchar('>');
fflush(stdout);
continue;
//指令不存在提醒
}
handler_server_message(c_fd,msg);//需要发送服务器处理端
}
return 0;
}
config1.h
#define LS 0
#define GET 1
#define PWD 2
#define CAT 3
#define IFGO 4
#define LPWD 5
#define LCD 6
#define LLS 7
#define PUT 8
#define CAD 9
#define QUIT 10
#define DOFILE 11
#define CD 12
#define RM 13
struct Msg
{
int type;
char data[1024];//处理指令
char secondBuf[128];//处理数据
};
四、实现效果