linux小项目FTP云盘实现
前言
这两天将之前学的linux基础知识(文件IO操作、进程、线程、网络编程、进程间通信)重新过了一遍,嗯整体来说比第一次学习对知识点的理解更加深刻了,很多之前模棱两可的概念,也有了自己的理解。俗话说:“温故而知新,可以为师矣”这些古人的说的都是很有道理的。
对于我个人学习来说,可能并不是很聪明,但是我也相信勤能补拙,所以也愿意花时间去将之前学的知识点一遍又一遍的复习,将学习到的知识点通过一个又一个的小实验进行演示,这样的学习方式对我个人来说是很有效的,确实感觉到了自己的不断进步。
对于编程学习来说,光通过看视频就想变强,可以说真的不可能,真的只能不断的敲代码,不断的制造Bug 又不断的花时间去解决最后不断的总结知识点,这样我们才能在编程道路上不断的成长。对每个新知识的学习,我自己感觉还是很有自信的,总的来说自己还是比较自律,也有明确的计划和目标,嗯,总之每天都要让自己前进一小步 加油加油。(一直都在奋斗的小殷同学)
嗯 FTP云盘这个小项目服务端和客户端加起来差不多500多行代码,当然对于其中的很多细节可能并没有处理,只是大概将功能实现了,get xxx 和 put xxx进行文件的下载和上传时还有一些小问题,不过能可以获取到我们想要的内容,后面会在优化。
提示:以下是本篇文章正文内容,下面案例可供参考
一、服务端 Server实现
/*********************************FTP-Server************************************
*实现客户端与服务端消息发送(服务端只进行接受处理指令)
*Date:2022-2-25
*Author:小殷同学
*Version:1.0
*******************************************************************************/
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
//命令宏定义
#define LS 1
#define CD 2
#define PWD 3
#define LCD 4
#define LLS 5
#define GET 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
#define MAX_LISTEN 20 //同时进行最大监听数
struct FTP
{
int type; //类型
char databuf[1024]; //数据
char buf[1024]; //数据
char cmd[24]; //命令
};
struct FTP msg;
int get_cmd_type(char *cmd );
char *getDesDir(char *str);
void MSG_Handler(struct FTP msg,int fd);//信息处理
int main(int argc,char *argv[])
{
int s_fd = 0,c_fd = 0;
int addr_len = 0;
int n_read = 0;
struct sockaddr_in s_addr,c_addr;
//1.创建套接字
//int socket(int domain, int type, int protocol);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == 0)
{
printf("socket error\n");
exit(-1);
}
//清空
//void *memset(void *s, int c, size_t n);
memset(&s_addr,'0',sizeof(struct sockaddr_in));
memset(&c_addr,'0',sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
//uint16_t htons(uint16_t hostshort);
//int atoi(const char *nptr);
s_addr.sin_port = htons(atoi(argv[2])); //端口号转换
//int inet_aton(const char *cp, struct in_addr *inp);
inet_aton(argv[1],&s_addr.sin_addr); //IP 地址
//2.绑定
// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)) == -1)
{
printf("bind error\n");
exit(-1);
}
//3.监听
//int listen(int sockfd, int backlog);
if(listen(s_fd,MAX_LISTEN) == -1)
{
printf("listen error\n");
exit(-1);
}
addr_len = sizeof(struct sockaddr_in);
while(1)
{
//4.接受
static int i = 0;
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&addr_len);
if(c_fd == -1)
{
printf("accept error\n");
exit(-1);
}
i++;
//char *inet_ntoa(struct in_addr in);
//连接成功将每次的ip和端口打印
printf("connect success No %d client ip:%s port:%d\n",i,inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
//5.读写
if(fork() == 0)
{
while(1)
{
printf("--------------------------wait cmd----------------------\n");
n_read = read(c_fd,msg.cmd,sizeof(msg.cmd));
if(n_read > 0)
{
MSG_Handler(msg,c_fd);
}
else if(n_read == 0)
{
printf("Client had quit connect\n");
exit(0);
}
else
{
printf("read error\n");
exit(-1);
}
}
}
}
//6.关闭
close(c_fd);
close(s_fd);
return 0;
}
/*---------------------------------------相关功能函数--------------------------------*/
//获取命令
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;
return 100;
}
//获取分隔文件
char *getDesDir(char *str)
{
char *p;
p = strtok(str," "); //str 要分隔的字符串 以空格进行分隔
p = strtok(NULL," "); //第二次要以NULL 作为str
printf("p is <%s>\n",p); //打印获取的文件
return p;
}
//服务端获取命令后进行处理解析
void MSG_Handler(struct FTP msg,int fd)
{
int ret = 0;
FILE *fp = NULL;
char *file = NULL; //获取文件
int ffile = 0; //文件打开返回描述符
int len = 0; //计算文件长度
char buf[1024] = {0}; //缓存内容
char *p = NULL;
//获取命令
ret = get_cmd_type(msg.cmd);
printf("Get cmd = %d\n",ret);
switch(ret)
{
case LS:
case PWD:
//FILE *popen(const char *command, const char *type);
fp = popen(msg.cmd,"r"); //将获取的内容通过popen 函数打印出来
if(NULL == fp)
{
printf("popen failed\n");
exit(-1);
}
//读取
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread(msg.databuf,sizeof(msg.databuf),1,fp);
pclose(fp);
//将获取的信息通过c_fd写回客户端
//ssize_t write(int fd, const void *buf, size_t count);
write(fd,msg.databuf,sizeof(msg.databuf));
break;
case CD:
p = getDesDir(msg.cmd);
chdir(p); //int chdir(const char *path); 路径处理
break;
case GET:
//获取文件
file = getDesDir(msg.cmd);
//判断该文件是否存在
//int access(const char *pathname, int mode);
if(access(file,F_OK) == -1) //不存在
{
//写入错误信息
strcpy(msg.databuf,"this file not exit");
//将信息返回给客户端
write(fd,msg.databuf,sizeof(msg.databuf));
}
//该文件存在
//打开文件将文件所以内容复制到buf中 在将其赋值给msg.data 发送到客户端
ffile = open(file,O_RDWR); //以可读可写的方式打开
len = lseek(ffile,0,SEEK_END); //计算文件总长度
lseek(ffile,0,SEEK_SET); //将其再次移动到开头 否则读取不到数据
read(ffile,buf,len); //读取内容
close(ffile);
msg.type = DOFILE; //标志
strcpy(msg.databuf,buf); //将读取的内容复制到msg.data
write(fd,&msg,sizeof(msg)); //发送到客户端
printf("get success\n");
break;
case QUIT:
printf("Client quit\n");
exit(-1);
break;
case PUT:
//先判断文件是否存在 在打开 将内容写入
ffile = open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0666);
write(ffile,msg.buf,strlen(msg.buf));
close(ffile);
default:
strcpy(buf,"cmd not exit");
write(fd,msg.databuf,sizeof(msg.databuf));
break;
}
}
二、客户端 Client 实现
/*********************************FTP-Client************************************
*实现客户端与服务端消息发送(服务端只进行接受处理指令)
*Date:2022-2-25
*Author:小殷同学
*Version:1.0
*******************************************************************************/
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
//命令宏定义
#define LS 1
#define CD 2
#define PWD 3
#define LCD 4
#define LLS 5
#define GET 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
#define MAX_LISTEN 20 //同时进行最大监听数
struct FTP
{
int type; //类型
char databuf[1024]; //数据
char buf[1024]; //数据
char cmd[24]; //命令
};
struct FTP msg;
int get_cmd_type(char *cmd );
char *getDesDir(char *str);
int MSG_Write_Handler(struct FTP msg,int fd);
void MSG_Read_Handler(struct FTP msg,int fd);
int main(int argc,char *argv[])
{
int c_fd = 0;
int ret = 0,mark = 0;
struct sockaddr_in c_addr;
//1.创建套接字
//int socket(int domain, int type, int protocol);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == 0)
{
printf("socket error\n");
exit(-1);
}
//清空
//void *memset(void *s, int c, size_t n);
memset(&c_addr,'0',sizeof(struct sockaddr_in));
c_addr.sin_family = AF_INET;
//uint16_t htons(uint16_t hostshort);
//int atoi(const char *nptr);
c_addr.sin_port = htons(atoi(argv[2])); //端口号转换
//int inet_aton(const char *cp, struct in_addr *inp);
inet_aton(argv[1],&c_addr.sin_addr); //IP 地址
//2.连接connect
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1)
{
printf("connect error\n");
exit(-1);
}
//连接成功 打印相关信息 IP
printf("connect success ip:%s\n",inet_ntoa(c_addr.sin_addr));
while(1)
{
//创建子进程进行命令写入 父进程进行数据接收显示
if(fork() == 0) //子进程创建成功
{
while(1)
{
//每次进行命令的输入将前一次清空
memset(msg.cmd,'0',sizeof(msg.cmd));
printf(">>>");
fflush(stdout);
//printf("input cmd:");
gets(msg.cmd);
//将命令发送给服务端
//write(c_fd,msg.cmd,sizeof(msg.cmd));
MSG_Write_Handler(msg,c_fd);
printf("---------------------send cmd done----------------\n\n");
}
}
while(1)
{
MSG_Read_Handler(msg,c_fd);
}
}
//6.关闭
close(c_fd);
return 0;
}
/*---------------------------------------相关功能函数--------------------------------*/
//获取命令
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(!strcmp("lls",cmd)) return LLS;
if(!strcmp("lcd",cmd)) return LCD;
if(strstr(cmd,"cd") != NULL) return CD;
if(strstr(cmd,"get") != NULL) return GET;
if(strstr(cmd,"put") != NULL) return PUT;
return 100;
}
char *getDesDir(char *str)
{
char *p;
p = strtok(str," ");
p = strtok(NULL," ");
printf("p is <%s>\n",p);
return p;
}
int MSG_Write_Handler(struct FTP msg,int fd)
{
int ret = 0;
char *dir = NULL;
int filefd = 0;
ret = get_cmd_type(msg.cmd);
switch(ret)
{
case LS:
case PWD:
case CD:
write(fd,msg.cmd,sizeof(msg.cmd));
break;
case GET:
//msg.type = DOFILE;
write(fd,msg.cmd,sizeof(msg.cmd));
break;
case LLS:
system("ls");
break;
case QUIT:
write(fd,msg.cmd,sizeof(msg.databuf));
exit(-1);
break;
case PUT:
//先判断该文件是否存在物
dir = getDesDir(msg.cmd);
if(access(dir,F_OK) == 0)
{
printf("file not exit\n");
fflush(stdout);;
}
//存在该内容读取到啊文件
filefd = open(dir,O_RDWR);
read(filefd,msg.buf,sizeof(msg.buf));
close(filefd);
//写入
write(fd,&msg,sizeof(msg));
break;
}
}
void MSG_Read_Handler(struct FTP msg,int fd)
{
int n_read = 0;
int newfilefd = 0;
n_read = read(fd,&msg,sizeof(msg));
if(n_read == 0)
{
printf("Server Had Quit\n");
exit(0);
}
else if(msg.type == DOFILE)
{
printf("had read file1\n");
char *file = getDesDir(msg.cmd); //获取该文件
newfilefd = open(file,O_RDWR | O_CREAT,0666); //以可读可写打开 如果不存在则创建
write(newfilefd,msg.databuf,strlen(msg.databuf));//将内容写入文件中
close(newfilefd);
printf(">>>");
fflush(stdout);
}
else
{
printf("---------------------get data------------------\n");
printf("%s\n",msg.databuf);
printf("------------------------------------------------\n\n");
printf(">>>");
fflush(stdout);
}
}
三、实现结果
总结
对自己学习的检验最好的方法就是做一个比较综合的小项目,查漏补缺,及时进行复习巩固。加油鸭