参考:FTP云盘项目
作者:糯米啊啊
发布时间: 2021-08-19 10:34:05
网址:https://blog.csdn.net/weixin_43732386?spm=1001.2014.3001.5509
参考:自制FTP云盘项目
作者:不说话的小脑斧
发布时间: 2021-01-13 12:02:23
网址:https://blog.csdn.net/qq_44745336/article/details/112547781?spm=1001.2014.3001.5502以及:https://blog.csdn.net/zouchengzhi1021/article/details/113668089
目录
- 项目简介
- 功能说明
- 代码编写
- config.h
- 服务端
- 客户端
- 运行效果
- V2.0版 -- 启用副服务器
项目简介
FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务。 FTP是File Transfer
Protocol(文件传输协议)。
程序运行,服务端不断接收客户端指令,服务端可同时处理多个客户端接入并对指令作出解析,并把执行结果返回给客户端,客户端根据服务端对指令的解析并把由服务端传递过来的处理信息通过客户端呈现给客户,实现文件的各种操作。
可作为嵌入式面试话术使用
这个项目分成ftp客户端及服务端,实现的功能和Linux开源的ftp服务器类似,客戶端通过网络,远程获取服务端磁盘上的文件夹内容,下载文件,上传文件等功能。(基本功能描述)
ftp服务器用到的是Socket通信,当收到客户端接入的时候,创建子进程对接连接,子进程启动后分析来自客户端的指令,比如收到get file1的指令,是客户端想要获取file1文件的,我先用strstr函数进行字符串分割,获取到文件名,在判断文件是否存在,如果文件存在,就读取文件內容,再将內容通过套接字发给客户端,客户端收到数据后,创建文件,并将收到的数据写入文件,完成文件的远程下载。(说明网络编程,字符串编程,文件编程的功底)
上传文件和下载文件类似,主要还是涉及文件的操作,字符串的操作,以及网络编程。
还支持了Is’pwd,cd等Linux系统常用的指令。普通指令的实现用popen来调用系统质量,并读取执行的结构。如果不需要获取执行结果,用system函数调用就可以了。(说明popen,system的编程)
这个项目我是来锻炼我的LinUx系统编程能力的,在学习系统编程的时候,我还学习了进程间通信,如管道,信号,共享内存,消息队列等。现在正在优化这个项目,想把这块知识用到项目中去,下次遇到项目的话就比较得心应手,做开发就是要多多折腾嘛。
功能说明
本文是基于Linux网络编程实现的FTP服务器,服务器由服务端和客户端组成,具有浏览远程服务端的文件和浏览客户端本地文件,同时支持对远程服务端文件的删除,存储,归档操作处理,以及客户端对远程服务端文件的上传和下载。
利用socket实现云盘的基本功能:
- ls———查看服务端文件
- lls———查看客户端自己的文件
- cd———切换服务端目录
- lcd———切换客户端自己的目录
- put———上传文件
- get———下载文件
- pwd———显示路径
- quit———退出
代码编写
config.h
#define LS 1
#define LLS 2
#define CD 3
#define GET 4
#define PUT 5
#define PWD 6
#define QUIT 0typedef struct msg //传递信息的结构体
{int type;//没有用到char cmd[128];//存放命令char data_buf[1024];//存放文件内容
}Msg;
服务端
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"int Analysis_Command(char *buf) //分析命令
{//int strcmp(const char *s1, const char *s2);//字符串比较if(strcmp("ls",buf)==0) return LS;if(strcmp("pwd",buf)==0) return PWD;if(strcmp("quit",buf)==0) return QUIT;if(strcmp("lls",buf)==0) return LLS;//char *strstr(const char *haystack, const char *needle);//搜索一个字符串在另一个字符串中的第一次出现if(strstr(buf,"cd")!=NULL && strstr(buf,"lcd")==NULL) return CD;if(strstr(buf,"get")!=NULL) return GET;//因为用户输入指令 带参数if(strstr(buf,"put")!=NULL) return PUT;return -1;
}char *getDirName(char *cmd) //获取文件名 最好定义成 get_dir(函数名 变量名)
{char *fileName = NULL;//char *strtok(char *s, char *delim)//分解字符串 str 为一组字符串,delim 为分隔符,返回值:分隔符匹配到的第一个子串fileName = strtok(cmd," "); //分割字符串fileName = strtok(NULL," "); //strtok函数固定用法 return fileName;
}int dowork(int *c_fd) //函数名规范定义 msgs_Handler
{int cmd = 666; //无效的值FILE *p_fd; //popen的返回值类型int n_read; char *file_name = NULL;Msg r_msg_buf; //socket通信间信息的载体Msg w_msg_buf; //socket通信间信息的载体memset(&r_msg_buf,0,sizeof(Msg));memset(&w_msg_buf,0,sizeof(Msg));// ssize_t recv(int sockfd, void *buf, size_t len, int flags);//与read功能类似//视频里讲 用read读完就没了 这里使用recv返回值为0判断客户端断开连接//ssize_t read(int fd, void *buf, size_t count);read(*c_fd,&r_msg_buf,sizeof(Msg)); //从socket套接字中读取命令cmd = Analysis_Command(r_msg_buf.cmd); //分析命令switch(cmd){ //客户端那边也有一样的这些指令 相互对接case LS:p_fd = popen("ls -l","r"); //调用popen函数执行 "ls-l"if(p_fd == NULL) //判断是否popen成功{printf("popen error\n");exit(-1);} fread(w_msg_buf.data_buf,1024,1,p_fd);//从块设备读到缓存write(*c_fd,&w_msg_buf,sizeof(Msg)); //缓存发给客户端,客户端调用read获得结果fclose(p_fd); //关闭printf("get cmd : %s\n",r_msg_buf.cmd); //服务端调试信息break;case PWD: //pwd和ls一样p_fd = popen("pwd","r");//调用popen函数执行 "pwd"if(p_fd == NULL){printf("popen error\n");exit(-1);}fread(w_msg_buf.data_buf,1024,1,p_fd);//将popen执行结果放在w_msg_buf.data_buf中write(*c_fd,&w_msg_buf,sizeof(Msg));//通过套接字将w_msg_buf.data_buf写到客户端吗,客户端调用read,然后输出即可获得结果fclose(p_fd);printf("get cmd : %s\n",r_msg_buf.cmd);//服务端调试信息break;case LLS:printf("get cmd : lls\n"); //lls指令是打印出客户端该目录下的文件,所以服务端不做操作,只打印一个调试信息break;case GET:file_name = getDirName(r_msg_buf.cmd);//获取文件名if(access(file_name,F_OK)==0)//通过文件名判断文件是否存在{int fd = open(file_name,O_RDWR);read(fd,w_msg_buf.data_buf,1024);write(*c_fd,&w_msg_buf,sizeof(Msg));//如果存在,即打开,读取,写入。close(fd);}else{strcpy(w_msg_buf.data_buf,"no this document!");//不存在则写入"no this document!"write(*c_fd,&w_msg_buf,sizeof(Msg));}printf("get cmd : %s %s\n",r_msg_buf.cmd,file_name);break;case CD: //进入服务端某文件夹file_name = getDirName(r_msg_buf.cmd);//(分割)获取文件夹名if(access(file_name,F_OK)==0)//判断该文件是否存在{//int chdir(const char *path)//改变当前工作目录chdir(file_name);//系统调用函数(同cd)改变当前目录,即进入了文件夹//不能用system(源码是fork 另起了一个shell 这里要求自己进入文件夹)strcpy(w_msg_buf.data_buf,file_name);write(*c_fd,&w_msg_buf,sizeof(Msg));}else{strcpy(w_msg_buf.data_buf,"the server no have this file directory!");//如果没有则写入"the server no have this file directory!"write(*c_fd,&w_msg_buf,sizeof(Msg));}printf("get cmd : %s %s\n",r_msg_buf.cmd,file_name);//服务端调试信息break;case PUT: //上传某文件到服务端file_name = getDirName(r_msg_buf.cmd);read(*c_fd,&r_msg_buf,sizeof(Msg));if(strcmp(r_msg_buf.data_buf, "The client no have this document!") != 0){if(access(file_name,F_OK)==0) //如果文件存在{int fd = open(file_name,O_RDWR|O_TRUNC);//调用O_TRUNC将源文件内容删除,写入新内容write(fd,r_msg_buf.data_buf,strlen(r_msg_buf.data_buf));close(fd);}else{int fd = creat(file_name, 0666);//如果不存在,创建if(fd == -1){perror("creat error!\n");}if(write(fd, r_msg_buf.data_buf, strlen(r_msg_buf.data_buf)) == -1)//写入新内容{perror("write error!\n");}close(fd);}}printf("get cmd : %s %s\n",r_msg_buf.cmd,file_name);break;case QUIT: //客户端退出write(*c_fd, &w_msg_buf, sizeof(Msg));printf("=====client exit=====\n");close(*c_fd);exit(0);break;case -1:strcpy(w_msg_buf.data_buf, "Command error!");//指令错误write(*c_fd, &w_msg_buf, sizeof(Msg));printf("===== cmd error =====\n");break;}return 0;
}int main(int argc,char *argv[])
{ int s_fd;int c_fd;struct sockaddr_in s_addr; struct sockaddr_in c_addr; //socket套接字所需要的结构体memset(&s_addr,0,sizeof(struct sockaddr_in));//清空memset(&c_addr,0,sizeof(struct sockaddr_in));int clen = sizeof(struct sockaddr_in);if(argc != 3){ //判断运行时传参是否正确printf("param error\n");exit(-1);}s_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&(s_addr.sin_addr));//1.socket//创建套接字//int socket(int domain, int type, int protocol);s_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){printf("socket error\n");perror("why");}//2.bind 绑定IP号及端口//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);if(bind(s_fd,(struct sockaddr *)&s_addr,clen) == -1){printf("bind error\n");perror("why");}//3.lieten 监听// int listen(int sockfd, int backlog);if(listen(s_fd,10) == -1){printf("listen error\n");}while(1){//4.accept 连接//int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);if(c_fd == -1){printf("accept error\n");perror("why");}if(fork()==0){ //有客户端连接,创建子进程来对接printf("***hava client***\n");while(1){dowork(&c_fd);}}}close(s_fd);return 0;
}
客户端
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"int Analysis_Command(char *buf)//分析指令
{if(strcmp(buf,"ls")==0) return LS;if(strcmp(buf,"lls")==0) return LLS;if(strcmp(buf,"pwd")==0) return PWD;if(strcmp(buf,"quit")==0) return QUIT;if(strstr(buf,"cd")!=NULL && strstr(buf,"lcd")==NULL) return CD;if(strstr(buf,"get")!=NULL) return GET;if(strstr(buf,"put")!=NULL) return PUT;return -1;
}char *getDirName(char *cmd)//获取文件名
{char *file_Name = NULL;file_Name = strtok(cmd," ");file_Name = strtok(NULL," ");//strtok函数固定用法return file_Name;
}int doWork(int *c_fd)
{int cmd = 666;//无效的值FILE *p_fd;//popen的返回类型为FILE*char *file_name = NULL;Msg w_msg_buf;Msg r_msg_buf;//套接字信息传递过程中的载体memset(&w_msg_buf,0,sizeof(Msg));memset(&r_msg_buf,0,sizeof(Msg));//清空printf(">>");gets(w_msg_buf.cmd);//获取用户输入write(*c_fd,&w_msg_buf,sizeof(Msg));//将用户输入的指令写入套接字中,供服务端读取cmd = Analysis_Command(w_msg_buf.cmd);//分析指令printf("*****************************\n");printf("\n");switch(cmd){case LS:read(*c_fd,&r_msg_buf,sizeof(Msg));//读取客户端popen的返回内容printf("%s",r_msg_buf.data_buf);//打印printf("***********************\n");break;case PWD:read(*c_fd,&r_msg_buf,sizeof(Msg));//读取客户端popen的返回内容printf("%s",r_msg_buf.data_buf);//打印printf("***********************\n");break;case LLS:p_fd = popen("ls -l","r");//调用popen函数执行"ls-l"fread(r_msg_buf.data_buf,1024,1,p_fd);//客户端自己读取自己popen返回的内容printf("%s\n",r_msg_buf.data_buf);//打印fclose(p_fd);break;case GET: //下载文件read(*c_fd,&r_msg_buf,sizeof(Msg));//读取客户端写入来的文件内容file_name = getDirName(w_msg_buf.cmd);//获取文件名if(strcmp(r_msg_buf.data_buf,"no this document!")==0){printf("%s\n",r_msg_buf.data_buf);}else{if(access(file_name,F_OK)==0)//判断文件是否存在{int fd = open(file_name,O_RDWR|O_TRUNC);//存在即打开,O_TRUNC作用是将源文件内容全部删除以方便写入新内容if(fd==-1){printf("open error!\n");perror("why");}else{int w_ret = write(fd,r_msg_buf.data_buf,strlen(r_msg_buf.data_buf));//写入内容if(w_ret==-1){printf("write error!\n");perror("why");}close(fd);}}else{int fd = creat(file_name, 0666);//不存在即创建if(fd == -1){perror("creat error: ");}if(write(fd, r_msg_buf.data_buf, strlen(r_msg_buf.data_buf)) == -1)//写入{perror("write error: ");}close(fd);}printf("%s download success!\n",file_name);//提示下载成功}break;case CD: //进入某文件夹read(*c_fd,&r_msg_buf,sizeof(Msg));if(strcmp(r_msg_buf.data_buf,"the server no have this file directory!")==0){printf("%s\n",r_msg_buf.data_buf);}else{printf("enter %s\n",r_msg_buf.data_buf);}printf("get cmd: CD\n");break;case PUT: //上传某文件至服务端file_name = getDirName(w_msg_buf.cmd);if(access(file_name, F_OK) == 0){int fd = open(file_name, O_RDWR);read(fd, w_msg_buf.data_buf, 1024);write(*c_fd, &w_msg_buf, sizeof(Msg));close(fd);}else{strcpy(w_msg_buf.data_buf,"The client no have this document!");write(*c_fd,&w_msg_buf,sizeof(Msg));printf("%s\n",w_msg_buf.data_buf);}printf("get cmd : put %s\n",file_name);break;case QUIT:exit(0);break;default :read(*c_fd, &r_msg_buf, sizeof(Msg));printf("%s\n", r_msg_buf.data_buf);break;}return 0;
}int main(int argc,char *argv[])
{int c_fd;struct sockaddr_in c_addr;if(argc != 3){printf("param error\n");exit(-1);}int clen = sizeof(struct sockaddr);memset(&c_addr,0,sizeof(struct sockaddr_in));//1.socket//创建套接字//int socket(int domain, int type, int protocol);c_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){printf("client socket error\n");perror("why");}c_addr.sin_family = AF_INET;c_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&(c_addr.sin_addr));//2.connect 连接//客户连接主机//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);if(connect(c_fd,(struct sockaddr *)&c_addr,clen) == -1){printf("conncet error\n");perror("why");}while(1){doWork(&c_fd);}return 0;
}