├── server ├── log │ └── log ├── conf │ └── server.conf ├── include │ ├── thread_factory.h │ ├── work_que.h │ └── funcpp.h ├── src │ ├── encrypt.cpp │ ├── tcpInit.cpp │ ├── work_que.cpp │ ├── mysqlDbHelper.cpp │ ├── transFile.cpp │ └── main.cpp └── CMakeLists.txt ├── images ├── timing_wheel.png ├── sequence_diagram.png ├── deployment_diagram.png └── reglogin_sequence.png ├── client ├── src │ ├── encrypt.cpp │ ├── transFile.cpp │ └── client.cpp ├── CMakeLists.txt └── include │ └── funcpp.h └── readme.md /server/log/log: -------------------------------------------------------------------------------- 1 | this is log 2 | -------------------------------------------------------------------------------- /server/conf/server.conf: -------------------------------------------------------------------------------- 1 | 192.168.67.128 2 | 2000 3 | /home/yu/AOS/FileServer/server/home 4 | -------------------------------------------------------------------------------- /images/timing_wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xty438307820/NetDisk-Private/HEAD/images/timing_wheel.png -------------------------------------------------------------------------------- /images/sequence_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xty438307820/NetDisk-Private/HEAD/images/sequence_diagram.png -------------------------------------------------------------------------------- /images/deployment_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xty438307820/NetDisk-Private/HEAD/images/deployment_diagram.png -------------------------------------------------------------------------------- /images/reglogin_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xty438307820/NetDisk-Private/HEAD/images/reglogin_sequence.png -------------------------------------------------------------------------------- /server/include/thread_factory.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_FACTORY__ 2 | #define __THREAD_FACTORY__ 3 | #include "work_que.h" 4 | 5 | //线程结构体 6 | typedef struct{ 7 | pthread_t *pthid;//线程id 8 | pQue threadQue; 9 | pthread_cond_t cond;//条件变量,用于同步 10 | int state;//状态,1为开启,0为关闭 11 | }factory,*pFactory; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /client/src/encrypt.cpp: -------------------------------------------------------------------------------- 1 | #include "funcpp.h" 2 | 3 | void GenerateStr(char* p){ 4 | char buf[STR_LEN+1]={0}; 5 | int i,flag; 6 | 7 | srand(time(NULL)); 8 | for(i=0;icurrentSize==pq->Capacity){ 5 | return -1; 6 | } 7 | if(NULL==pq->phead){ 8 | pq->phead=pnode; 9 | pq->pTail=pnode; 10 | } 11 | else{ 12 | pq->pTail->nextNode=pnode; 13 | pq->pTail=pnode; 14 | } 15 | pq->currentSize++; 16 | return 0; 17 | } 18 | 19 | int quePop(pQue pq,pNode* pnode){ 20 | if(pq->currentSize==0){ 21 | return -1; 22 | } 23 | (*pnode)=pq->phead; 24 | pq->phead=pq->phead->nextNode; 25 | pq->currentSize--; 26 | return 0; 27 | } 28 | 29 | int queEmpty(pQue pq){ 30 | if(pq->currentSize==0) return 1; 31 | return 0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /server/include/work_que.h: -------------------------------------------------------------------------------- 1 | #ifndef __WORK_QUE__ 2 | #define __WORK_QUE__ 3 | #include "funcpp.h" 4 | 5 | typedef struct{ 6 | char absPath[1024];//home路径 7 | char curPath[1024];//相对路径 8 | char UserName[30];//用户名 9 | char Virtual[1024];//虚拟文件目录路径 10 | int state;//状态:0为刚连接,1为登录,2为注册,3为登录成功 11 | char *token; 12 | }UserCtl,*pUserCtl; 13 | 14 | typedef struct Node{ 15 | int newfd;//与客户端传数据的socketfd 16 | int setPos;//环形队列所处位置 17 | pUserCtl pUserInfo; 18 | struct Node* nextNode; 19 | }Node,*pNode; 20 | 21 | //队列结构体,实现先来先服务 22 | typedef struct{ 23 | pNode phead,pTail; 24 | int currentSize; 25 | int Capacity;//最大容量100 26 | pthread_mutex_t queMutex; 27 | }Que,*pQue; 28 | 29 | int queInsert(pQue,pNode);//入队 30 | int quePop(pQue,pNode*);//出队 31 | int queEmpty(pQue); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake 最低版本号要求 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | # 项目信息 5 | project(FtpServer) 6 | 7 | set(INCROOT ${CMAKE_CURRENT_SOURCE_DIR}/include) 8 | set(SRCROOT ${CMAKE_CURRENT_SOURCE_DIR}/src) 9 | 10 | # 指定头文件 11 | include_directories(${INCROOT}) 12 | 13 | set(FILES_HEADER 14 | ${INCROOT}/funcpp.h 15 | ${INCROOT}/thread_factory.h 16 | ${INCROOT}/work_que.h 17 | ) 18 | 19 | set(FILES_SRC 20 | ${SRCROOT}/encrypt.cpp 21 | ${SRCROOT}/mysqlDbHelper.cpp 22 | ${SRCROOT}/tcpInit.cpp 23 | ${SRCROOT}/transFile.cpp 24 | ${SRCROOT}/work_que.cpp 25 | ${SRCROOT}/main.cpp 26 | ) 27 | 28 | # 指定生成目标 29 | add_executable(FtpServer ${FILES_HEADER} ${FILES_SRC}) 30 | 31 | # 目标文件与库文件进行链接 32 | target_link_libraries(FtpServer pthread mysqlclient crypto) 33 | -------------------------------------------------------------------------------- /client/include/funcpp.h: -------------------------------------------------------------------------------- 1 | #ifndef __FUNCPP_H__ 2 | #define __FUNCPP_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | using namespace std; 47 | 48 | #define MaxNum 100 49 | #define ThreadNum 5 50 | #define STR_LEN 10 51 | #define judgeArgc(argc,num) {if(argc!=num){printf("arg err!\n");return -1;}} 52 | 53 | int tcpInit(char*,char*); 54 | int send_n(int,void*,int); 55 | int recv_n(int,void*,int); 56 | int transFile(int,char*,long);//循环发送文件 57 | int recvFile(int,char*,long);//循环接收文件 58 | void GenerateStr(char*);//生成盐值 59 | #endif 60 | -------------------------------------------------------------------------------- /server/include/funcpp.h: -------------------------------------------------------------------------------- 1 | #ifndef __FUNCPP_H__ 2 | #define __FUNCPP_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | using namespace std; 47 | 48 | #define MaxNum 100 49 | #define ThreadNum 5 50 | #define STR_LEN 10 51 | #define VirtualPath "../virtualFile" 52 | #define TimeOut 30 53 | #define judgeArgc(argc,num) {if(argc!=num){printf("arg err!\n");return -1;}} 54 | 55 | int tcpInit(char*,char*); 56 | int send_n(int,void*,int); 57 | int recv_n(int,void*,int); 58 | int transFile(int,char*,long);//循环发送文件 59 | int recvFile(int,char*,long);//循环接收文件 60 | void GenerateStr(char*);//随机生成盐值 61 | 62 | int sqlSingleSelect(char*,char*);//单返回值查询 63 | int sqlFindData(char*);//返回是否有匹配 64 | int sqlTableChange(char*);//插入、删除 65 | #endif 66 | -------------------------------------------------------------------------------- /server/src/mysqlDbHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "funcpp.h" 2 | 3 | //查询返回一个值,0成功,-1失败,buf为传出参数 4 | int sqlSingleSelect(char* sql,char* buf) 5 | { 6 | MYSQL *conn; 7 | MYSQL_RES *res; 8 | MYSQL_ROW row; 9 | const char *server="localhost"; 10 | const char *user="abc"; 11 | const char *password="123"; 12 | const char *database="FileServer"; 13 | 14 | //printf("%s\n",sql); 15 | 16 | int t; 17 | conn=mysql_init(NULL); 18 | if(!mysql_real_connect(conn,server,user,password,database,0,NULL,0)){ 19 | //printf("Error connecting to database:%s\n",mysql_error(conn)); 20 | return -1; 21 | } 22 | else{ 23 | //printf("Connected...\n"); 24 | } 25 | t=mysql_query(conn,sql); 26 | if(t){ 27 | //printf("Error making query:%s\n",mysql_error(conn)); 28 | mysql_close(conn); 29 | return -1; 30 | } 31 | else{ 32 | res=mysql_use_result(conn); 33 | row=mysql_fetch_row(res); 34 | if(row){ 35 | strcpy(buf,row[0]); 36 | } 37 | else{ 38 | //printf("Don't find data\n"); 39 | mysql_free_result(res); 40 | mysql_close(conn); 41 | return -1; 42 | } 43 | mysql_free_result(res); 44 | } 45 | mysql_close(conn); 46 | return 0; 47 | } 48 | 49 | //返回是否有匹配,有则返回1,无则返回0 50 | int sqlFindData(char* sql){ 51 | MYSQL *conn; 52 | MYSQL_RES *res; 53 | MYSQL_ROW row; 54 | const char *server="localhost"; 55 | const char *user="abc"; 56 | const char *password="123"; 57 | const char *database="FileServer"; 58 | 59 | //printf("%s\n",sql); 60 | 61 | int t; 62 | conn=mysql_init(NULL); 63 | if(!mysql_real_connect(conn,server,user,password,database,0,NULL,0)){ 64 | //printf("Error connecting to database:%s\n",mysql_error(conn)); 65 | return -1; 66 | } 67 | else{ 68 | //printf("Connected...\n"); 69 | } 70 | t=mysql_query(conn,sql); 71 | if(t){ 72 | //printf("Error making query:%s\n",mysql_error(conn)); 73 | mysql_close(conn); 74 | return -1; 75 | } 76 | else{ 77 | res=mysql_use_result(conn); 78 | row=mysql_fetch_row(res); 79 | if(row){ 80 | mysql_free_result(res); 81 | mysql_close(conn); 82 | return 1; 83 | } 84 | else{ 85 | //printf("Don't find data\n"); 86 | mysql_free_result(res); 87 | mysql_close(conn); 88 | return 0; 89 | } 90 | mysql_free_result(res); 91 | } 92 | mysql_close(conn); 93 | return 0; 94 | } 95 | 96 | int sqlTableChange(char* sql){ 97 | MYSQL *conn; 98 | const char *server="localhost"; 99 | const char *user="abc"; 100 | const char *password="123"; 101 | const char *database="FileServer"; 102 | 103 | //printf("%s\n",sql); 104 | 105 | int t; 106 | conn=mysql_init(NULL); 107 | if(!mysql_real_connect(conn,server,user,password,database,0,NULL,0)){ 108 | //printf("Error connecting to database:%s\n",mysql_error(conn)); 109 | return -1; 110 | } 111 | else{ 112 | //printf("Connected...\n"); 113 | } 114 | t=mysql_query(conn,sql); 115 | if(t){ 116 | //printf("Error making query:%s\n",mysql_error(conn)); 117 | mysql_close(conn); 118 | return -1; 119 | } 120 | mysql_close(conn); 121 | return 0; 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /server/src/transFile.cpp: -------------------------------------------------------------------------------- 1 | #include "funcpp.h" 2 | 3 | int send_n(int newfd,void *data,int dataLen){ 4 | int ret,total=0; 5 | char* snd=(char*)data; 6 | while(total CREATE USER 'abc'@'localhost' IDENTIFIED BY '123'; 20 | //创建数据库 21 | mysql> create database FileServer; 22 | //分配权限 23 | mysql> GRANT ALL PRIVILEGES ON FileServer.* TO 'abc'@'localhost'; 24 | mysql> exit 25 | //以普通用户登陆 26 | mysql -uabc -p123 27 | ``` 28 | 29 | 创建并初始化数据表 30 | 31 | ```mysql 32 | use FileServer; 33 | create table UserInfo( 34 | UserName varchar(128) primary key, 35 | Salt char(32) not null, 36 | PassWord char(128) not null 37 | ); 38 | ``` 39 | 40 | 41 | 42 | # 功能需求 43 | 44 | ## 一、基本功能 45 | 46 | 编写服务器端,服务器端启动,然后启动客户端,通过客户端可以输入以下命令进行服务器上的文件查看: 47 | 48 | ``` 49 | 1.cd [目录名] #进入对应目录,cd .. 进入上级目录 50 | 2.ls #列出相应目录文件 51 | 3.puts [文件名] #将本地文件上传至服务器 52 | 4.gets [文件名] #下载服务器文件到本地 53 | 5.remove [文件、目录名] #删除服务器上文件或目录 54 | 6.pwd #显示目前所在路径 55 | 7.mkdir [目录名] #创建目录 56 | 8.clear #清空屏幕 57 | 9.其他命令不响应 58 | ``` 59 | 60 | ## 二、扩展功能 61 | 62 | ### 2.1 用户注册,密码验证 63 | 64 | 客户端进行用户密码验证后,才可进行操作,客户端只能看到自己的文件,不能看到其他用户的文件。 65 | 66 | 服务端通过数据库存储用户名和密码。 67 | 68 | ### 2.2 断点续传 69 | 70 | 文件传输过程中断,下次传输同一文件从中断处开始传输 71 | 72 | ### 2.3 日志记录 73 | 74 | 服务端记录客户端连接时间及操作时间,写入log文件 75 | 76 | ### 2.4 超时断开 77 | 78 | 针对连接上的客户端,超过30秒未发送任何请求,关闭其描述符 79 | 80 | ### 2.5 大文件加速传递 81 | 82 | 文件大小大于100M,将大文件映射进内存,进行网络传递 83 | 84 | 85 | 86 | # 运行项目 87 | 88 | ## 一、运行服务端 89 | 90 | 编译 91 | 92 | ```bash 93 | cd server 94 | mkdir home virtualFile //创建用户工作目录 95 | mkdir build 96 | cd build 97 | cmake .. 98 | make 99 | ``` 100 | 101 | 修改 conf/server.conf 服务端ip和端口 102 | 103 | ``` 104 | 192.168.67.128 //ip 105 | 2000 //端口号 106 | /home/yu/AOS/FileServer/server/home //用户工作目录 107 | ``` 108 | 109 | 启动服务端 110 | 111 | ```bash 112 | ./FtpServer ../conf/server.conf 113 | ``` 114 | 115 | ## 二、运行客户端 116 | 117 | 编译 118 | 119 | ```bash 120 | cd client 121 | mkdir build 122 | cd build 123 | cmake .. 124 | make 125 | ``` 126 | 127 | 启动客户端 128 | 129 | ```bash 130 | ./FtpClient [服务端ip] [服务端端口] 131 | ``` 132 | 133 | ## 三、测试 134 | 135 | 先启动服务器,再启动客户端程序 136 | 137 | ### 3.1 连接成功,注册账号 138 | 139 | ``` 140 | 服务端输出: 141 | thread pool start success 142 | client 192.168.67.128 36714 connect 143 | ... 144 | 145 | 客户端输出: 146 | connect success 147 | Please choose your action: 148 | 1.login 149 | 2.register 150 | >> 2 151 | Please input you UserName:user1 152 | Input password: 153 | register success 154 | ``` 155 | 156 | ### 3.2 注册成功,进行登陆 157 | 158 | ``` 159 | Please choose your action: 160 | 1.login 161 | 2.register 162 | >> 1 163 | Please input your UserName:user1 164 | Input password: 165 | login success 166 | ``` 167 | 168 | ### 3.3 登陆成功,进行操作 169 | 170 | 创建3个目录,列出目录,进入目录,查看目前所在路径 171 | 172 | ``` 173 | user1> mkdir dir1 174 | user1> mkdir dir2 175 | user1> mkdir dir3 176 | user1> ls 177 | type name size 178 | d dir1 4096B 179 | d dir2 4096B 180 | d dir3 4096B 181 | user1> cd dir1 182 | user1> pwd 183 | /dir1 184 | user1> cd .. 185 | user1> pwd 186 | / 187 | ``` 188 | 189 | 将本地文件上传 190 | 191 | ``` 192 | //清空屏幕 193 | user1> clear 194 | user1> puts The_Holy_Bible.txt 195 | 100.0% 196 | puts success 197 | user1> ls 198 | type name size 199 | d dir1 4096B 200 | d dir2 4096B 201 | d dir3 4096B 202 | - The_Holy_Bible.txt 4351658B 203 | ``` 204 | 205 | 将本地文件The_Holy_Bible.txt删除,测试下载功能 206 | 207 | ``` 208 | user1> gets The_Holy_Bible.txt 209 | 100.00% 210 | gets success 211 | ``` 212 | 213 | 测试remove功能,删除一个文件和一个目录 214 | 215 | ``` 216 | user1> remove The_Holy_Bible.txt 217 | user1> remove dir1 218 | user1> ls 219 | type name size 220 | d dir2 4096B 221 | d dir3 4096B 222 | ``` 223 | 224 | 等待30秒不输入命令,服务端自动断开客户端连接 225 | 226 | 227 | 228 | 日志记录查看log文件夹下的log文件 229 | 230 | # 设计图 231 | 232 | ## 注册登陆时序图 233 | 234 | 用户注册,客户端生成随机盐值(salt),对用户密码进行加密,客户端将用户账号,盐值,密文密码传输至服务端,服务端将用户信息存储到mysql中;用户登陆,客户端先将用户名传输给服务端,服务端根据用户名查找盐值,然后返回盐值给客户端,客户端用拿到的盐值对密码加密,然后传输密文密码至服务端,服务端将密文密码与mysql存储的密文密码进行比较,返回登陆成功与否 235 | 236 | ![reglogin_sequence.png](./images/reglogin_sequence.png) 237 | 238 | ## 系统时序图 239 | 240 | ![sequence_diagram](./images/sequence_diagram.png) 241 | 242 | ## 系统部署图 243 | 244 | ![deployment_diagram](./images/deployment_diagram.png) 245 | 246 | ## 应用层数据包格式 247 | 248 | ———————————————————————————— 249 | 250 | |        数据长度(n)      |                                数 据                           | 251 | 252 | |              (4 B)            |                                (n B)                            | 253 | 254 | ———————————————————————————— 255 | 256 | ## 超时断开算法 257 | 258 | 使用环形队列法,环上每个节点维护一个Set,监控线程设定每隔1秒触发的定时器,每次监控线程定时器超时,前进一个节点,将此节点的fd全部断开,然后清空Set;当客户端有请求到达时,从Set中删除此fd,然后将fd加进当前节点的前一个位置的Set中。 259 | 260 | 261 | 262 | ![timing_wheel](./images/timing_wheel.png) -------------------------------------------------------------------------------- /client/src/transFile.cpp: -------------------------------------------------------------------------------- 1 | #include "funcpp.h" 2 | 3 | int send_n(int newfd,void *data,int dataLen){ 4 | int ret,total=0; 5 | char* snd=(char*)data; 6 | while(total=distSize){ 81 | printf("%5.2f%s\r",(double)currentSize/totalSize*100,"%"); 82 | fflush(stdout); 83 | preSize=currentSize; 84 | } 85 | } 86 | ret=send_n(newfd,&dataLen,sizeof(int)); 87 | if(-1==ret){ 88 | close(fd); 89 | return -1; 90 | } 91 | } 92 | else{//大于100M用mmap发送 93 | char *p=(char*)mmap(NULL,totalSize,PROT_READ,MAP_SHARED,fd,0); 94 | dataLen=1024; 95 | while(currentSize=distSize){ 111 | printf("%5.2f%s\r",(double)currentSize/totalSize*100,"%"); 112 | fflush(stdout); 113 | preSize=currentSize; 114 | } 115 | } 116 | munmap(p,totalSize); 117 | } 118 | printf("100.0%s \n","%"); 119 | close(fd); 120 | return 0; 121 | } 122 | 123 | int recvFile(int newfd,char* curPath,long offset){ 124 | 125 | //接文件名 126 | char buf[1024]={0}; 127 | int dataLen; 128 | int ret=recv_n(newfd,&dataLen,sizeof(int)); 129 | if(-1==ret){ 130 | return -1; 131 | } 132 | recv_n(newfd,buf+strlen(buf),dataLen); 133 | strcpy(buf,curPath); 134 | if(-1==ret){ 135 | return -1; 136 | } 137 | 138 | //接文件大小 139 | int fd=open(buf,O_RDWR|O_CREAT|O_APPEND,0666); 140 | long totalSize; 141 | ret=recv_n(newfd,&dataLen,sizeof(int)); 142 | if(-1==ret){ 143 | close(fd); 144 | return -1; 145 | } 146 | ret=recv_n(newfd,&totalSize,dataLen); 147 | if(-1==ret){ 148 | close(fd); 149 | return -1; 150 | } 151 | 152 | long currentSize=offset,preSize=offset; 153 | long distSize=totalSize/1000;//每0.1%更新进度条 154 | 155 | //接文件内容 156 | //小于100M普通循环接收 157 | if(totalSize<=(long)100*1024*1024){ 158 | while(1){ 159 | ret=recv_n(newfd,&dataLen,sizeof(int)); 160 | if(0==dataLen) break; 161 | if(-1==ret){ 162 | close(fd); 163 | return -1; 164 | } 165 | ret=recv_n(newfd,buf,dataLen); 166 | if(-1==ret){ 167 | close(fd); 168 | return -1; 169 | } 170 | write(fd,buf,dataLen); 171 | currentSize+=dataLen; 172 | if(currentSize-preSize>=distSize){ 173 | printf("%5.2f%s\r",(double)currentSize/totalSize*100,"%"); 174 | fflush(stdout); 175 | preSize=currentSize; 176 | } 177 | } 178 | } 179 | else{//大于100M用mmap接收 180 | ftruncate(fd,totalSize); 181 | char *p=(char*)mmap(NULL,totalSize,PROT_WRITE,MAP_SHARED,fd,0); 182 | while(currentSize=distSize){ 199 | printf("%5.2f%s\r",(double)currentSize/totalSize*100,"%"); 200 | fflush(stdout); 201 | preSize=currentSize; 202 | } 203 | } 204 | munmap(p,totalSize); 205 | } 206 | printf("100.00%s \n","%"); 207 | close(fd); 208 | return 0; 209 | } 210 | -------------------------------------------------------------------------------- /client/src/client.cpp: -------------------------------------------------------------------------------- 1 | #include "funcpp.h" 2 | 3 | void splitChar(char*,char*,char*);//分割char*,将中间空格分开,后两个参数为传出参数 4 | int fileExist(char*,char*,char);//根据传入的相对路径判断文件或文件夹是否存在 5 | void* threadFunc(void*);//子线程执行puts和gets 6 | 7 | char ip[32]; 8 | char port[10]; 9 | char token[64]={0}; 10 | char userName[128]={0}; 11 | 12 | int main(int argc,char* argv[]) 13 | { 14 | judgeArgc(argc,3); 15 | 16 | strcpy(ip,argv[1]); 17 | strcpy(port,argv[2]); 18 | //客户端连接 19 | int socketfd=socket(AF_INET,SOCK_STREAM,0); 20 | struct sockaddr_in sock; 21 | memset(&sock,0,sizeof(sockaddr_in)); 22 | sock.sin_family=AF_INET; 23 | sock.sin_port=htons(atoi(argv[2])); 24 | sock.sin_addr.s_addr=inet_addr(argv[1]); 25 | int ret=connect(socketfd,(struct sockaddr*)&sock,sizeof(sockaddr_in)); 26 | if(-1==ret){ 27 | perror("connect"); 28 | return -1; 29 | } 30 | cout<<"connect success"<>dataLen; 49 | while(!(dataLen==1||dataLen==2)){ 50 | cout<<"Please enter 1 to login or 2 to register"<>dataLen; 52 | } 53 | //瞬时命令先发送1 54 | ret=1; 55 | send_n(socketfd,&ret,sizeof(int)); 56 | send_n(socketfd,&dataLen,sizeof(int)); 57 | if(dataLen==1){//登录 58 | cout<<"Please input your UserName:"; 59 | scanf("%s",buf); 60 | getchar(); 61 | dataLen=strlen(buf); 62 | strcpy(userName,buf); 63 | 64 | //登录第一步,编号1 65 | ret=1;//瞬时命令 66 | send_n(socketfd,&ret,sizeof(int)); 67 | workNum='1'; 68 | send_n(socketfd,&workNum,1); 69 | send_n(socketfd,&dataLen,sizeof(int)); 70 | send_n(socketfd,buf,dataLen); 71 | memset(salt,0,sizeof(salt)); 72 | recv_n(socketfd,&dataLen,sizeof(int)); 73 | recv_n(socketfd,salt,dataLen); 74 | 75 | //登录第二步,编号2 76 | ret=1;//瞬时命令 77 | send_n(socketfd,&ret,sizeof(int)); 78 | pwd=getpass("Input password:"); 79 | secret=crypt(pwd,salt); 80 | dataLen=strlen(secret); 81 | workNum='2'; 82 | send_n(socketfd,&workNum,1); 83 | send_n(socketfd,&dataLen,sizeof(int)); 84 | send_n(socketfd,secret,dataLen); 85 | 86 | recv_n(socketfd,&ret,sizeof(int)); 87 | if(1==ret){//第一次登录成功 88 | cout<<"login success"<30){ 124 | cout<<"UserName should be less than 30 bytes,enter anain:"; 125 | memset(buf,0,sizeof(buf)); 126 | scanf("%s",buf); 127 | dataLen=strlen(buf); 128 | } 129 | ret=1; 130 | send_n(socketfd,&ret,sizeof(int)); 131 | workNum='1'; 132 | send_n(socketfd,&workNum,1); 133 | send_n(socketfd,&dataLen,sizeof(int)); 134 | send_n(socketfd,buf,dataLen); 135 | 136 | recv_n(socketfd,&ret,sizeof(int)); 137 | while(ret){ 138 | cout<<"UserName already exists,enter new UserName:"; 139 | memset(buf,0,sizeof(buf)); 140 | scanf("%s",buf); 141 | dataLen=strlen(buf); 142 | ret=1; 143 | send_n(socketfd,&ret,sizeof(int)); 144 | send_n(socketfd,&workNum,1); 145 | send_n(socketfd,&dataLen,sizeof(int)); 146 | send_n(socketfd,buf,dataLen); 147 | recv_n(socketfd,&ret,sizeof(int)); 148 | } 149 | 150 | //注册第二步,输入密码 151 | pwd=getpass("Input password:"); 152 | GenerateStr(salt); 153 | dataLen=strlen(salt); 154 | ret=1; 155 | send_n(socketfd,&ret,sizeof(int)); 156 | workNum='2'; 157 | send_n(socketfd,&workNum,1); 158 | send_n(socketfd,&dataLen,sizeof(int)); 159 | send_n(socketfd,salt,dataLen); 160 | secret=crypt(pwd,salt); 161 | dataLen=strlen(secret); 162 | send_n(socketfd,&dataLen,sizeof(int)); 163 | send_n(socketfd,secret,dataLen); 164 | cout<<"register success"< ",userName); 170 | after_puts: 171 | memset(buf,0,sizeof(buf)); 172 | memset(operate,0,sizeof(operate)); 173 | memset(operand,0,sizeof(operand)); 174 | fgets(buf,sizeof(buf),stdin); 175 | buf[strlen(buf)-1]=0; 176 | if(strcmp(buf,"clear")==0){ 177 | system("clear"); 178 | continue; 179 | } 180 | //瞬时命令先发送1 181 | ret=1; 182 | send_n(socketfd,&ret,sizeof(int)); 183 | 184 | dataLen=strlen(buf); 185 | ret=send_n(socketfd,&dataLen,sizeof(int)); 186 | if(-1==ret){ 187 | cout<<"Connection close,no operation in 30 seconds."< ",userName); 491 | fflush(stdout); 492 | close(socketfd); 493 | return (void*)0; 494 | } 495 | 496 | int fileExist(char* curPath,char* fileName,char fileType){ 497 | DIR *dir=opendir(curPath); 498 | struct dirent *dnt; 499 | char buf[128]={0}; 500 | strcpy(buf,curPath); 501 | buf[strlen(buf)]='/'; 502 | int i=strlen(buf); 503 | struct stat sta; 504 | char type; 505 | while((dnt=readdir(dir))!=NULL){ 506 | buf[i]=0; 507 | strcat(buf,dnt->d_name); 508 | stat(buf,&sta); 509 | if(sta.st_mode>>12==4){//4是目录 510 | type='d'; 511 | } 512 | else if(sta.st_mode>>12==8){//8是文件 513 | type='-'; 514 | } 515 | if(strcmp(dnt->d_name,fileName)==0&&fileType==type){ 516 | closedir(dir); 517 | return 1; 518 | } 519 | } 520 | closedir(dir); 521 | return 0; 522 | } 523 | 524 | void splitChar(char* from,char* operate,char* operand){ 525 | int fromLen=strlen(from); 526 | int i=0,j=0; 527 | while(from[i]==' '&&i fdSet[TimeOut+1];//环形队列,用于30s断开连接,可改funcpp.h里面的TimeOut宏更改超时秒数 16 | int fdPointer=0;//记录环形队列指针位置 17 | pthread_mutex_t setMutex; 18 | map mp;//将newfd和pNode对应 19 | map tok;//将token与newfd对应 20 | factory f;//全局变量f,用于puts和gets 21 | 22 | int main(int argc,char* argv[]) 23 | { 24 | judgeArgc(argc,2); 25 | 26 | //读配置文件 27 | char ip[20]={0}; 28 | char port[10]={0}; 29 | char absPath[1024]={0}; 30 | ServerInit(argv[1],ip,port,absPath); 31 | 32 | //创建子线程 33 | int i,j; 34 | memset(&f,0,sizeof(f)); 35 | f.pthid=(pthread_t*)calloc(ThreadNum+1,sizeof(pthread_t)); 36 | f.threadQue=(pQue)calloc(1,sizeof(Que)); 37 | f.threadQue->Capacity=MaxNum; 38 | pthread_mutex_init(&f.threadQue->queMutex,NULL); 39 | pthread_mutex_init(&setMutex,NULL); 40 | pthread_cond_init(&f.cond,NULL); 41 | f.state=1;//1为线程池开启 42 | logfd=open("../log/log",O_WRONLY|O_APPEND|O_CREAT,0666); 43 | for(i=0;inewfd=newfd; 86 | pnode->pUserInfo=(pUserCtl)calloc(1,sizeof(UserCtl)); 87 | strcpy(pnode->pUserInfo->absPath,absPath); 88 | strcpy(pnode->pUserInfo->Virtual,VirtualPath); 89 | if(currentSizenewfd]=pnode; 93 | } 94 | else ret=-1; 95 | if(-1==ret){//队满直接关闭 96 | close(newfd); 97 | free(pnode); 98 | pnode=NULL; 99 | } 100 | if(0==ret){ 101 | event.data.fd=newfd; 102 | epoll_ctl(epfd,EPOLL_CTL_ADD,newfd,&event); 103 | } 104 | } 105 | else{ 106 | newfd=evs[i].data.fd; 107 | pnode=mp[newfd]; 108 | 109 | //插入环形队列 110 | pthread_mutex_lock(&setMutex); 111 | pnode->setPos=fdPointer-1; 112 | if(-1==pnode->setPos) pnode->setPos=TimeOut; 113 | fdSet[pnode->setPos].insert(pnode->newfd); 114 | pthread_mutex_unlock(&setMutex); 115 | 116 | dataLen=1; 117 | recv_n(newfd,&dataLen,sizeof(int)); 118 | if(1==dataLen){//1为瞬时命令 119 | 120 | //更新环形队列 121 | pthread_mutex_lock(&setMutex); 122 | fdSet[pnode->setPos].erase(pnode->newfd); 123 | pnode->setPos=fdPointer-1; 124 | if(-1==pnode->setPos) pnode->setPos=TimeOut; 125 | fdSet[pnode->setPos].insert(pnode->newfd); 126 | pthread_mutex_unlock(&setMutex); 127 | 128 | if(0==pnode->pUserInfo->state){//刚连接只能接收1或2 129 | ret=recv_n(newfd,&dataLen,sizeof(int)); 130 | if(-1==ret){ 131 | close(newfd); 132 | event.data.fd=newfd; 133 | epoll_ctl(epfd,EPOLL_CTL_DEL,newfd,&event); 134 | free(mp[newfd]); 135 | pnode=NULL; 136 | mp.erase(newfd); 137 | currentSize--; 138 | continue; 139 | } 140 | if(1==dataLen) pnode->pUserInfo->state=1;//登录状态 141 | else if(2==dataLen) pnode->pUserInfo->state=2;//注册状态 142 | } 143 | else if(1==pnode->pUserInfo->state){//登录 144 | ret=userLogin(newfd,pnode); 145 | if(-1==ret){//两次登录失败 146 | close(newfd); 147 | event.data.fd=newfd; 148 | epoll_ctl(epfd,EPOLL_CTL_DEL,newfd,&event); 149 | free(mp[newfd]); 150 | pnode=NULL; 151 | mp.erase(newfd); 152 | currentSize--; 153 | } 154 | } 155 | else if(2==pnode->pUserInfo->state){//注册 156 | ret=userRegister(newfd,pnode); 157 | if(-1==ret){//客户端断开,注册失败 158 | close(newfd); 159 | event.data.fd=newfd; 160 | epoll_ctl(epfd,EPOLL_CTL_DEL,newfd,&event); 161 | free(mp[newfd]); 162 | pnode=NULL; 163 | mp.erase(newfd); 164 | currentSize--; 165 | } 166 | } 167 | else if(3==pnode->pUserInfo->state){//登录成功 168 | ret=userAction(pnode); 169 | if(-1==ret){ 170 | cout<<"Client bye"<pUserInfo->token);//注销token 172 | close(newfd); 173 | event.data.fd=newfd; 174 | epoll_ctl(epfd,EPOLL_CTL_DEL,newfd,&event); 175 | free(mp[newfd]); 176 | pnode=NULL; 177 | mp.erase(newfd); 178 | currentSize--; 179 | } 180 | } 181 | } 182 | else if(2==dataLen){//2为puts或gets数据连接 183 | 184 | //puts和gets从环形队列删除 185 | pthread_mutex_lock(&setMutex); 186 | fdSet[pnode->setPos].erase(pnode->newfd); 187 | pthread_mutex_unlock(&setMutex); 188 | 189 | //执行token认证 190 | recv_n(newfd,&dataLen,sizeof(int)); 191 | memset(buf,0,sizeof(buf)); 192 | recv_n(newfd,buf,dataLen); 193 | 194 | //认证成功 195 | if(tok[buf]&&strcmp(buf,mp[tok[buf]]->pUserInfo->token)==0){ 196 | ret=0; 197 | send_n(newfd,&ret,sizeof(int)); 198 | strcpy(pnode->pUserInfo->absPath,mp[tok[buf]]->pUserInfo->absPath); 199 | strcpy(pnode->pUserInfo->curPath,mp[tok[buf]]->pUserInfo->curPath); 200 | strcpy(pnode->pUserInfo->UserName,pnode->pUserInfo->UserName); 201 | pthread_mutex_lock(&f.threadQue->queMutex); 202 | ret=queInsert(f.threadQue,pnode); 203 | pthread_mutex_unlock(&f.threadQue->queMutex); 204 | if(-1==ret){//队满 205 | send_n(newfd,&ret,sizeof(int)); 206 | close(newfd); 207 | free(mp[newfd]); 208 | pnode=NULL; 209 | mp.erase(newfd); 210 | currentSize--; 211 | continue; 212 | } 213 | event.data.fd=newfd; 214 | epoll_ctl(epfd,EPOLL_CTL_DEL,newfd,&event); 215 | send_n(newfd,&ret,sizeof(int)); 216 | pthread_cond_signal(&f.cond); 217 | currentSize--; 218 | } 219 | else{//认证失败 220 | ret=-1; 221 | send_n(newfd,&ret,sizeof(int)); 222 | close(newfd); 223 | event.data.fd=newfd; 224 | epoll_ctl(epfd,EPOLL_CTL_DEL,newfd,&event); 225 | free(mp[newfd]); 226 | pnode=NULL; 227 | mp.erase(newfd); 228 | currentSize--; 229 | } 230 | } 231 | } 232 | } 233 | } 234 | 235 | close(epfd); 236 | close(logfd); 237 | close(socketfd); 238 | return 0; 239 | } 240 | 241 | int userAction(void* p){//登录成功,为用户服务 242 | char buf[128]={0}; 243 | char buf1[128]={0}; 244 | char operate[32]={0};//操作类型 245 | char operand[96]={0};//操作对象 246 | char *secret,*pwd; 247 | char sql[216]={0}; 248 | pNode pnode=(pNode)p; 249 | int ret,i,dataLen,fd; 250 | 251 | DIR *dir; 252 | struct dirent *dnt; 253 | struct stat sta; 254 | long fileSize; 255 | 256 | time_t tim; 257 | tim=time(NULL); 258 | 259 | ret=recv_n(pnode->newfd,&dataLen,sizeof(int)); 260 | if(-1==ret) return -1; 261 | ret=recv_n(pnode->newfd,buf,dataLen); 262 | if(-1==ret) return -1; 263 | 264 | //将操作数和对象分开 265 | splitChar(buf,operate,operand); 266 | 267 | if(strcmp(operate,"cd")==0){ 268 | ctime_r(&tim,buf1); 269 | sprintf(buf,"%s: %s %s %s",pnode->pUserInfo->UserName,operate,operand,buf1); 270 | write(logfd,buf,strlen(buf)); 271 | if(strcmp(operand,".")==0){ 272 | ret=1; 273 | send_n(pnode->newfd,&ret,sizeof(int)); 274 | return 0; 275 | } 276 | else if(strcmp(operand,"..")==0){ 277 | if(strlen(pnode->pUserInfo->curPath)==0){ 278 | ret=1; 279 | send_n(pnode->newfd,&ret,sizeof(int)); 280 | return 0; 281 | } 282 | i=strlen(pnode->pUserInfo->curPath)-1; 283 | while(i>=0&&pnode->pUserInfo->curPath[i]!='/') i--; 284 | if(i>=0) pnode->pUserInfo->curPath[i]=0; 285 | } 286 | else if(operand[0]=='/'){ 287 | strcat(pnode->pUserInfo->curPath,operand); 288 | } 289 | else{ 290 | pnode->pUserInfo->curPath[strlen(pnode->pUserInfo->curPath)+1]=0; 291 | strcpy(buf,pnode->pUserInfo->absPath); 292 | strcat(buf,pnode->pUserInfo->curPath); 293 | ret=fileExist(buf,operand,'d'); 294 | if(0==ret){ 295 | send_n(pnode->newfd,&ret,sizeof(int)); 296 | return 0; 297 | } 298 | pnode->pUserInfo->curPath[strlen(pnode->pUserInfo->curPath)]='/'; 299 | strcat(pnode->pUserInfo->curPath,operand); 300 | } 301 | ret=1; 302 | send_n(pnode->newfd,&ret,sizeof(int)); 303 | } 304 | else if(strcmp(operate,"ls")==0){ 305 | ctime_r(&tim,buf1); 306 | sprintf(buf,"%s: %s %s",pnode->pUserInfo->UserName,operate,buf1); 307 | write(logfd,buf,strlen(buf)); 308 | strcpy(buf,pnode->pUserInfo->absPath); 309 | strcat(buf,pnode->pUserInfo->curPath); 310 | dir=opendir(buf); 311 | i=strlen(buf)+1; 312 | buf[i-1]='/'; 313 | 314 | while((dnt=readdir(dir))!=NULL){ 315 | if(dnt->d_name[0]=='.'||!strcmp("..",dnt->d_name)||!strcmp(".",dnt->d_name)) continue; 316 | buf[i]=0; 317 | strcat(buf,dnt->d_name); 318 | stat(buf,&sta); 319 | 320 | //传文件类型 321 | ret=sta.st_mode>>12; 322 | if(8==ret){//8为文件 323 | buf1[0]='-'; 324 | send_n(pnode->newfd,buf1,1); 325 | } 326 | else if(4==ret){//4为目录 327 | buf1[0]='d'; 328 | send_n(pnode->newfd,buf1,1); 329 | } 330 | 331 | //传文件名 332 | strcpy(buf1,dnt->d_name); 333 | dataLen=strlen(buf1); 334 | send_n(pnode->newfd,&dataLen,sizeof(int)); 335 | send_n(pnode->newfd,buf1,dataLen); 336 | 337 | //传文件大小 338 | send_n(pnode->newfd,&sta.st_size,sizeof(long)); 339 | } 340 | dataLen=0; 341 | send_n(pnode->newfd,&dataLen,1); 342 | closedir(dir); 343 | dir=NULL; 344 | } 345 | else if(strcmp(operate,"puts")==0){ 346 | ctime_r(&tim,buf1); 347 | sprintf(buf,"%s: %s %s %s",pnode->pUserInfo->UserName,operate,operand,buf1); 348 | write(logfd,buf,strlen(buf)); 349 | strcpy(buf,pnode->pUserInfo->absPath); 350 | strcat(buf,pnode->pUserInfo->curPath); 351 | 352 | dataLen=recv_n(pnode->newfd,&ret,sizeof(int)); 353 | if(-1==ret) return 0; 354 | ret=fileExist(buf,operand,'d'); 355 | if(1==ret){//存在同名文件夹,不能puts 356 | ret=-1; 357 | send_n(pnode->newfd,&ret,sizeof(int)); 358 | return 0; 359 | } 360 | ret=fileExist(buf,operand,'-'); 361 | if(1==ret){//存在同名文件,不能puts 362 | ret=-1; 363 | send_n(pnode->newfd,&ret,sizeof(int)); 364 | return 0; 365 | } 366 | send_n(pnode->newfd,&ret,sizeof(int)); 367 | 368 | } 369 | else if(strcmp(operate,"gets")==0){ 370 | ctime_r(&tim,buf1); 371 | sprintf(buf,"%s: %s %s %s",pnode->pUserInfo->UserName,operate,operand,buf1); 372 | write(logfd,buf,strlen(buf)); 373 | strcpy(buf,pnode->pUserInfo->absPath); 374 | strcat(buf,pnode->pUserInfo->curPath); 375 | ret=fileExist(buf,operand,'-'); 376 | if(0==ret){//文件不存在,gets失败 377 | ret=-1; 378 | send_n(pnode->newfd,&ret,sizeof(int)); 379 | return 0; 380 | } 381 | else if(1==ret){//文件存在 382 | send_n(pnode->newfd,&ret,sizeof(int)); 383 | } 384 | } 385 | else if(strcmp(operate,"remove")==0){ 386 | ctime_r(&tim,buf1); 387 | sprintf(buf,"%s: %s %s %s",pnode->pUserInfo->UserName,operate,operand,buf1); 388 | write(logfd,buf,strlen(buf)); 389 | strcpy(buf,pnode->pUserInfo->absPath); 390 | strcat(buf,pnode->pUserInfo->curPath); 391 | ret=fileExist(buf,operand,'d'); 392 | if(1==ret){//删除文件夹 393 | send_n(pnode->newfd,&ret,sizeof(int)); 394 | buf[strlen(buf)+1]=0; 395 | buf[strlen(buf)]='/'; 396 | strcat(buf,operand); 397 | remove(buf); 398 | return 0; 399 | } 400 | ret=fileExist(buf,operand,'-'); 401 | if(1==ret){//文件存在 402 | send_n(pnode->newfd,&ret,sizeof(int)); 403 | buf[strlen(buf)+1]=0; 404 | buf[strlen(buf)]='/'; 405 | strcat(buf,operand); 406 | stat(buf,&sta); 407 | if(sta.st_nlink>2){ 408 | remove(buf); 409 | } 410 | else{//硬链接数=2,删除用户目录文件、虚拟目录文件 411 | fd=open(buf,O_RDONLY); 412 | pwd=(char*)mmap(NULL,sta.st_size,PROT_READ,MAP_SHARED,fd,0); 413 | secret=(char*)MD5((unsigned char*)pwd,sta.st_size,NULL); 414 | munmap(pwd,sta.st_size); 415 | close(fd); 416 | 417 | strcpy(buf1,VirtualPath); 418 | buf1[strlen(buf1)+1]=0; 419 | buf1[strlen(buf1)]='/'; 420 | strcat(buf1,secret); 421 | sprintf(sql,"delete from FileInfo where FileName='%s'",secret); 422 | sqlTableChange(sql); 423 | remove(buf); 424 | remove(buf1); 425 | } 426 | return 0; 427 | } 428 | else if(0==ret){//文件或文件夹不存在 429 | ret=-1; 430 | send_n(pnode->newfd,&ret,sizeof(int)); 431 | return 0; 432 | } 433 | } 434 | else if(strcmp(operate,"pwd")==0){ 435 | ctime_r(&tim,buf1); 436 | sprintf(buf,"%s: %s %s",pnode->pUserInfo->UserName,operate,buf1); 437 | write(logfd,buf,strlen(buf)); 438 | memset(buf,0,sizeof(buf)); 439 | strcpy(buf,pnode->pUserInfo->curPath); 440 | if(strlen(buf)==0){ 441 | buf[0]='/'; 442 | } 443 | dataLen=strlen(buf); 444 | send_n(pnode->newfd,&dataLen,sizeof(int)); 445 | send_n(pnode->newfd,buf,dataLen); 446 | } 447 | else if(strcmp(operate,"mkdir")==0){ 448 | ctime_r(&tim,buf1); 449 | sprintf(buf,"%s: %s %s",pnode->pUserInfo->UserName,operate,buf1); 450 | write(logfd,buf,strlen(buf)); 451 | strcpy(buf,pnode->pUserInfo->absPath); 452 | strcat(buf,pnode->pUserInfo->curPath); 453 | ret=fileExist(buf,operand,'d'); 454 | if(1==ret) ret=-1; 455 | if(0==ret) ret=fileExist(buf,operand,'-'); 456 | if(1==ret) ret=-1; 457 | send_n(pnode->newfd,&ret,sizeof(int)); 458 | if(0==ret){ 459 | buf[strlen(buf)+1]=0; 460 | buf[strlen(buf)]='/'; 461 | strcat(buf,operand); 462 | mkdir(buf,0775); 463 | } 464 | } 465 | return 0; 466 | } 467 | 468 | void* threadfunc(void* p){//puts和gets函数 469 | pNode pnode; 470 | char buf[128]={0}; 471 | char buf1[128]={0}; 472 | char operate[32]={0};//操作类型 473 | char operand[96]={0};//操作对象 474 | char sql[216]={0}; 475 | int ret,dataLen,i; 476 | 477 | struct stat sta; 478 | long fileSize; 479 | 480 | while(1){ 481 | memset(buf,0,128); 482 | memset(buf1,0,sizeof(buf1)); 483 | memset(operate,0,sizeof(operate)); 484 | memset(operand,0,sizeof(operand)); 485 | 486 | pthread_mutex_lock(&f.threadQue->queMutex); 487 | if(0==f.threadQue->currentSize){ 488 | pthread_cond_wait(&f.cond,&f.threadQue->queMutex); 489 | } 490 | ret=quePop(f.threadQue,&pnode); 491 | pthread_mutex_unlock(&f.threadQue->queMutex); 492 | if(-1==ret){ 493 | continue; 494 | } 495 | 496 | //拿到结点,开始传数据 497 | //重新接收命令 498 | ret=recv_n(pnode->newfd,&dataLen,sizeof(int)); 499 | if(-1==ret) goto end; 500 | ret=recv_n(pnode->newfd,buf,dataLen); 501 | if(-1==ret) goto end; 502 | splitChar(buf,operate,operand); 503 | 504 | if(strcmp(operate,"puts")==0){ 505 | //查找数据库,看虚拟目录是否有相同文件 506 | recv_n(pnode->newfd,&dataLen,sizeof(int)); 507 | recv_n(pnode->newfd,buf1,dataLen);//文件hash值 508 | sprintf(sql,"select FileName from FileInfo where FileName='%s'",buf1); 509 | ret=sqlFindData(sql); 510 | if(1==ret){//虚拟目录存在此文件,执行秒传 511 | ret=2; 512 | send_n(pnode->newfd,&ret,sizeof(int)); 513 | strcpy(buf,pnode->pUserInfo->Virtual); 514 | buf[strlen(buf)+1]=0; 515 | buf[strlen(buf)]='/'; 516 | strcat(buf,buf1);//buf是虚拟目录 517 | strcpy(buf1,pnode->pUserInfo->absPath); 518 | strcat(buf1,pnode->pUserInfo->curPath); 519 | buf1[strlen(buf1)+1]=0; 520 | buf1[strlen(buf1)]='/'; 521 | strcat(buf1,operand);//buf1是用户目录 522 | link(buf,buf1); 523 | goto end; 524 | } 525 | strcpy(buf,pnode->pUserInfo->absPath); 526 | strcat(buf,pnode->pUserInfo->curPath); 527 | ret=fileExist(buf,operand,'-'); 528 | if(1==ret){//文件存在,断点续传 529 | send_n(pnode->newfd,&ret,sizeof(int)); 530 | i=strlen(buf); 531 | buf[strlen(buf)+1]=0; 532 | buf[strlen(buf)]='/'; 533 | strcat(buf,operand); 534 | stat(buf,&sta); 535 | send_n(pnode->newfd,&sta.st_size,sizeof(long)); 536 | fileSize=sta.st_size; 537 | buf[i]=0; 538 | } 539 | else if(0==ret){ 540 | fileSize=0; 541 | send_n(pnode->newfd,&ret,sizeof(int)); 542 | } 543 | ret=recvFile(pnode->newfd,buf,fileSize); 544 | if(0==ret){ 545 | sprintf(sql,"insert into FileInfo values('%s')",buf1); 546 | strcpy(buf,pnode->pUserInfo->Virtual); 547 | buf[strlen(buf)+1]=0; 548 | buf[strlen(buf)]='/'; 549 | strcat(buf,buf1);//buf为虚拟目录路径 550 | strcpy(buf1,pnode->pUserInfo->absPath); 551 | strcat(buf1,pnode->pUserInfo->curPath); 552 | buf1[strlen(buf1)+1]=0; 553 | buf1[strlen(buf1)]='/'; 554 | strcat(buf1,operand);//buf1为用户空间目录路径 555 | link(buf1,buf);//建立硬链接 556 | ret=sqlTableChange(sql); 557 | } 558 | else if(-1==ret) perror("puts failed"); 559 | 560 | } 561 | else if(strcmp(operate,"gets")==0){ 562 | recv_n(pnode->newfd,&ret,sizeof(int)); 563 | if(1==ret){//文件存在,进行断点续传 564 | recv_n(pnode->newfd,&fileSize,sizeof(int)); 565 | } 566 | else{ 567 | fileSize=0; 568 | } 569 | strcpy(buf,pnode->pUserInfo->absPath); 570 | strcat(buf,pnode->pUserInfo->curPath); 571 | buf[strlen(buf)+1]=0; 572 | buf[strlen(buf)]='/'; 573 | strcat(buf,operand); 574 | ret=transFile(pnode->newfd,buf,fileSize); 575 | if(0==ret) cout<<"gets success"<newfd); 581 | free(mp[pnode->newfd]); 582 | mp.erase(pnode->newfd); 583 | pnode=NULL; 584 | } 585 | } 586 | 587 | int userRegister(int newfd,void* p){ 588 | pNode pnode=(pNode)p; 589 | char buf[128]={0}; 590 | char sql[216]={0}; 591 | char salt[32]={0}; 592 | int dataLen,ret; 593 | char workNum='0'; 594 | recv_n(newfd,&workNum,1); 595 | if('1'==workNum){//注册第一步,输入用户名 596 | recv_n(newfd,&dataLen,sizeof(int)); 597 | recv_n(newfd,buf,dataLen); 598 | sprintf(sql,"select * from UserInfo where UserName='%s'",buf); 599 | ret=sqlFindData(sql); 600 | send_n(newfd,&ret,sizeof(int)); 601 | if(ret==0){ 602 | strcpy(pnode->pUserInfo->UserName,buf); 603 | } 604 | return 0; 605 | } 606 | else if('2'==workNum){//注册第二步,输入密码 607 | recv_n(newfd,&dataLen,sizeof(int)); 608 | recv_n(newfd,&salt,dataLen); 609 | recv_n(newfd,&dataLen,sizeof(int)); 610 | recv_n(newfd,buf,dataLen); 611 | 612 | sprintf(sql,"insert into UserInfo(UserName,Salt,PassWord) values('%s','%s','%s')",pnode->pUserInfo->UserName,salt,buf); 613 | sqlTableChange(sql); 614 | 615 | memset(buf,0,sizeof(buf)); 616 | strcpy(buf,pnode->pUserInfo->absPath); 617 | buf[strlen(buf)]='/'; 618 | strcat(buf,pnode->pUserInfo->UserName); 619 | mkdir(buf,0775); 620 | pnode->pUserInfo->state=0;//注册成功,返回选择操作界面 621 | return 0; 622 | } 623 | return -1; 624 | } 625 | 626 | int userLogin(int newfd,void* p){ 627 | pNode pnode=(pNode)p; 628 | char buf[128]={0}; 629 | char buf1[128]={0}; 630 | char UserName[32]={0}; 631 | char sql[216]={0}; 632 | char salt[32]={0}; 633 | int dataLen,ret; 634 | char workNum='0'; 635 | time_t tim; 636 | recv_n(newfd,&workNum,1); 637 | //客户端登录第一步,输入用户名 638 | if('1'==workNum){ 639 | recv_n(newfd,&dataLen,sizeof(int)); 640 | recv_n(newfd,UserName,dataLen); 641 | sprintf(sql,"select Salt from UserInfo where UserName='%s'",UserName); 642 | sqlSingleSelect(sql,salt); 643 | if(strlen(salt)==0) strcpy(salt,"$6$sss");//没有该用户则给该盐值 644 | dataLen=strlen(salt); 645 | send_n(newfd,&dataLen,sizeof(int)); 646 | send_n(newfd,salt,dataLen); 647 | strcpy(pnode->pUserInfo->UserName,UserName); 648 | return 0; 649 | }//客户端获取到盐值 650 | //客户端登录第二步,输入密码 651 | else if('2'==workNum){ 652 | recv_n(newfd,&dataLen,sizeof(int)); 653 | recv_n(newfd,buf,dataLen); 654 | sprintf(sql,"select PassWord from UserInfo where UserName='%s'",pnode->pUserInfo->UserName); 655 | sqlSingleSelect(sql,buf1); 656 | if(strcmp(buf,buf1)==0){//验证成功 657 | ret=1; 658 | send_n(newfd,&ret,sizeof(int)); 659 | pnode->pUserInfo->absPath[strlen(pnode->pUserInfo->absPath)]='/'; 660 | strcat(pnode->pUserInfo->absPath,pnode->pUserInfo->UserName); 661 | pnode->pUserInfo->state=3;//状态改为3登录成功 662 | 663 | //生成token值,传给客户端 664 | tim=time(NULL); 665 | sprintf(buf,"%ld%s",tim,pnode->pUserInfo->UserName); 666 | pnode->pUserInfo->token = (char*)malloc(128); 667 | strcpy(pnode->pUserInfo->token,(char*)MD5((unsigned char*)buf,strlen(buf),NULL)); 668 | //pnode->pUserInfo->token=(char*)MD5((unsigned char*)buf,strlen(buf),NULL); 669 | dataLen=strlen(pnode->pUserInfo->token); 670 | send_n(newfd,&dataLen,sizeof(int)); 671 | send_n(newfd,pnode->pUserInfo->token,dataLen); 672 | tok[pnode->pUserInfo->token]=newfd; 673 | } 674 | else{//第一次登录失败 675 | ret=0; 676 | send_n(newfd,&ret,sizeof(int)); 677 | } 678 | return 0; 679 | } 680 | //第二次登录 681 | else if('3'==workNum){ 682 | recv_n(newfd,&dataLen,sizeof(int)); 683 | recv_n(newfd,buf,dataLen); 684 | sprintf(sql,"select PassWord from UserInfo where UserName='%s'",pnode->pUserInfo->UserName); 685 | sqlSingleSelect(sql,buf1); 686 | if(strcmp(buf,buf1)==0){//登录成功 687 | ret=1; 688 | send_n(newfd,&ret,sizeof(int)); 689 | pnode->pUserInfo->absPath[strlen(pnode->pUserInfo->absPath)]='/'; 690 | strcat(pnode->pUserInfo->absPath,pnode->pUserInfo->UserName); 691 | pnode->pUserInfo->state=3;//状态改为3登录成功 692 | 693 | //生成token值,传给客户端 694 | tim=time(NULL); 695 | sprintf(buf,"%ld%s",tim,pnode->pUserInfo->UserName); 696 | pnode->pUserInfo->token = (char*)malloc(64); 697 | strcpy(pnode->pUserInfo->token,(char*)MD5((unsigned char*)buf,strlen(buf),NULL)); 698 | //pnode->pUserInfo->token=(char*)MD5((unsigned char*)buf,strlen(buf),NULL); 699 | dataLen=strlen(pnode->pUserInfo->token); 700 | send_n(newfd,&dataLen,sizeof(int)); 701 | send_n(newfd,pnode->pUserInfo->token,dataLen); 702 | tok[pnode->pUserInfo->token]=newfd; 703 | return 0; 704 | } 705 | else{ 706 | ret=0; 707 | send_n(pnode->newfd,&ret,sizeof(int)); 708 | return -1; 709 | } 710 | } 711 | return -1; 712 | } 713 | 714 | //监督子线程 715 | void* supervise(void *p){ 716 | //每扫描一次将set里面有的fd关闭,表明30秒超时 717 | set::iterator it; 718 | int epfd=*(int*)p; 719 | struct epoll_event event; 720 | memset(&event,0,sizeof(event)); 721 | event.events=EPOLLIN; 722 | while(1){ 723 | sleep(1); 724 | pthread_mutex_lock(&setMutex); 725 | for(it=fdSet[fdPointer].begin();it!=fdSet[fdPointer].end();it++){ 726 | event.data.fd=*it; 727 | epoll_ctl(epfd,EPOLL_CTL_DEL,*it,&event); 728 | close(*it); 729 | free(mp[*it]); 730 | mp.erase(*it); 731 | } 732 | fdSet[fdPointer].clear(); 733 | fdPointer=(fdPointer+1)%(TimeOut+1); 734 | pthread_mutex_unlock(&setMutex); 735 | } 736 | } 737 | 738 | //绝对路径如/home/adam,最后一个文件夹后面无'/' 739 | int fileExist(char* absPath,char* fileName,char fileType){ 740 | DIR *dir=opendir(absPath); 741 | struct dirent *dnt; 742 | char buf[128]={0}; 743 | strcpy(buf,absPath); 744 | buf[strlen(buf)]='/'; 745 | int i=strlen(buf); 746 | struct stat sta; 747 | char type; 748 | while((dnt=readdir(dir))!=NULL){ 749 | buf[i]=0; 750 | strcat(buf,dnt->d_name); 751 | stat(buf,&sta); 752 | if(sta.st_mode>>12==4){//4是目录 753 | type='d'; 754 | } 755 | else if(sta.st_mode>>12==8){//8是文件 756 | type='-'; 757 | } 758 | if(strcmp(dnt->d_name,fileName)==0&&fileType==type){ 759 | closedir(dir); 760 | return 1; 761 | } 762 | } 763 | closedir(dir); 764 | return 0; 765 | } 766 | 767 | //将from中间的空格分开,前部分为操作数,后部分为操作对象 768 | void splitChar(char* from,char* operate,char* operand){ 769 | int fromLen=strlen(from); 770 | int i=0,j=0; 771 | while(from[i]==' '&&i