FTP云盘

2023-11-07

参考: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

项目简介

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	0

typedef 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版 – 启用副服务器

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FTP云盘 的相关文章

随机推荐

  • Typora使用指南

    Typora使用指南 简介 Typora是一款轻便简洁的Markdown编辑器 支持即时渲染技术 这也是与其他Markdown编辑器最显著的区别 即时渲染使得你写Markdown就想是写Word文档一样流畅自如 不像其他编辑器的有编辑栏和显
  • Live555学习之路(一)

    有关live555的介绍 还是百度百科 http baike baidu com view 3495912 html fromTaglist 下载live555 http www live555 com liveMedia public 我
  • yolov4论文解读和训练自己数据集

    前天YOLOv4终于问世 YOLO v4 论文 https arxiv org abs 2004 10934 YOLO v4 开源代码 GitHub AlexeyAB darknet YOLOv4 Scaled YOLOv4 YOLO Ne
  • 简介setsockopt和udp的多播(组播)广播

    Tcp Udp中的单播 组播 广播 2019年06月27日 1 Setsockopt方法 1 1 setsockopt 函数 用于任意类型 任意状态套接口的设置选项值 int setsockopt int sockfd int level
  • JVM调优相关

    1 jvm中的一些工具 1 1 jps jps 用于查看java进程运行情况 输出JVM中运行的进程状态信息 命令行参数如下 m 输出传入main方法的参数 l 输出main类或Jar的全限名 v 输出传入JVM的参数 如上 bootstr
  • 逼格的一些小功能

    日常开发中 实用的一些小功能 比如各进制转换 计算内存地址什么的 分为C 部分和Unity部分 C 部分 只是存C 代码即可运行 Unity部分 需要用的Unity相关的api C 部分
  • TDK MPU9250的详细功能 替代方案ICM20948

    mpu9250的替代方案ICM20948 零知模块新品上架 ICM20948九轴模块替代MPU9250 附带示例 https www amobbs com thread 5722167 1 1 html ICM20948 TDK规格书 ht
  • 干货,一文彻底搞懂 Java 的 Optional

    想学习 永远都不晚 尤其是针对 Java 8 里面的好东西 Optional 就是其中之一 该类提供了一种用于表示可选值而非空引用的类级别解决方案 作为一名 Java 程序员 我真的是烦透了 NullPointerException NPE
  • Java判断String是否为空或不为空(并且equals的好的写法,不报空指针)

    首先澄清一个概念 String str if str null str equals 1 2 3 str null 说明str还未指向一个特定的字符串对象 此时谈不上是否为空 str 说明str是个空字符串 只不过长度为0 字符串判断空的几
  • bleve和es RediSearch 区别

    目录 区别 bleve支撑多大的数据量 bleve是否支持集群和分布式 bleve主要应用场景 区别 Bleve 和 Elasticsearch 是两个不同的搜索引擎库 而不是像 Bleve 和 RediSearch 那样相互竞争的产品 B
  • PAT (Basic Level) Practice (中文)1033 旧键盘打字

    旧键盘上坏了几个键 于是在敲一段文字的时候 对应的字符就不会出现 现在给出应该输入的一段文字 以及坏掉的那些键 打出的结果文字会是怎样 输入格式 输入在 2 行中分别给出坏掉的那些键 以及应该输入的文字 其中对应英文字母的坏键以大写给出 每
  • wireshark抓包红色_Wireshark使用教程:不同报文颜色的含义

    Wireshark色彩规则 在Wireshark主界面 报文会显示各种各样的颜色 它们表示不同的含义 这些颜色 是由色彩规则控制的 对这些颜色进行适当的了解 对分析报文有很大帮助 01 设置 色彩规则有两个入口 一个在报文上方的工具栏内 如
  • Java调用WebService接口的四种方式

    调用WebService 使用wsimport生成代码 不推荐 使用Axis 1 4 动态调用 使用HTTP SOAP方式远程调用 通过Spring注解方式调用 使用wsimport生成代码 不推荐 配置java环境变量后在命令窗口中输入
  • Keil提示错误L6218E:Undefined symbol TIM_OC2Init解决办法

    1 如题 我在将正点原子的PWM输出代码移植到led的程序上 编译后发现报了七个错误 都是L6218E 2 由于这个是标准库 程序中引用了很多ST官方的库函数 但是你没有引用 所以会报错 对比正点原子的代码内容和我的 发现我的FWLIB中缺
  • JavaScript 数据结构之数组

    JavaScript 数据结构之数组思维导图 JavaScript 数据结构之数组源码
  • Kafka最详细总结

    Kafka Kafka是最初由Linkedin公司开发 是一个分布式 支持分区的 partition 多副本的 replica 基于zookeeper协调的分布式消息系统 它的最大的特性就是可以实时的处理大量数据以满足各种需求场景 比如基于
  • mac android自动化测试学习心得,解决uiautomator视图界面无法打开的问题

    原因是我安装的jdk版本为9 0 4 彻底卸载后 安装8即可正常启动 输入命令 sudo rm fr Library Internet Plug Ins JavaAppletPlugin plugin sudo rm fr Library
  • 各版本Pytorch安装详解

    Pytorch安装教程 windows版本 conda安装 1 cuda9 0 python3 6 3 5 3 7 conda install pytorch c pytorch pip3 install torchvision 2 cud
  • AVFoundation 播放器实例

    播放器 使用苹果官方的 AVFoundation 框架 可以很容易的封装一个视频的播放器 在获取视频资源后 只需要将视频在播放图层中渲染即可 并且可以在图层中添加控件 以便对视频的播放和暂停进行控制 下面封装的库 主要包含两个类 HXJPl
  • FTP云盘

    参考 FTP云盘项目 作者 糯米啊啊 发布时间 2021 08 19 10 34 05 网址 https blog csdn net weixin 43732386 spm 1001 2014 3001 5509 参考 自制FTP云盘项目