FTP云盘
參考: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系統編程能力的,在學習系統編程的時候,我還學習了進程間通信,如管道,信號,共享內存,消息隊列等?,F在正在優化這個項目,想把這塊知識用到項目中去,下次遇到項目的話就比較得心應手,做開發就是要多多折騰嘛。
功能說明
本文是基于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; }運行效果
V2.0版 – 啟用副服務器
總結
- 上一篇: 宽容与忍耐 忍乃济——这是《尚书》这部中
- 下一篇: 秒跳转html代码,5秒定时跳转指定网页