基于TCP协议的共享文件夹(Linux)
一、实现功能
help 功能目录 pwd 我在哪 ls 目录 cd 就是cd mkdir 创建文件夹 rmdir 删除文件夹 rm 删除文件 get 下载 put 上传
二、操作系统
Ubuntu(64-bit)
三、代码
1.服务器 server.c
#include"server.h" pthread_rwlock_t lock; int epfd; Client gcls[MAX_CLIENTS+1] = {}; //事件表 fd path 账号 int size = 0; struct epoll_event events[MAX_CLIENTS+1] = {}; //就绪事件表 int is=0; int init_server(const char *ip,unsigned short int port); void *accept_client(void *id); int recv_data(int fd); void *sign_in(void *id); void select_fd(int fd); void zoule(void); int cmp_num(const void *a,const void *b); void sig_exit(); int main(int argc,char *argv[]){ pLogFile=stdout; //log日志输出流指向标准输出 if(argc < 3){ printf("%s ip port\n",argv[0]); return -1; } if(signal(SIGINT,sig_exit)==SIG_ERR) LOG_TRACE("退出信号处理失败\n"); int ret=pthread_rwlock_init(&lock,NULL); //初始化线程锁 atexit(zoule); //退出前执行 list_cus=list_init(CSIZE); //定义 一个双向循环链表用于存储账号 load_cus(); //载入账号 密码 load_num(); //载入未注册账号 strcpy(localip,argv[1]); //保存IP和端口 全局变量 localport=atoi(argv[2]); int fd = init_server(argv[1],atoi(argv[2]));//初始化服务器 select_fd(fd); //连接客户端 return 0; } int init_server(const char *ip,unsigned short int port){ int fd = socket(AF_INET,SOCK_STREAM,0); //套接字 assert(fd != -1); struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); socklen_t len = sizeof(addr); int ret = bind(fd,(const struct sockaddr*)&addr,len); //绑定端口 assert(ret != -1); ret = listen(fd,MAX_CLIENTS); //监听 assert(ret != -1); return fd; } void select_fd(int fd){ epfd = epoll_create(MAX_CLIENTS); if(epfd == -1){ perror("epoll_create"); return; } struct epoll_event event = {}; event.events = EPOLLIN; // 可读事件 event.data.fd = fd; //数据(主线程fd) int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event); if(ret == -1){ perror("epoll_ctl"); return ; } while(true){ usleep(1000); //这里应该用线程池 ret = epoll_wait(epfd,events,MAX_CLIENTS+1,-1); if(ret == -1){ perror("epoll_wait"); break; } //就绪事件数量 监听数量 LOG_TRACE("events_size=%d wait_ret=%d\n",size,ret); int i; pthread_t id; for(i=0;i<ret;i++){ is = i; //就绪事件 fd LOG_TRACE("events[is].data.fd =%d\n",events[i].data.fd ); if(events[i].data.fd == fd){ //有客户端连接 int rret = pthread_create(&id,NULL,accept_client,(void *)fd); //这里应该用线程池 assert(rret == 0); }else{ //有数据接收 if(events[i].events & EPOLLIN){ pthread_t id; int rret = pthread_create(&id,NULL,sign_in,(void*)events[i].data.fd); //子线程去处理数据 assert(rret == 0); } } } } } void *sign_in(void *id){ int fd=(int)id; int ret = recv_data(fd); //处理数据 if(ret == 0){ //客户端异常 删除事件 struct epoll_event ev = {}; ev.events = EPOLLIN|EPOLLET|EPOLLONESHOT; ev.data.fd = fd; ret = epoll_ctl(epfd,EPOLL_CTL_DEL,events[is].data.fd,&ev); if(ret == -1){ perror("epoll_ctl"); } } } void zoule(void){ //退出前保存数据 save_cus(); save_num(); } int cmp_num(const void *a,const void *b){ //比较函数 const CUS* node=b; return strcmp((char *)a,node->id); } void sig_exit(){ //收到信号 退出程序 exit(0); } void *accept_client(void *id){ int fd=(int)id; struct sockaddr_in addr = {}; socklen_t len = sizeof(addr); int cfd = accept(fd,(struct sockaddr*)&addr,&len); //有客户端连接 if(cfd != -1){ Client cls = {}; cls.fd = cfd; cls.addr = addr; strcpy(cls.id,""); //未绑定账号 gcls[size] = cls; ++size; struct epoll_event event = {}; event.events = EPOLLIN; event.data.fd = cfd; pthread_rwlock_wrlock(&lock); int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&event); //客户端套接字加入事件表 if(ret == -1){ perror("epoll_ctl"); } pthread_rwlock_unlock(&lock); LOG_TRACE("有一个客户端登入了服务器\n"); } } int recv_data(int fd){ int index = 0; pthread_rwlock_rdlock(&lock); for(;index<size;index++){ if(gcls[index].fd == fd){ //在事件表中找到该fd break; } } pthread_rwlock_unlock(&lock); int ord=0; int ret = 0; if(strcmp(gcls[index].id,"")==0){ //未绑定账号,去登陆或者注册 ret = recv(fd,&ord,4,0); nixu(&ord,sizeof(ord)); LOG_TRACE("线程未绑定\n"); if(ret==-1){ LOG_TRACE("%m\n"); return 0; } if(ord==1){ //注册 CUS elem={}; ret=recv(fd,&elem,sizeof(elem),0); if(ret==-1){ LOG_TRACE("%m\n"); return 0; } pthread_rwlock_wrlock(&lock); strcpy(elem.id,num); ret=send(fd,num,8,0); if(ret==-1){ LOG_TRACE("%m\n"); return 0; } add(num,"1"); LOG_TRACE("\n账户:%s 密码:%s\n",elem.id,elem.pass); end_insert(list_cus,&elem); pthread_rwlock_unlock(&lock); }else if(ord==2){ //登录 SIGN ke; ret=recv(fd,&ke,sizeof(ke),0); if(ret==-1){ LOG_TRACE("%m\n"); return 0; } LOG_TRACE("收到登陆数据\n"); pthread_rwlock_rdlock(&lock); struct Node *node=find_elem(list_cus,ke.id,cmp_num); pthread_rwlock_unlock(&lock); if(node!=NULL){ CUS *cnode=node->elem; if(strcmp(cnode->pass,ke.pass)!=0){ node=NULL; LOG_TRACE("账号密码不匹配\n"); } } if(node!=NULL){ CUS *cnode=node->elem; ret=send(fd,cnode,sizeof(CUS),0); if(ret==-1){ LOG_TRACE("%m\n"); return 0; } LOG_TRACE(">>>>>>登陆账户%s\n",cnode->id); strcpy(gcls[index].id,cnode->id); //fd绑定已登录id strcpy(gcls[index].path,""); //初始化path }else{ CUS cnode={}; strcpy(cnode.id,"0"); ret=send(fd,&cnode,sizeof(CUS),0); if(ret==-1){ LOG_TRACE("%m\n"); return 0; } LOG_TRACE("登陆失败\n"); strcpy(gcls[index].id,""); //清空fd绑定的id strcpy(gcls[index].path,""); //清空fd绑定的path } }else{ int index = 0; pthread_rwlock_wrlock(&lock); for(;index<size;index++){ if(gcls[index].fd == fd){ break; } } gcls[index]=gcls[size-1]; --size; pthread_rwlock_unlock(&lock); struct epoll_event ev = {}; ev.events = EPOLLIN; ev.data.fd = fd; ret = epoll_ctl(epfd,EPOLL_CTL_DEL,events[is].data.fd,&ev); //把事件从事件表里删除 if(ret == -1){ perror("epoll_ctl"); } LOG_TRACE("有一个客户端登出了服务器!!!\n"); return 1; } }else{ //已登录进程,获得绑定id和path pthread_rwlock_rdlock(&lock); struct Node *node=find_elem(list_cus,gcls[index].id,cmp_num); pthread_rwlock_unlock(&lock); int run=1; while(run){ INS ans; ret = recv(fd,&ans,sizeof(INS),0); //接受命令参数 if(ret<=0){ //客户端异常退出 清空id path run=0; strcpy(gcls[index].id,""); strcpy(gcls[index].path,""); break; } nixu(&ans.nn,4); //转网络字节序 if(strcmp(ans.ins[0],"quit")!=0){ LOG_TRACE("非quit指令,进行命令处理\n"); deal(fd,ans.nn,ans.ins,gcls[index].path); //命令处理 }else{ //quit指令 退出 LOG_TRACE("账户退出\n"); run=0; strcpy(gcls[index].id,""); strcpy(gcls[index].path,""); break; } } } return 1; }
2.客户端 client.c
#include "client.h" char path[40]; char signin[8]; void show_new(); void newcus(int fd); void menu(); void show_sign(); void show_file(); void tuichu(int fd); void sign_in(int fd); void tuia(int fd); int connect_server(const char *ip,unsigned short int port); int main(int argc,char *argv[]){ if(argc < 3){ LOG_FATAL("%s ip port\n",argv[0]); return -1; } pLogFile=stdout; strcpy(localip,argv[1]); //保存ip int fd = connect_server(argv[1],atoi(argv[2])); //连接服务器 int ord; int run=1; while(run){ menu(); scanf("%d",&ord); switch(ord){ case 1: newcus(fd); //注册 break; case 2: sign_in(fd); //登录 break; default: tuia(fd); //退出 run=0; break; } } close(fd); return 0; } int connect_server(const char *ip,unsigned short int port){ //连接服务器 int fd = socket(AF_INET,SOCK_STREAM,0); assert(fd != -1); struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); socklen_t len = sizeof(addr); int ret = connect(fd,(const struct sockaddr*)&addr,len); assert(ret != -1); return fd; } void show_new(){ printf("*****请输入账号密码*****\n"); printf("密码\n"); } void newcus(int fd){ int ord=1; nixu(&ord,sizeof(ord)); //转换网络字节序 int ret=send(fd,&ord,sizeof(ord),0); if(ret==-1){ LOG_TRACE("%m\n"); return; } CUS node={}; show_new(); strcpy(node.id,"0"); scanf("%s",node.pass); ret=send(fd,&node,sizeof(node),0); if(ret==-1){ LOG_TRACE("%m\n"); return; } char msg[8]={}; ret=recv(fd,msg,8,0); if(ret==-1){ LOG_TRACE("%m\n"); return; } printf(">>>账户:%s\n",msg); printf("已成功注册,请重新登录!!\n"); } void menu(){ printf("*****请输入功能*****\n"); printf("1.注册\n"); printf("2.登录\n"); printf("0.退出\n"); } void show_sign(){ printf("*****请输入账号密码*****\n"); printf("*** 账号 "); printf("密码 ***\n"); } void show_file(){ printf("%s:~/%s $",signin,path); //模拟终端输入 账户:路径/ 指令 fflush(stdout); } void sign_in(int fd){ int ord=2; nixu(&ord,sizeof(ord)); int ret=send(fd,&ord,sizeof(ord),0); if(ret==-1){ LOG_TRACE("%m\n"); return; } show_sign(); SIGN snode={}; printf("输入账户:\n"); printf(">>>"); fflush(stdout); scanf("%s",snode.id); while(getchar()!='\n'); printf("输入密码:\n"); printf(">>>"); fflush(stdout); //scanf("%s",snode.pass); get_pass(snode.pass,12,true); //输入密码不回显 LOG_TRACE("登录数据读取成功!\n"); ret=send(fd,&snode,sizeof(snode),0); if(ret==-1){ LOG_TRACE("%m\n"); return; } LOG_TRACE("账户密码发送成功\n"); CUS node={}; ret=recv(fd,&node,sizeof(CUS),0); //接收登录结果 if(ret==-1){ LOG_TRACE("%m\n"); return; } LOG_TRACE("查找节点已返回\n"); CUS *knode=&node; if(strcmp(knode->id,"0")!=0){ LOG_TRACE("登陆成功,进入业务界面\n"); printf("*****请输入指令*****\n"); strcpy(signin,knode->id); strcpy(path,""); int run=1; while(run){ show_file(); INS ans={}; ans.nn = 0; char message_order[100]; gets(message_order);//读入命令行 ans.nn=message_tool(ans.ins,message_order);//转换成结构体 nixu(&ans.nn,4); send(fd,&ans,sizeof(INS),0); //命令参数发送给服务器 nixu(&ans.nn,4); if(strcmp(ans.ins[0],"quit")==0){ //quit指令退出 run=0; LOG_TRACE("quit正常退出登录!\n"); strcpy(path,""); }else if(ans.ins[0][0]=='!'){ //!指令 系统指令调用 char *p=message_order+1; system(p); }else{ insdeal(fd,ans.nn,ans.ins,path); //进行指令处理 } } return; }else{ printf("账户或密码不正确!\n"); printf("ID or password is false!!!\n"); printf("请重新登录!!!\n"); return; } } void tuichu(int fd){ //账户退出 int ord=6; nixu(&ord,sizeof(ord)); int ret=send(fd,&ord,sizeof(ord),0); if(ret==-1){ LOG_TRACE("%m\n"); return; } } void tuia(int fd){ //客户端退出 int ord=0; nixu(&ord,sizeof(ord)); int ret=send(fd,&ord,sizeof(ord),0); if(ret==-1){ LOG_TRACE("%m\n"); return; } LOG_TRACE("退出信号:%d\n",ord); if(ret==-1){ LOG_TRACE("%m\n"); return; } printf("已退出\n"); }
3.服务器指令处理deal.c
#include "get.h" #include"deal.h" #include"log.h" #include<string.h> #include<assert.h> #include <errno.h> #include <assert.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <signal.h> #include <stdbool.h> #include <sys/sendfile.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> char localip[20]; //ip 用于连接数据通道 unsigned short int localport; //端口 void deal(int fd,int gc,char gv[5][20],char path[40]){ int i=0; char nowpath[40]="./sfile/"; strcat(nowpath,path); //账户当前路径 if(strcmp(gv[0],"ls")==0){ // ls char messturn[1024]={}; int ret=lsdir(nowpath,messturn); //ls输出到messturn strcat(messturn,"\0"); puts(messturn); if(ret==-1) LOG_TRACE("lsdir error!\n"); send(fd,messturn,strlen(messturn)+1,0); }else if(strcmp(gv[0],"pwd")==0){ //pwd char jiadepath[40]; strcpy(jiadepath,"~/"); strcat(jiadepath,path); send(fd,jiadepath,40,0); }else if(strcmp(gv[0],"tree")==0){ // tree char messturn[1024]={}; int ret=tree(nowpath,messturn); strcat(messturn,"\0"); send(fd,messturn,strlen(messturn)+1,0); puts(messturn); if(ret==-1) LOG_TRACE("tree error!\n"); }else if(strcmp(gv[0],"cd")==0){ // cd char testpath[40]; strcpy(testpath,nowpath); strcat(testpath,gv[1]); LOG_TRACE("当前收到的路径:%s\n",testpath); if(strcmp(gv[1],"~")==0){ strcpy(path,""); send(fd,path,40,0);//收到~ 返回空路径 }else{ if(access(testpath,F_OK)==0){ LOG_TRACE("路径合理,寻找..和~ 简化路径\n"); strcat(path,gv[1]); strcat(path,"/"); char *p=path; int len=strlen(path)-1; int i=0,j=0; while(len>=0){ if(p[len]=='.'&&p[len-1]=='.'){ i=len-3; j=len+2; while(i>=0&&p[i]!='/'){ i--; } if(i<0){ strcpy(path,""); }else{ i++; while(p[j]!='\0'){ p[i++]=p[j++]; } p[i]='\0'; } len-=4; }else{ --len; } } send(fd,path,40,0);///成功发回当前位置 }else{ LOG_TRACE("收到的路径不合理,返回error\n"); char messturn[20]="path error"; send(fd,messturn,strlen(messturn)+1,0);//失败返回路径不存在 } } }else if(strcmp(gv[0],"mkdir")==0){ // mkdir char mkfilepath[40]; strcpy(mkfilepath,nowpath); strcat(mkfilepath,gv[1]); if(mkdir(mkfilepath,0777)==0){ char messturn[20]="success"; send(fd,messturn,strlen(messturn)+1,0); LOG_TRACE("文件夹 %s 创建 成功!!! \n",mkfilepath); }else{ char messturn[20]="path error"; send(fd,messturn,strlen(messturn)+1,0); LOG_TRACE("文件夹 %s 创建 失败??? \n",mkfilepath); } }else if(strcmp(gv[0],"rmdir")==0){ // rmdir char rmfilepath[40]; strcpy(rmfilepath,nowpath); strcat(rmfilepath,gv[1]); if(rmdir(rmfilepath)==0){ char messturn[20]="success"; send(fd,messturn,strlen(messturn)+1,0); LOG_TRACE("文件夹 %s 删除成功!!! \n",rmfilepath); }else{ char messturn[20]="path error"; send(fd,messturn,strlen(messturn)+1,0); LOG_TRACE("文件夹 %s 删除失败??? \n",rmfilepath); } }else if(strcmp(gv[0],"get")==0){ // get char testpath[40]; strcpy(testpath,nowpath); strcat(testpath,gv[1]); LOG_TRACE("当前收到的文件:%s\n",testpath); int openfd; openfd=open(testpath,O_RDONLY); //打开成功,文件存在 if(openfd>0){ struct stat stat_buf; fstat(openfd,&stat_buf); //获得文件大小 localport++; send(fd,&localport,2,0); //数据通道的端口发送给客户端 LOG_TRACE("收到的文件合理,返回合理\n"); char messturn[20]="path success"; send(fd,messturn,20,0); //成功返回路径存在 int nfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 这里应该封装一下 assert(nfd != -1); struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(localport); addr.sin_addr.s_addr = inet_addr(localip); socklen_t len = sizeof(addr); int ret = bind(nfd,(const struct sockaddr*)&addr,len); //绑定端口 assert(ret != -1); ret = listen(nfd,10); //监听 assert(ret != -1); LOG_TRACE("线程服务器已开启\n"); LOG_TRACE("ip:%s port:%hu \n",localip,localport); int cfd = accept(nfd,(struct sockaddr*)&addr,&len); //接受客户端 send(cfd,&stat_buf.st_size,sizeof(stat_buf.st_size),0); //发送文件大小 sendfile(cfd,openfd,NULL,stat_buf.st_size); //sendfile零拷贝读写 strcpy(messturn,"sendsuccess"); LOG_TRACE("%s\n",messturn); close(openfd); close(cfd); close(nfd); }else{ LOG_TRACE("收到的文件不合理,返回error\n"); char messturn[20]="path error"; send(fd,messturn,20,0); } }else if(strcmp(gv[0],"put")==0){ localport++; send(fd,&localport,2,0); //发送数据通道的端口 char *p=gv[1]+strlen(gv[1])-1; while(p!=&gv[1][0]&&*p!='/') --p; if(*p=='/') ++p; char testpath[40]={}; strcpy(testpath,nowpath); strcat(testpath,p); //获得文件名 LOG_TRACE("当前收到的文件:%s\n",testpath); int openfd; char messturn[20]={}; recv(fd,messturn,20,0); if(strcmp(messturn,"path success")==0){ openfd=open(testpath,O_WRONLY|O_CREAT|O_EXCL,0644); //创建文件 已存在则失败 if(openfd>0){ LOG_TRACE("成功创建文件\n"); char messturn[20]="open success"; send(fd,messturn,20,0); LOG_TRACE("收到的文件合理,返回合理\n"); int nfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 assert(nfd != -1); //这里应该封装一下 struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(localport); addr.sin_addr.s_addr = inet_addr(localip); socklen_t len = sizeof(addr); int ret = bind(nfd,(const struct sockaddr*)&addr,len); //绑定端口 assert(ret != -1); ret = listen(nfd,10); //监听 assert(ret != -1); LOG_TRACE("线程服务器已开启\n"); LOG_TRACE("ip:%s port:%hu \n",localip,localport); int cfd = accept(nfd,(struct sockaddr*)&addr,&len); //接收客户端 size_t st_size=0; recv(cfd,&st_size,sizeof(size_t),0); //接受文件大小 char data[1024]={}; while(st_size>0){ size_t recv_size=recv(cfd,data,1024,0); st_size-=recv_size; write(openfd,data,recv_size); } //接收文件 puts("get success"); close(openfd); close(cfd); close(nfd); }else{ LOG_TRACE("收到的文件名已存在\n"); char messturn[20]="Already exists"; send(fd,messturn,20,0); } } }else if(strcmp(gv[0],"rm")==0){ // rmdir char rmfilepath[40]; strcpy(rmfilepath,nowpath); strcat(rmfilepath,gv[1]); if(remove(rmfilepath)==0){ char messturn[20]="success"; send(fd,messturn,strlen(messturn)+1,0); LOG_TRACE("文件 %s 删除成功!!! \n",rmfilepath); }else{ char messturn[20]="path error"; send(fd,messturn,strlen(messturn)+1,0); LOG_TRACE("文件 %s 删除失败??? \n",rmfilepath); } } }
4.客户端指令处理insdeal.c
#include"insdeal.h" #include"log.h" #include "get.h" #include<string.h> #include<assert.h> #include <errno.h> #include <assert.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <signal.h> #include <sys/sendfile.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> char localip[20]; //保存IP void insdeal(int fd,int gc,char gv[5][20],char path[40]){ if(strcmp(gv[0],"ls")==0||strcmp(gv[0],"tree")==0){ // ls tree char messturn[1024]={}; recv(fd,messturn,1024,0); printf("~/"); puts(path); puts(messturn); }else if(strcmp(gv[0],"pwd")==0){ //pwd char jiadepath[40]; recv(fd,jiadepath,40,0); puts(jiadepath); }else if(strcmp(gv[0],"help")==0){ //help printf("可选指令:\npwd ls tree mkdir rmdir rm cd get put quit\n"); }else if(strcmp(gv[0],"cd")==0){ //cd recv(fd,path,40,0); if(strcmp(path,"path error")==0) puts(path); }else if(strcmp(gv[0],"mkdir")==0){ //mkdir char messturn[20]={}; recv(fd,messturn,20,0); if(strcmp(messturn,"path error")==0) puts(messturn); }else if(strcmp(gv[0],"rmdir")==0){ //rmdir char messturn[20]={}; recv(fd,messturn,20,0); if(strcmp(messturn,"path error")==0) puts(messturn); }else if(strcmp(gv[0],"rm")==0){ //rm char messturn[20]={}; recv(fd,messturn,20,0); if(strcmp(messturn,"path error")==0) puts(messturn); }else if(strcmp(gv[0],"get")==0){ //get unsigned short int localport=0; recv(fd,&localport,2,0); //接受数据通道端口 char messturn[20]={}; recv(fd,messturn,20,0); if(strcmp(messturn,"path success")==0){ //文件存在 char nowpath[40]="./cfile/"; strcat(nowpath,gv[1]); int openfd; openfd=open(nowpath,O_WRONLY|O_CREAT,0664); //创建文件 已存在就覆盖 if(openfd<=0){ printf("open error\n"); return; } int nfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 这里应该封装 assert(nfd != -1); struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(localport); addr.sin_addr.s_addr = inet_addr(localip); socklen_t len = sizeof(addr); LOG_TRACE("ip:%s port:%hu \n",localip,localport); int ret = connect(nfd,(const struct sockaddr*)&addr,len); //连接服务器 assert(ret != -1); size_t st_size=0; recv(nfd,&st_size,sizeof(size_t),0); char data[1024]={}; while(st_size>0){ size_t recv_size=recv(nfd,data,1024,0); st_size-=recv_size; write(openfd,data,recv_size); } //接收文件 puts("get success"); close(openfd); close(nfd); }else{ puts("NO such file!"); return; } }else if(strcmp(gv[0],"put")==0){ unsigned short int localport=0; recv(fd,&localport,2,0); //接受数据通道端口 char testpath[40]="./cfile/"; strcat(testpath,gv[1]); int openfd; openfd=open(testpath,O_RDONLY); //文件存在 if(open>0){ char messturn[20]="path success"; send(fd,messturn,20,0); struct stat stat_buf; fstat(openfd,&stat_buf); recv(fd,messturn,20,0); if(strcmp(messturn,"Already exists")==0){ //服务器已存在该文件名 printf("Already exists"); return; } int nfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 这里应该封装 assert(nfd != -1); struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(localport); addr.sin_addr.s_addr = inet_addr(localip); socklen_t len = sizeof(addr); LOG_TRACE("ip:%s port:%hu \n",localip,localport); int ret = connect(nfd,(const struct sockaddr*)&addr,len); //连接服务器 assert(ret != -1); send(nfd,&stat_buf.st_size,sizeof(stat_buf.st_size),0); //发送文件大小 sendfile(nfd,openfd,NULL,stat_buf.st_size); //发送文件 strcpy(messturn,"sendsuccess"); LOG_TRACE("%s\n",messturn); close(openfd); close(nfd); }else{ char messturn[20]="path error"; send(fd,messturn,20,0); puts(messturn); } } }
5.服务器调用的lsdir()、tree() get.c
#include "get.h" #include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <dirent.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <stdlib.h> int lsdir(const char *path,char messturn[1024]){ //输出到数组 struct stat st = {}; int ret = stat(path,&st); if(ret == -1){ printf("%m\n"); return -1; } if(S_ISDIR(st.st_mode)){//是目录 DIR *dir = opendir(path);//打开目录 if(dir == NULL){ printf("open %s failed! %m\n",path); return -1; } struct dirent *pd = NULL; int i = 0; while((pd = readdir(dir)) != NULL){//读取目录中条目 strcat(messturn,pd->d_name); ++i; if(i%2==0){ strcat(messturn,"\n"); }else{ int namelen=strlen(pd->d_name); for(i=0;i<28-namelen;i++){ strcat(messturn," "); } } } strcat(messturn,"\n"); closedir(dir); }else{ printf("%s not a dirent!\n",path); return -1; } } int tree_a(const char *path,int deep,char messturn[1024]){ int i; for(i=0;i<deep;i++){ strcat(messturn," "); } if(deep > 0){ const char *s = path+strlen(path)-1; while(*s != '/'){ --s; } strcat(messturn,s); strcat(messturn,"\n"); }else{ strcat(messturn,"\n"); } DIR *dir = opendir(path); if(dir == NULL) return -1; struct dirent *pd = NULL; while((pd = readdir(dir))!=NULL){ if(pd->d_type == DT_DIR){//目录 if(strcmp(pd->d_name,".")==0||strcmp(pd->d_name,"..")==0){ continue; } char npath[256] = {}; strcpy(npath,path); strcat(npath,"/"); strcat(npath,pd->d_name); tree_a(npath,deep+4,messturn); //是文件夹 递归 }else{//普通文件 for(i=0;i<deep+4;i++){ strcat(messturn," "); } strcat(messturn,pd->d_name); strcat(messturn,"\n"); } } } int tree(char gv[],char messturn[1024]){ struct stat st = {}; if(stat(gv,&st)==-1){ perror("stat:"); return -1; } if(S_ISDIR(st.st_mode)){ //是否是文件夹 tree_a(gv,0,messturn); }else{ printf("%s not dirent!\n",gv); } return 0; }
6.服务器载入/保存数据 file.c
#include"file.h" #include"doublelist.h" #include"v.h" #include"tool.h" #include"log.h" int load_cus(){ int fd=open("./data/custom.txt",O_RDONLY); if(fd!=-1){ //LOG_TRACE("载入:custom.txt open成功\n"); int listsize; int ret=read(fd,&listsize,sizeof(listsize)); LOG_TRACE("载入:链表大小:%d载入成功\n",listsize); if(ret!=-1){ int i=0; for(;i<listsize;i++){ struct custom pp; read(fd,&pp,CUSIZE); int ret=end_insert(list_cus,&pp); if(ret==-1){ LOG_TRACE("载入:加载插入链表失败\n"); return -1; } LOG_TRACE("账户:%s 密码:%s 读取成功\n",pp.id,pp.pass); } } close(fd); } } int save_cus(){ int fd=open("./data/custom.txt",O_WRONLY|O_CREAT,0664); //LOG_TRACE("保存:custom.txt open成功\n"); if(fd==-1){ LOG_TRACE("%m\n"); return -1; } int listsize; listsize=list_cus->size; int ret=write(fd,&listsize,sizeof(listsize)); LOG_TRACE("保存:链表大小:%d保存成功\n",listsize); if(ret==-1){ printf("%m\n"); return -1; } struct Node* node=list_cus->head->next; for(;node!=list_cus->head;node=node->next){ CUS *pp=node->elem; write(fd,pp,CUSIZE); LOG_TRACE("%s %s 写入成功\n",pp->id,pp->pass); } destory(list_cus); close(fd); } int load_num(){ int fd=open("./data/number.txt",O_RDONLY); if(fd!=-1){ //LOG_TRACE("载入:number.txt open成功\n"); int ret=read(fd,num,8); if(ret!=-1){ LOG_TRACE("载入:num[8]:%s载入成功!\n",num); close(fd); } } } int save_num(){ int fd=open("./data/number.txt",O_WRONLY|O_CREAT,0664); if(fd==-1){ LOG_TRACE("%m\n"); return -1; } LOG_TRACE("保存:number.txt open成功\n"); int len=strlen(num)+1; int ret=write(fd,num,len); LOG_TRACE("num[8]:%s保存成功\n",num); if(ret==-1){ LOG_TRACE("%m\n"); return -1; } close(fd); }
7.小工具 tool.c
#include"tool.h" char* get_pass(char* pass,size_t len,bool flag){ //输入密码不回显 fflush(stdin); size_t index = 0; while(index < len) { char key = getch(); if(127== key){ if(index > 0){ index--; if(flag) printf("\b \b"); } continue; } if('\n' == key) break; pass[index++] = key; if(flag) printf("*"); } pass[index] = '\0'; printf("\n"); return pass; } int message_tool(char mess[5][20],char ord[100]){ //将命令行拆分成字符串组 int i=0,j=0; char *p=ord; while(*p!='\0'){ if(*p==' '){ mess[i][j]='\0'; j=0; ++i; }else{ mess[i][j]=*p; ++j; } ++p; } mess[i][j]='\0'; return i+1; } void add(char *a,char *b){ //字符串数字 a=a+b int len1=strlen(a); int len2=strlen(b); int ll=len1>len2?(len1+1):(len2+1); char cc[ll]; int i=0; int flag=0; len1--;len2--; while(len1>=0||len2>=0){ if(len1>=0&&len2>=0){ char cur=a[len1]+b[len2]+flag-'0'; if(cur>'9'){ cc[i++]=cur-10; flag=1; }else{ cc[i++]=cur; flag=0; } len1--;len2--; }else if(len1>=0){ char cur=a[len1]+flag; if(cur>'9'){ cc[i++]='0'; flag=1; }else{ cc[i++]=cur; flag=0; } len1--; }else if(len2>=0){ char cur=b[len2]+flag; if(cur>'9'){ cc[i++]='0'; flag=1; }else{ cc[i++]=cur; flag=0; } len2--; } } if(flag==1){ cc[i]='1'; }else{ i--; } int j=0; while(i>=0){ a[j++]=cc[i--]; } a[j]='\0'; } void ddd(char *a,char *b){ //字符串数字 a=a-b int len1=strlen(a); int len2=strlen(b); int ll=len1+1; char cc[ll]; int i=0; int flag=0; len1--;len2--; while(len1>=0){ if(len1>=0&&len2>=0){ char cur=a[len1]-b[len2]+'0'-flag; if(cur<'0'){ cc[i++]=cur+10; flag=1; }else{ cc[i++]=cur; flag=0; } len1--;len2--; }else if(len1>=0){ char cur=a[len1]-flag; if(cur<'0'){ cc[i++]='9'; flag=1; }else{ cc[i++]=cur; flag=0; } len1--; } } if(cc[--i]=='0'){ --i; } int j=0; while(i>=0){ a[j++]=cc[i--]; } a[j]='\0'; } int vs(char *a,char *b){ //字符串数字 a-b int len1=strlen(a); int len2=strlen(b); if(len1-len2!=0) return len1-len2; while(*a==*b&&*a!='\0'&&*b!='\0'){ ++a;++b; } return *a-*b; } void nixu(void *p,int len){ //字节序转换 好像不需要 有函数可以直接调用 char a; char *pp=(char *)p; int i=0; while(i<len-1&&len-1>=0){ a=pp[i]; pp[i]=pp[len-1]; pp[len-1]=a; ++i; --len; } }
8.变量初始化 结构体定义 v.c v.h (下次可不能取这种“导见打”的文件名了)
v.c
#include"v.h" Doublelist list_cus=NULL; char num[8]="2106001";
v.h
#ifndef _V_H_ #define _V_H_ #include"doublelist.h" #include <sys/epoll.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> typedef struct custom{ char id[8]; char pass[12]; }CUS; typedef struct ins{ int nn; char ins[5][20]; }INS; typedef struct sign{ char id[8]; char pass[12]; }SIGN; typedef struct Client{ int fd; struct sockaddr_in addr; char id[8]; char path[40]; }Client; #define NO_LEN 8 #define MAX_CLIENTS 100 #define MSG_LEN 1024 #define CUSIZE sizeof(struct custom) extern Doublelist list_cus; extern char num[8]; #endif
9.双向循环链表 doublelist.c doublielist.h 其实(绝)大部分功能都用不到
doublelist.c
#include"doublelist.h" //创建循环双链表,头节点head Doublelist list_init(int elemsize){ Doublelist list=(Doublelist)malloc(sizeof(struct Doublelist)); if(list==NULL) return NULL; list->head=(struct Node*)malloc(NODE); if(list->head==NULL){ free(list); return NULL; } list->head->next=list->head; list->head->prev=list->head; list->elemsize=elemsize; list->size=0; return list; } //判断循环双链表是否为空 bool is_empty(Doublelist list){ return list->size==0; } //循环双链表元素个数 int num_element(Doublelist list){ return list->size; } static struct Node* front_find(Doublelist list,size_t pos){ if(list==list->head&&list->head==list->head) return NULL; struct Node* node=list->head; while(pos-->0&&node->next!=list->head){ node=node->next; } return node; } static struct Node* back_find(Doublelist list,size_t pos){ if(list==list->head&&list->head==list->head) return NULL; struct Node* node=list->head; while(pos-->0&&node->prev!=list->head){ node=node->prev; } return node; } //从前往后,在指定位置pos,插入元素 elem int front_insert(Doublelist list,size_t pos,void *pelem){ struct Node* node=(struct Node*)malloc(NODE); if(node==NULL) return -1; struct Node* prev=front_find(list,pos); if(prev==NULL) return-2; node->elem=(void *)malloc(list->elemsize); if(node->elem==NULL){ free(node); return -1; } memcpy(node->elem,pelem,list->elemsize); node->next=prev->next; node->next->prev=node; node->prev=prev; prev->next=node; list->size++; return 0; } //从后往前,在指定位置pos,插入元素 elem int back_insert(Doublelist list,size_t pos,void *pelem){ struct Node* next=back_find(list,pos); if(next==list->head) return-2; struct Node* node=(struct Node*)malloc(NODE); if(node==NULL) return -1; node->elem=(void *)malloc(list->elemsize); if(node->elem==NULL){ free(node); return -1; } memcpy(node->elem,pelem,list->elemsize); node->prev=next->prev; node->prev->next=node; node->next=next; next->prev=node; list->size++; return 0; } //在开头加入一个元素 elem int first_insert(Doublelist list,void *pelem){ struct Node* node=(struct Node*)malloc(NODE); if(node==NULL) return -1; node->elem=(void *)malloc(list->elemsize); if(node->elem==NULL){ free(node); return -1; } memcpy(node->elem,pelem,list->elemsize); node->next=list->head->next; node->next->prev=node; node->prev=list->head; list->head->next=node; list->size++; return 0; } //在末尾加入一个元素 elem int end_insert(Doublelist list,void *pelem){ struct Node* node=(struct Node*)malloc(NODE); if(node==NULL) return -1; node->elem=(void *)malloc(list->elemsize); if(node->elem==NULL){ free(node); return -1; } memcpy(node->elem,pelem,list->elemsize); node->prev=list->head->prev; node->prev->next=node; node->next=list->head; list->head->prev=node; list->size++; return 0; } //从前往后,删除指定位置pos 的元素elem int front_dele(Doublelist list,size_t pos,void *pelem){ struct Node* prev=front_find(list,pos); if(prev==list->head) return-2; struct Node* node=prev->next; memcpy(pelem,prev->next->elem,list->elemsize); free(prev->next->elem); prev->next=prev->next->next; prev->next->prev=prev; free(node); list->size--; return 0; } //从后往前,删除指定位置pos 的元素elem int back_dele(Doublelist list,size_t pos,void *pelem){ struct Node* back=back_find(list,pos); if(back==list->head) return-2; struct Node* node=back->prev; memcpy(pelem,node->elem,list->elemsize); free(node->elem); back->prev=node->prev; back->prev->next=back; free(node); list->size--; return 0; } //删除首元素 elem int first_dele(Doublelist list,void *pelem){ if(list->head==list->head||list->head->next==list->head) return -1; memcpy(pelem,list->head->next->elem,list->elemsize); free(list->head->next->elem); list->head->next=list->head->next->next; free(list->head->next->prev); list->head->next->prev=list->head; list->size--; return 0; } //删除末尾元素 elem int end_dele(Doublelist list,void *pelem){ if(list->head==list->head||list->head->prev==list->head) return -1; memcpy(pelem,list->head->prev->elem,list->elemsize); free(list->head->prev->elem); list->head->prev=list->head->prev->prev; free(list->head->prev->next); list->head->prev->next=list->head; list->size--; return 0; } //从前往后,获取指定位置pos 的元素elem int front_get(Doublelist list,size_t pos,void* pelem){ struct Node* node=front_find(list,pos); if(node==list->head) return 1; memcpy(pelem,node->next->elem,list->elemsize); return 0; } //从后往前,获取指定位置pos 的元素elem int back_get(Doublelist list,size_t pos,void *pelem){ struct Node* node=back_find(list,pos); if(node==list->head) return 1; memcpy(pelem,node->prev->elem,list->elemsize); return 0; } //获取第一个元素 int first_get(Doublelist list,void *pelem){ if(list->head==list->head||list->head->next==list->head) return -1; memcpy(pelem,list->head->next->elem,list->elemsize); return 0; } //获得最后一个元素 int end_get(Doublelist list,void *pelem){ if(list->head==list->head||list->head->prev==list->head) return -1; memcpy(pelem,list->head->prev->elem,list->elemsize); return 0; } //根据值elem来查找 返回-1/NULL表示没有该值 否则返回位置 struct Node* find_elem(Doublelist list,void *pelem,int (*cmp)(const void *,const void *)){ struct Node* node=list->head->next; int i=0; while(node!=list->head){ if(cmp(pelem,node->elem)==0) return node; i++; node=node->next; } return NULL; } int find_elem_num(Doublelist list,void* pelem,int (*cmp)(const void *,const void *)){ struct Node* node=list->head->next; int i=0; while(node!=list->head){ if(cmp(pelem,node->elem)==0) return i; i++; node=node->next; } return -1; } int find_nelem(Doublelist list,void* pelem,int n,int (*cmp)(const void *,const void *)){ struct Node* node=list->head->next; int i=0; while(n>0&&node!=list->head){ if(cmp(pelem,node->elem)==0) n--; i++; node=node->next; } if(n>0) return -1; else return i; } //遍历 void travel(Doublelist list,int flag,void (*tra)(const void *)){ if(list==NULL||list->head==NULL) return; struct Node* node=list->head->next; if(flag==0){ while(node!=list->head){ tra(node->elem); node=node->next; } }else{ while(node!=list->head){ tra(node->elem); node=node->prev; } } } //清除所有元素 void clear(Doublelist list){ if(list->head==NULL||list->head->next==NULL) return; struct Node* node=list->head->next; struct Node* next; while(node!=list->head){ next=node->next; free(node->elem); free(node); node=next; } list->head->next=list->head; list->size=0; return; } //销毁循环双链表 void destory(Doublelist list){ clear(list); free(list->head); free(list); return; }
doublelist.h
#ifndef _DOUBLELIST_H_ #define _DOUBLELIST_H_ #include<stdio.h> #include<stdlib.h> #include<stdbool.h> #include<string.h> #define NODE sizeof(struct Node) struct Node{ void* elem; struct Node *prev; struct Node *next; }; typedef struct Doublelist{ struct Node* head; int elemsize; size_t size; }*Doublelist; //创建循环双链表,头节点head Doublelist list_init(int elemsize); //判断循环双链表是否为空 bool is_empty(Doublelist list); //循环双链表元素个数 int num_element(Doublelist list); //从前往后,在指定位置pos,插入元素 elem int front_insert(Doublelist list,size_t pos,void *pelem); //从后往前,在指定位置pos,插入元素 elem int back_insert(Doublelist list,size_t pos,void *pelem); //在开头加入一个元素 elem int first_insert(Doublelist list,void *pelem); //在末尾加入一个元素 elem int end_insert(Doublelist list,void *pelem); //从前往后,删除指定位置pos 的元素elem int front_dele(Doublelist list,size_t pos,void *pelem); //从后往前,删除指定位置pos 的元素elem int back_dele(Doublelist list,size_t pos,void *pelem); //删除首元素 elem int first_dele(Doublelist list,void *pelem); //删除末尾元素 elem int end_dele(Doublelist list,void *pelem); //从前往后,获取指定位置pos 的元素elem int front_get(Doublelist list,size_t pos,void *pelem); //从后往前,获取指定位置pos 的元素elem int back_get(Doublelist list,size_t pos,void *pelem); //获取第一个元素 int first_get(Doublelist list,void *pelem); //获得最后一个元素 int end_get(Doublelist list,void *pelem); //根据值elem来查找 返回-1表示没有该值 否则返回位置 struct Node* find_elem(Doublelist list,void* pelem,int (*cmp)(const void *,const void *)); int find_elem_num(Doublelist list,void* pelem,int (*cmp)(const void *,const void *)); //查找第n个 elem int find_nelem(Doublelist list,void* pelem,int n,int (*cmp)(const void *,const void *)); //遍历 void travel(Doublelist list,int flag,void (*tra)(const void *)); //清除所有元素 void clear(Doublelist list); //销毁循环双链表 void destory(Doublelist list); #endif
10.log日志打印 log.c log.h (导子哥教的东西要学会用)
log.c
#include "log.h" FILE *pLogFile =NULL; char time_buf[TIME_BUF_LEN] = {}; char local_time_buf[TIME_BUF_LEN] = {}; char* time_to_str(time_t *t){ memset(time_buf,0,TIME_BUF_LEN); struct tm *tm = localtime(t); sprintf(time_buf,"%4d-%02d-%02d %02d:%02d:%02d ", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); return time_buf; }
log.h
#ifndef _LOG_H__ #define _LOG_H__ #include <stdio.h> #include <time.h> #include <string.h> enum LogLevel{TRACE=0,DEBUG,INFO,WARN,ERROR,FATAL}; #define LOG_LEVEL TRACE //可更改日志级别 #define TIME_BUF_LEN 48 extern FILE* pLogFile; extern char time_buf[TIME_BUF_LEN]; extern char local_time_buf[TIME_BUF_LEN]; char* time_to_str(const time_t *t); static inline char* local_time_str(){ time_t t = time(NULL); memset(local_time_buf,0,TIME_BUF_LEN); struct tm *tm = localtime(&t); sprintf(local_time_buf,"%4d-%02d-%02d %02d:%02d:%02d ", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); return local_time_buf; } //不定长宏函数 #define LOG_TRACE(fmt, args...) \ if(LOG_LEVEL<=TRACE){ \ fprintf(pLogFile,"%s [trace] %s:%d "fmt,\ local_time_str(), __FILE__, __LINE__, ##args);\ } #define LOG_DEBUG(fmt, args...) \ if(LOG_LEVEL<=DEBUG){\ fprintf(pLogFile,"%s [debug] %s:%d "fmt,\ local_time_str(), __FILE__, __LINE__, ##args);\ } #define LOG_INFO(fmt, args...) \ if(LOG_LEVEL<=INFO){\ time_t t = time(NULL);\ fprintf(pLogFile,"%s [info ] %s:%d "fmt,\ local_time_str(), __FILE__, __LINE__, ##args);\ } #define LOG_WARN(fmt, args...) \ if(LOG_LEVEL<=WARN){\ fprintf(pLogFile,"%s [warn ] %s:%d "fmt,\ local_time_str(), __FILE__, __LINE__, ##args);\ } #define LOG_ERROR(fmt, args...) \ if(LOG_LEVEL<=ERROR){\ fprintf(pLogFile,"%s [error] %s:%d "fmt,\ local_time_str(),__FILE__, __LINE__, ##args);\ } #define LOG_FATAL(fmt, args...) \ if(LOG_LEVEL<=FATAL){\ fprintf(pLogFile,"%s [fatal] %s:%d "fmt,\ local_time_str(), __FILE__, __LINE__, ##args);\ } #endif //_LOG_H__
11.最后是头文件
deal.h
#ifndef _DEAL_H_ #define _DEAL_H_ void deal(int fd,int gc,char gv[5][20],char path[40]); extern char localip[20]; extern unsigned short int localport; #endif
server.h 这个似乎好像大概完全没有必要
#ifndef _SERVER_H_ #define _SERVER_H_ #include"doublelist.h" #include"v.h" #include"tool.h" #include"log.h" #include"file.h" #include"deal.h" #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <errno.h> #include <assert.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <signal.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include <stdbool.h> #include <sys/types.h> #endif
insdeal.h
#ifndef _INSDEAL_H_ #define _INSDEAL_H_ void insdeal(int fd,int gc,char gv[5][20],char path[40]); extern char localip[20]; #endif
client.h 这个也似乎好像大概完全没有必要
#ifndef _CUSTOM_H_ #define _CUSTOM_H_ #include"doublelist.h" #include"v.h" #include"tool.h" #include"log.h" #include"file.h" #include"insdeal.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <assert.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include <signal.h> #endif
get.h
#ifndef _GET_H_ #define _GET_H_ int lsdir(const char *path,char messturn[1024]); int tree(char gv[],char messturn[1024]); #endif
tool.h
#ifndef _TOOL_H_ #define _TOOL_H_ #include<stdio.h> #include<string.h> #include<stdbool.h> #include<getch.h> void add(char* a,char *b); void ddd(char* a,char *b); int vs(char *a,char *b); void nixu(void *p,int len); char* get_pass(char* pass,size_t len,bool flag); int message_tool(char mess[5][20],char ord[100]); #endif
file.h
#ifndef _FILE_H_ #define _FILE_H #include<fcntl.h> int load_cus(void); int save_cus(void); int load_num(void); int save_num(void); #endif
12.一个不成熟的总结
冗余很多,逻辑不够严谨,定个小目标,模拟ftp的客户端和服务器
by:璇玑