FTP云盘整体功能
先认识一些API
popen应用于执行shell命令,并读取此命令的执行结果
FILE *popen(const char *command, const char *type);
popen()的返回值是个标准I/O流,必须由pclose来终止
int pclose(FILE *stream);
在这里主要做清空内存使用
void *memset(void *s, int c, size_t n);
s指向要填充的内存块。
c是要被设置的值。
n是要被设置该值的字符数。
返回类型是一个指向存储区s的指针。
比较字符串s1和s2
int strcmp(const char *s1,const char *s2);
自左向右逐个按照ASCII码值进行比较,直到出现不同的字符或遇’\0’为止。
如果返回值 < 0,则表示 s1 小于 s2。
如果返回值 > 0,则表示 s1 大于 s2。
如果返回值 = 0,则表示 s1 等于 s2
char *strstr(const char *str1, const char *str2)
查找第一次出现字符串 s2 的位置,未找到返回NULL
char *strtok(char *str, const char *delim)
str—要被分解的字符串
delim—用作分隔符的字符(可以是一个,也可以是集合)
第二次要把str设置为NULL
char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest
需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。
int system(const char * command)
执行 dos(windows系统) 或 shell(Linux/Unix系统) 命令,执行结果显示在终端```
#include<unistd.h>
int access(const char* pathname, int mode);
pathname 是文件的路径名+文件名
mode:指定access的作用,取值如下:
F_OK 值为0,判断文件是否存在
X_OK 值为1,判断对文件是可执行权限
W_OK 值为2,判断对文件是否有写权限
R_OK 值为4,判断对文件是否有读权限
注:后三种可以使用或“|”的方式,一起使用,如W_OK|R_OK
源码
分为三个文件:server.c 、client.c、config.h
server端一直在accept,等待客户端连接,有客户端接入就创建子进程对接处理指令
config.h
/*配置*/
#define LS 0 //显示服务端的文件
#define PWD 1 //显示服务端路径
#define CD 2 //切换服务端路径
#define GET 3 //获取服务端文件
#define PUT 4 //上传客户端文件到服务端
#define LLS 5 //显示客户端文件
#define LCD 6 //切换客户端路径
#define QUIT 7 //退出客户端
#define CLC 8 //客户端清屏
struct Msg {
char data[1024]; //存放指令
char buffer[1024*4]; //存放数据
};
server.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/*struct sockaddr_in
{
u_short sin_family;// 地址族
u_short sin_port;// 端口
struct in_addr sin_addr;// IPV4 地址
char sin_zero[8];
};*/
int Cmd_transition(char *cmd)
{
/* strcmp(s1,s1)比较 s1 和 s2
若s1=s2,返回0*/
/*strstr(s1,s1)在字符串 s1 中
查找第一次出现字符串 s2 的位置,未找到返回NULL*/
if(strcmp(cmd,"ls") == 0){
return LS;
}
if(strcmp(cmd,"pwd") == 0){
return PWD;
}
if(strcmp(cmd,"lls") == 0){
return LLS;
}
if(strcmp(cmd,"quit") == 0){
return QUIT;
}
if(strstr(cmd,"lcd") != NULL){
return LCD;
}
if(strstr(cmd,"cd") != NULL){
return CD;
}
if(strstr(cmd,"get") != NULL){
return GET;
}
if(strstr(cmd,"put") != NULL){
return PUT;
}else {
return -1;
}
}
char *Cmd_division(char *cmd)
{
char *p = NULL;
strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
int Cmd_dispose(struct Msg msg,int c_fd)
{
int Cmd_int;
int File_fd;
char *File_name;
FILE *p;
/* 1.获取命令msg.data,并将数据转换为int整数类型*/
Cmd_int = Cmd_transition(msg.data);
printf("server Cmd_int:%d\n",Cmd_int);
/* 2.调用switch处理命令*/
switch(Cmd_int){
case LS:
case PWD:
p = popen(msg.data,"r"); //使用popen调用命令,将输出的结果存放到p中
fread(msg.buffer,sizeof(msg.buffer),1,p); //再使用fread,读取p中的内容
write(c_fd,&msg,sizeof(msg));
pclose(p);
//printf("server return \n");
break;
case CD:
File_name = Cmd_division(msg.data);
printf("File_name:%s\n",File_name);
if(chdir(File_name) == -1){
perror("chdir");
printf("chdir eror\n");
}
break;
case GET:
File_name = Cmd_division(msg.data);
if(access(File_name,F_OK) == -1){
printf("file does not exist ");
strcpy(msg.buffer,"file does not exist\n");
write(c_fd,&msg,sizeof(msg));
}else {
File_fd = open(File_name,O_RDWR);
read(File_fd,&msg.buffer,sizeof(msg.buffer));
write(c_fd,&msg,sizeof(msg));
}
break;
case PUT:
File_name = Cmd_division(msg.data);
//printf("File_name:%s\n",File_name);
if(access(File_name,F_OK) != -1){
strcpy(msg.buffer,"The file already exists\n");
write(c_fd,&msg,sizeof(msg));
}else {
File_fd = open(File_name,O_RDWR|O_CREAT,0666);
if(File_fd == -1&&errno != EEXIST ){
printf("open %s error\n",File_name);
}
write(File_fd,&msg.buffer,sizeof(msg.buffer));
close(File_fd);
strcpy(msg.buffer,"put file succeed\n");
write(c_fd,&msg,sizeof(msg));
}
break;
}
}
int main()
{
int s_fd;
int c_fd;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8888);
inet_aton("192.168.20.131",&s_addr.sin_addr);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
printf("socket error\n");
exit(-1);
}
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
listen(s_fd,5);
int c_len = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1){
perror("accept:");
exit(-1);
}
printf("client ip :%s\n",inet_ntoa(c_addr.sin_addr));
struct Msg msg;
int n_read;
if(fork() == 0)
while(1){
memset(&msg,0,sizeof(msg));
n_read = read(c_fd,&msg,sizeof(msg));
//printf("get %s cmd\n",msg.data);
if(strcmp(msg.data,"quit") == 0){
printf("client exit\n");
exit(0);
}
if(n_read == 0){
printf("client abnormal exit\n");
exit(-1);
}
if(n_read > 0){
Cmd_dispose(msg,c_fd);
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include <fcntl.h>
#include <sys/stat.h>
/*struct sockaddr_in
{
u_short sin_family;// 地址族
u_short sin_port;// 端口
struct in_addr sin_addr;// IPV4 地址
char sin_zero[8];
};*/
int Cmd_transition(char *cmd)
{
/* strcmp(s1,s1)比较 s1 和 s2
若s1=s2,返回0*/
/*strstr(s1,s1)在字符串 s1 中
查找第一次出现字符串 s2 的位置,未找到返回NULL*/
if(strcmp(cmd,"ls") == 0){
return LS;
}
if(strcmp(cmd,"pwd") == 0){
return PWD;
}
if(strcmp(cmd,"lls") == 0){
return LLS;
}
if(strcmp(cmd,"quit") == 0){
return QUIT;
}
if(strcmp(cmd,"clc") == 0){
return CLC;
}
if(strstr(cmd,"lcd") != NULL){
return LCD;
}
if(strstr(cmd,"cd") != NULL){
return CD;
}
if(strstr(cmd,"get") != NULL){
return GET;
}
if(strstr(cmd,"put") != NULL){
return PUT;
}else {
return -1;
}
}
char *Cmd_division(char *cmd)
{
char *p = NULL;
strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
int Cmd_dispose(struct Msg msg,int c_fd)
{
char *File_name = NULL;
char *TEMP;
TEMP = malloc(40);
int File_fd;
int Cmd_int;
/* 1.将字符串指令,转换为int整数类型*/
Cmd_int = Cmd_transition(msg.data);
//printf("Cmd_int:%d\n",Cmd_int);
/* 2.调用switch处理ret*/
switch(Cmd_int){
case LS:
case PWD:
case CD:
write(c_fd,&msg,sizeof(msg));
break;
case LLS:
printf("=======================================================");
system("ls");
printf("=======================================================");
break;
case LCD:
File_name = Cmd_division(msg.data);
//printf("File_name:%s\n",File_name);
if(access(File_name,F_OK) == -1){
printf("file does not exist ");
}else {
printf("=======================================================");
chdir(File_name);
system("pwd");
printf("=======================================================");
}
break;
case GET:
write(c_fd,&msg,sizeof(msg));
break;
case PUT:
TEMP = strdup(msg.data); //拷贝字符串,不要改变msg.data
printf("==== %s\n",msg.data);
File_name = Cmd_division(TEMP);
if(access(File_name,F_OK) == -1){
printf("file does not exist ");
return 100;
}else {
File_fd = open(File_name,O_RDWR);
read(File_fd,&msg.buffer,sizeof(msg.buffer));
close(File_fd);
write(c_fd,&msg,sizeof(msg));
//printf("client send cmd:%s\n",msg.data);
//printf("client send TEMP:%s\n",TEMP);
}
break;
case QUIT:
write(c_fd,&msg,sizeof(msg));
exit(0);
break;
case CLC:
system("clear");
break;
}
return Cmd_int;
}
/*
int access(const char *pathname, int mode);
参数:
pathname: 需要测试的文件路径名。
mode: 需要测试的操作模式,可能值是一个或多个R_OK(可读?), W_OK(可写?),
X_OK(可执行?) 或 F_OK(文件存在?)组合体
成功执行时,返回0。失败返回-1
*/
int Server_Process_output(struct Msg msg,int c_fd,int cmd_ret,char *File_name)
{
int File_fd;
memset(&msg.buffer,0,sizeof(msg.buffer));
printf("client reading......\n");
read(c_fd,&msg,sizeof(msg));
if(cmd_ret == GET){
File_fd = open(File_name,O_RDWR|O_CREAT);
write(File_fd,&msg.buffer,sizeof(msg.buffer));
close(File_fd);
}else {
printf("=======================================================");
printf("\nOutput:%s\n",msg.buffer);
printf("=======================================================");
}
}
int main(int argc,char **argv)
{
int con;
int c_fd;
if(argc != 3){
printf("Input param error!!\n");
exit(-1);
}
struct sockaddr_in c_addr;
c_addr.sin_family = AF_INET;
c_addr.sin_port =htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1){
printf("socket error\n");
exit(-1);
}
con = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
if(con == -1){
perror("connect");
exit(-1);
}
struct Msg msg;
char *File_name;
int cmd_ret;
while(1){
printf("\nInut cmd:");
memset(&msg.data,0,sizeof(msg.data));
//fgets(msg.data,20,stdin);
gets(msg.data); // gets不会去检查字符串的长度,如果字符串过长就会导致溢出。如果溢出的字符覆盖了其他一些重要数据就会导致不可预测的后果。
// printf("client Input:%s\n",msg.data);
cmd_ret = Cmd_dispose(msg,c_fd);
if(cmd_ret == -1||cmd_ret == 100){
continue;
}
if(cmd_ret != CD&&cmd_ret != LCD&&cmd_ret != LLS&&cmd_ret !=CLC){ //此处判断是因为防止server执行完cd后没有内容返回,导致read阻塞
if(cmd_ret == GET){
File_name = Cmd_division(msg.data);
//printf("get %s sucess\n",File_name);
//printf("client File_name:%s\n",File_name);
}
Server_Process_output(msg,c_fd,cmd_ret,File_name);
}
}
return 0;
}