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