day12 实现自动云同步

该代码实现了一个简单的基于TCP/IP的文件传输协议,包括客户端和服务器端。客户端支持获取、上传、列出文件列表和同步文件信息的功能,而服务器端负责处理这些请求,进行相应的文件操作。整个系统使用自定义协议进行通信。
摘要由CSDN通过智能技术生成

server.c

#include "file_transfer.h"

void Client_Handle(int newfd, struct file_transfer *ft);

int main(int argc, char *argv[])
{
	char buf[BUFSIZ] = {};
	int fd, newfd;
	struct config conf;
	struct file_transfer *ft = (struct file_transfer *)buf;

	/*根据config初始化环境*/
	FT_InitConfig(&conf);

	/*准备套接字*/
	fd = SocketInit(conf.ip, conf.port, true);

	/*循环处理客户端请求*/
	while(1){
		/*接收来自客户端的连接*/
		newfd = accept(fd, NULL, NULL);
		Client_Handle(newfd, ft);
		close(newfd);
	}

	close(fd);

	return 0;
}

void Client_Handle(int newfd, struct file_transfer *ft){
	char *err_str = "Invalid option!";
	/*接收客户端数据*/
	while(Read(newfd, ft, SIZE_FT_TYPE) > 0){
		switch(ft->type){
		case TYPE_GET://处理获取文件的请求
			FT_GetFileHandler(newfd, ft);
			break;
		case TYPE_PUT://处理上传文件的请求
			FT_PutFileHandler(newfd, ft);
			break;
		case TYPE_LIST://处理获取文件列表的请求
			FT_FileListHandler(newfd, ft);
			break;
		case TYPE_SYNC://同步文件信息
			FT_SyncHandler(newfd, ft);
			break;
		default:
			Write(newfd, err_str, strlen(err_str));
		}
	}
}

clinet.c

#include "file_transfer.h"

void Usage(int argc, char *argv[]);

int main(int argc, char *argv[])
{
	char buf[BUFSIZ] = {};
	struct config conf;
	struct file_transfer *ft = (struct file_transfer *)buf;

	/*参数检查*/
	Usage(argc, argv);

	/*根据config初始化环境*/
	FT_InitConfig(&conf);

	/*准备套接字*/
	int fd = SocketInit(conf.ip, conf.port, false);

	/*执行相应功能*/
	switch(argv[1][1]) {
	case 'g': //获取文件
		FT_GetFile(fd, argv[2], ft);
		break;
	case 'p': //上传文件
		FT_PutFile(fd, argv[2], ft);
		break;
	case 'l'://获取文件列表
		FT_FileList(fd, ft);
		break;
	case 's'://同步文件信息
		FT_Sync(fd, ft);
		break;
	default:
		fprintf(stderr, "Invalid option!\n");
	}
	/*关闭文件描述符*/
	close(fd);

	return 0;
}

void Usage(int argc, char *argv[]){
	if(argc < 2){
		fprintf(stderr, "%s[type]<argment>\n", argv[0]);
		fprintf(stderr, "\t[type]<filename> -g: get file.\n");
		fprintf(stderr, "\t[type]<filename> -p: put file.\n");
		fprintf(stderr, "\t[type] -l: upload file list\n");
		fprintf(stderr, "\t[type] -s: File synchronization.\n");
		exit(EXIT_FAILURE);
	}
	if((argv[1][1] == TYPE_GET || argv[1][1] == TYPE_PUT) && argc < 3){
		fprintf(stderr, "\t[type]<filename> -g: get file.\n");
		fprintf(stderr, "\t[type]<filename> -p: put file.\n");
		exit(EXIT_FAILURE);
	}
}

 file_transfer.c

#include "file_transfer.h"

/*私有函数*/
/*********************************/
/*创建文件,并写入空内容*/
static int W_zero(char *f_name, long long f_size){
	int count, ret;
	char buf[BUFSIZ] = {};
	int f_fd = open(f_name, O_WRONLY|O_CREAT|O_TRUNC, 0640);
	if(f_fd < 0){
		perror("open");
		return f_fd;
	}
	/*占领磁盘空间(内核一次最多能写入BUFSIZ)*/
	while(f_size > 0){
		if(f_size > BUFSIZ) count = BUFSIZ;
		else count = f_size;
		ret = Write(f_fd, buf, count);
		if(ret < 0)
			return ret;
		f_size -= ret;
	}
	close(f_fd);
	return 0;
}

/*接收文件*/
void W_body(int sockfd,char *f_name, long long f_size){
	char buf[BUFSIZ] = {};
	int f_fd, ret, count;
	if( (f_fd = open(f_name, O_WRONLY) ) < 0)
		ErrExit("open");
	while(f_size > 0){
		if(f_size > BUFSIZ) count = BUFSIZ;
		else count = f_size;
		ret = Read(sockfd, buf, count);
		Write(f_fd, buf, ret);
		f_size -= ret;
	}
	close(f_fd);
}

/*发送文件*/
void R_body(int sockfd,char *f_name, long long f_size){
	char buf[BUFSIZ] = {};
	int f_fd, count, ret;
	/*打开文件*/
	if( (f_fd = open(f_name, O_RDONLY) ) < 0)
		return;
	while(f_size > 0){
		if(f_size > BUFSIZ) count = BUFSIZ;
		else count = f_size;
		ret = Read(f_fd, buf, count);
		Write(sockfd, buf, ret);
		f_size -= ret;
	}
	close(f_fd);
}

/**客户端函数**/
/*********************************/
/*初始化环境*/
void FT_InitConfig(struct config *conf){
	size_t len;
	FILE *fp = fopen(CONFIG_FILE, "r");
	if(fp == NULL)
		ErrExit("fopen");
	/*设置IP地址*/
	fgets(conf->ip, SIZE_IP_STR, fp);
	len = strlen(conf->ip);
	if(conf->ip[len-1] == '\n')
		conf->ip[len-1] = '\0';
	/*设置端口号*/
	fgets(conf->port, SIZE_IP_STR, fp);
	len = strlen(conf->port);
	if(conf->port[len-1] == '\n')
		conf->port[len-1] = '\0';
#ifdef DEBUG
	printf("[%s:%d] conf->ip=%s\n", __FUNCTION__, __LINE__,
			conf->ip);
	printf("[%s:%d] conf->port=%s\n", __FUNCTION__, __LINE__, 
			conf->port);
#endif
	fclose(fp);
}
/*获取文件*/
void FT_GetFile(int sockfd, char *f_name, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	long long f_size;
	ft->type = TYPE_GET;
	/*发送请求信息*/
	Write(sockfd, ft, SIZE_FT_TYPE);
	ft->len = strlen(f_name);
	Write(sockfd, &ft->len, 1);
	Write(sockfd, f_name, ft->len);
	/*接收请求结果*/
	Read(sockfd, ft, SIZE_FT_TYPE);
	if(ft->type == TYPE_OK){
		Read(sockfd, &f_size, SIZE_FT_F_SIZE);
		/*创建文件,并写入空内容*/
		W_zero(f_name, f_size);
		/*写入文件内容*/
		W_body(sockfd, f_name, f_size);
	}else{
		/*如果接收文件失败,打印错误信息*/
		Read(sockfd, &ft->len, 1);
		Read(sockfd, ft->msg, ft->len);
		ft->msg[ft->len] = '\0';
		printf("get file [%s] failed [%s]\n", f_name, ft->msg);
	}
}
/*上传文件*/
void FT_PutFile(int sockfd, char *f_name, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	/*打开文件*/
	int f_fd = open(f_name, O_RDONLY);
	if(f_fd < 0)
		ErrExit("open");
	/*获取文件属性*/
	struct stat st;
	if( fstat(f_fd, &st) )
		ErrExit("stat");
	/*检查文件类型:只能上传普通文件*/
	if(!(st.st_mode & S_IFREG))
		return;
	/*获取文件大小*/
	long long f_size = (long long)st.st_size;
	/*设置自定义协议*/
	ft->type = TYPE_PUT; //消息类型
	Write(sockfd, ft, SIZE_FT_TYPE); //发送消息类型
	Write(sockfd, &f_size, SIZE_FT_F_SIZE); //发送文件大小
	ft->len = strlen(f_name);
	Write(sockfd, &ft->len, 1);
	Write(sockfd, f_name, ft->len);  //发送文件名字
	/*等待确认*/
	if( !Read(sockfd, ft, SIZE_FT_TYPE) )
		return;
	if(ft->type == TYPE_OK){  //发送文件
		R_body(sockfd, f_name, f_size);
	}else{                    //如果确认失败,打印错误信息
		Read(sockfd, &ft->len, 1);
		Read(sockfd, ft->msg, ft->len);
		ft->msg[ft->len] = '\0';
		printf("get file [%s] failed [%s]\n", f_name, ft->msg);
	}
	/*关闭文件*/
	close(f_fd);
}
/*获取文件列表*/
void FT_FileList(int sockfd, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	time_t mtime;
	ft->type = TYPE_LIST;
	Write(sockfd, &ft->type, SIZE_FT_TYPE);
	if( Read(sockfd, ft, SIZE_FT_TYPE) == 0)
		return;
	if(ft->type == TYPE_OK){
		while(1){
			Read(sockfd, &ft->len, 1);
			Read(sockfd, ft->f_name, ft->len);
			if(ft->len == 0)
				break;
			ft->f_name[ft->len] = '\0';
			printf("%-32s", ft->f_name);
			Read(sockfd, &mtime, sizeof(time_t) );
			printf("%s", ctime(&mtime) );
		}
	}else{
		printf("get list failed [%s]", ft->msg);
	}
}
/*同步文件信息*/
void FT_Sync(int sockfd, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	DIR *dir;
	struct dirent *p;
	/*打开目录*/
	if( (dir = opendir(".") ) == NULL)
		ErrExit("opendir");
	/*读取目录*/
	while( (p = readdir(dir) ) != NULL ){
		if(p->d_type == DT_REG && p->d_name[0] != '.'){
			printf("输入任意键,继续上传文件%s\n", p->d_name);
			FT_PutFile(sockfd, p->d_name, ft);
		}
	}
	closedir(dir);
}
/*********************************/

/**服务端函数**/
/*********************************/
/*处理获取文件的请求*/
void FT_GetFileHandler(int sockfd, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	Read(sockfd, &ft->len, 1); 
	Read(sockfd, ft->f_name, ft->len); 
	ft->f_name[ft->len] = '\0';
	/*获取文件属性*/
	struct stat st;
	if( stat(ft->f_name, &st) )
		ErrExit("stat");
	/*检查文件类型:只能下载普通文件*/
	char *errmsg = "只可以下载普通文件\n";
	if(!(st.st_mode & S_IFREG) || ft->f_name[0] == '.'){
		ft->type = TYPE_ERR;
		Write(sockfd, &ft->type, SIZE_FT_TYPE);
		ft->len = strlen(errmsg);
		Write(sockfd, &ft->len, 1);
		Write(sockfd, errmsg, ft->len);
		return;
	}
	ft->type = TYPE_OK;
	Write(sockfd, &ft->type, SIZE_FT_TYPE);
	long long f_size = (long long)st.st_size;
	Write(sockfd, &f_size, SIZE_FT_F_SIZE);
	/*发送文件内容*/
	R_body(sockfd, ft->f_name, f_size);
}
/*处理上传文件的请求*/
void FT_PutFileHandler(int sockfd, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	long long f_size;
	/*创建文件*/
	Read(sockfd, &f_size, SIZE_FT_F_SIZE);
	Read(sockfd, &ft->len, 1);
	Read(sockfd, ft->f_name, ft->len);
	ft->f_name[ft->len] = '\0';
	if(ft->f_name[0] == '.'){
		ft->type = TYPE_ERR;
		sprintf(ft->msg, "不可以上传隐藏文件\n");
		Write(sockfd, ft, SIZE_FT_TYPE+strlen(ft->msg)+1);
		return;
	}
	int f_fd = open(ft->f_name, O_WRONLY|O_CREAT|O_TRUNC, 0640);
	if(f_fd < 0) {
		ft->type = TYPE_ERR;
		Write(sockfd, &ft->type, SIZE_FT_TYPE);
		sprintf(ft->msg, "[创建打开失败][open:%s]\n", strerror(errno) );
		ft->len = strlen(ft->msg);
		Write(sockfd, &ft->len, 1);
		Write(sockfd, ft->msg, ft->len);
		printf("[send msg]%s\n", ft->msg);
		return;
	}
	/*占领磁盘空间(内核一次最多能写入BUFSIZ)*/
	if (W_zero(ft->f_name, f_size) < 0){
		ft->type = TYPE_ERR;
		Write(sockfd, &ft->type, SIZE_FT_TYPE);
		sprintf(ft->msg, "[磁盘空间不足][write:%s]\n", strerror(errno) );
		ft->len = strlen(ft->msg);
		Write(sockfd, &ft->len, 1);
		Write(sockfd, ft->msg, ft->len);
		printf("[send msg]%s\n", ft->msg);
		return;
	}

	/*发送确认信息*/
	ft->type = TYPE_OK;
	if( Write(sockfd, ft, 1) < 0)
		return;
	/*写入文件内容*/
	W_body(sockfd, ft->f_name, f_size);
	printf("[文件接收成功]文件名: %-32s文件大小:%lld\n", ft->f_name, f_size);
}
/*处理获取文件列表的请求*/
void FT_FileListHandler(int sockfd, struct file_transfer *ft){
	printf("[%s:%d]\n", __FUNCTION__, __LINE__);
	DIR *dir;
	struct dirent *p;
	struct stat st;
	/*打开目录*/
	if( (dir = opendir(".") ) == NULL){
		ft->type = TYPE_ERR;
		sprintf(ft->msg, "[目录打开失败][opendir:%s]\n", strerror(errno) );
		Write(sockfd, ft, SIZE_FT_TYPE+strlen(ft->msg)+1);
		return;
	}
	/*读取目录*/
	ft->type = TYPE_OK;
	Write(sockfd, ft, SIZE_FT_TYPE);
	while( (p = readdir(dir) ) != NULL ){
		if(p->d_type == DT_REG && p->d_name[0] != '.'){
			ft->len = strlen(p->d_name);
			Write(sockfd, &ft->len, 1);
			Write(sockfd, p->d_name, ft->len);
			stat(p->d_name, &st);
			Write(sockfd, &st.st_mtime, sizeof(time_t) );
		}
	}
	ft->len = 0;
	Write(sockfd, &ft->len, 1);
	closedir(dir);
}
/*同步文件信息*/
void FT_SyncHandler(int sockfd, struct file_transfer *ft){
}

 file_transfer.h

#ifndef _FILE_TRANSFER_H_
#define _FILE_TRANSFER_H_

#include "net.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>

#define DEBUG printf("==debug information==\n");

#define CONFIG_FILE ".config"
#define SIZE_IP_STR 17
#define SIZE_PORT_STR 6
#define SIZE_PATH_STR 255
#define SIZE_CLOUD_SPACE 1024*1024*1024   //云空间大小 单位:byte

#define SIZE_FT_TYPE 1
#define SIZE_FT_F_SIZE 8

/*自定义协议类型*/
enum {
	TYPE_ERR, TYPE_OK, TYPE_GET, TYPE_PUT, TYPE_LIST, TYPE_SYNC,
};

/*自定义协议结构体*/
struct file_transfer {
	uint8_t type;
	/*GNU C的扩展属性,0长度的数组不占用内存空间
	 * msg 有效时,其他字段无效
	 * */
	char f_size[8];
	uint8_t len;
	char f_name[0];
	char msg[0];     
	char f_body[0];
};

/*环境信息*/
struct config{
	char ip[SIZE_IP_STR];
	char port[SIZE_PORT_STR];
};

/*文件链表*/
typedef struct node{
	time_t mtime;
	char name[NAME_MAX];
	struct node *next;
}node_t;

/**客户端函数**/
/*********************************/
/*初始化环境*/
void FT_InitConfig(struct config *conf);
/*获取文件*/
void FT_GetFile(int sockfd, char *f_name, struct file_transfer *ft);
/*上传文件*/
void FT_PutFile(int sockfd, char *f_name, struct file_transfer *ft);
/*获取文件列表*/
void FT_FileList(int sockfd, struct file_transfer *ft);
/*同步文件信息*/
void FT_Sync(int sockfd, struct file_transfer *ft);
/*********************************/

/**服务端函数**/
/*********************************/
/*处理获取文件的请求*/
void FT_GetFileHandler(int sockfd, struct file_transfer *ft);
/*处理上传文件的请求*/
void FT_PutFileHandler(int sockfd, struct file_transfer *ft);
/*处理获取文件列表的请求*/
void FT_FileListHandler(int sockfd, struct file_transfer *ft);
/*同步文件信息*/
void FT_SyncHandler(int sockfd, struct file_transfer *ft);

#endif

net.h

#ifndef _NET_H_
#define _NET_H_

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>

#define BACKLOG 5

#define ErrExit(msg) do { \
	fprintf(stderr, "[%s:%d] ", \
			__FUNCTION__, __LINE__); \
	perror(msg); \
	exit(EXIT_FAILURE); } while(0)

typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;

void Argment(int argc, char *argv[]);
int SocketInit(char *addr, char *port, bool server);

ssize_t Read(int fd, void *buf, size_t len);
ssize_t Write(int fd, const void *buf, size_t len);

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值