http://www.cnblogs.com/wunaozai/p/3891062.html
接上一小節,這次增加另外的兩張表,用于記錄用戶是保存那些文件。增加傳上來的文件的文件指紋,使用MD5表示。
兩張表如下定義:
1 create table files( 2 fid int, 3 filename varchar(64), 4 md5 varchar(64) 5 ); 6 7 create table relations( 8 uid int, 9 fid int 10 );
表與表之間的關系如下:
client.cpp 在上一小節基礎上增加了一個md5的功能,傳輸給服務器,用于作為文件的唯一標識
...
42 struct File 43 { 44 int uid; 45 char filename[64]; 46 char md5[64]; 47 }; 48 struct FileList 49 { 50 char list[1024]; 51 }; 52 53 void print_time(char *ch);//打印時間 54 int file_push(struct Addr addr,struct User user,char *filenames); 55 int check_login(struct Addr addr,struct User * user); 56 int md5sum(char *filename,unsigned char *md5); 57 int file_pull(struct Addr addr,struct User user,char *filenames); 58 59 60 int main(int argc,char *argv[]) 61 {... 131 return 0; 132 } 133 134 //驗證成功時返回大于0的uid號碼,錯誤返回-1 135 int check_login(struct Addr addr,struct User * user) 136 {...
186 } 187 188 int file_push(struct Addr addr,struct User user,char *filenames) 189 { ... ... 193 struct File file;//文件指紋 194 int sockfd; 195 FILE *fp; 196 char md5[64]; 197 unsigned char md5tmp[64]; 198 ... ...
220 //計算MD5 221 memset(md5,0,sizeof(md5)); 222 md5sum(filenames,md5tmp); 223 printf("計算得到的MD5:"); 224 for(int i=0;i<16;i++) 225 { 226 sprintf(&md5[i*2],"%02X",md5tmp[i]); 227 } 228 printf("%s\n",md5); 229 230 //打開文件 231 if((fp=fopen(filenames,"rb"))==NULL) 232 { 233 perror("文件打開失敗"); 234 exit(-1); 235 } 236 //這里傳輸控制信號 237 control.control=FILE_PUSH; 238 control.uid=user.uid; 239 if(send(sockfd,(char *)&control,sizeof(struct Control),0)<0) 240 { 241 perror("控制信號發送失敗"); 242 exit(-1); 243 } 244 //發送文件指紋 245 strcpy(file.filename,filenames); 246 strcpy(file.md5,md5); 247 file.uid=user.uid; 248 if(send(sockfd,(char *)&file,sizeof(struct File),0)<0) 249 { 250 perror("文件指紋發送失敗"); 251 exit(-1); 252 } 253 char buffer[BUFFER_SIZE]; 254 bzero(buffer,BUFFER_SIZE); 255 printf("正在傳輸文件"); 256 int len=0; 257 //不斷的讀取文件直到文件結束 258 while((len=fread(buffer,1,BUFFER_SIZE,fp))>0) 259 { 260 if(send(sockfd,buffer,len,0)<0) 261 { 262 perror("發送數據失敗"); 263 exit(-1); 264 } 265 bzero(buffer,BUFFER_SIZE); 266 printf(".");//1K打印一個點//如果要實現百分比,就要計算文件大小,然后再處理即可 267 } 268 269 printf("傳輸完畢\n"); 270 fclose(fp);//關閉文件流 271 close(sockfd);//關閉socket連接 272 273 return 0; 274 } 275 276 int md5sum(char *filename,unsigned char *md5) 277 { 278 MD5_CTX ctx; 279 char buffer[1024]; 280 unsigned char outmd[16]; 281 int len=0; 282 int i; 283 FILE *fp=NULL; 284 memset(outmd,0,sizeof(outmd)); 285 memset(buffer,0,sizeof(buffer)); 286 fp=fopen(filename,"rb"); 287 if(fp==NULL) 288 { 289 perror("打開文件失敗"); 290 } 291 MD5_Init(&ctx); 292 while((len=fread(buffer,1,sizeof(buffer),fp))>0) 293 { 294 MD5_Update(&ctx,buffer,len); 295 memset(buffer,0,sizeof(buffer)); 296 } 297 MD5_Final(outmd,&ctx); 298 for(i=0;i<16;i++) 299 { 300 md5[i]=outmd[i]; 301 } 302 fclose(fp); 303 return 0; 304 } 305
server.cpp?
... ...
40 struct File 41 { 42 int uid; 43 char filename[64]; 44 char md5[64]; 45 }; 46 struct FileList 47 { 48 char list[1024]; 49 }; 50 51 void print_time(char *ch);//打印時間 52 int MAX(int a,int b); 53 int mysql_check_login(struct User user); 54 int mysql_file_in(struct File file); 55 56 int mysql_get_max_fid() 57 { 58 MYSQL conn; 59 MYSQL_RES *res_ptr; 60 MYSQL_ROW result_row; 61 int res;int row;int column;int mfid; 62 char sql[256]; 63 strcpy(sql,"select max(fid) from files;"); 64 mfid=0; 65 mysql_init(&conn); 66 if(mysql_real_connect(&conn,"localhost","root","","filetranslate",0,NULL,CLIENT_FOUND_ROWS)) 67 { 68 res=mysql_query(&conn,sql); 69 if(res) 70 { 71 perror("SELECT SQL ERROR!"); 72 exit(-1); 73 } 74 else 75 { 76 res_ptr=mysql_store_result(&conn); 77 if(res_ptr) 78 { 79 column=mysql_num_fields(res_ptr); 80 row=mysql_num_rows(res_ptr)+1; 81 if(row<=1) 82 { 83 ;//沒有數據 84 } 85 else 86 { 87 result_row=mysql_fetch_row(res_ptr); 88 printf("最大的fid是:%s\n",result_row[0]); 89 if(result_row[0]==NULL) 90 mfid=0; 91 else 92 mfid=atoi(result_row[0]); 93 } 94 } 95 else 96 { 97 printf("沒有查詢到匹配的數據\n"); 98 } 99 } 100 } 101 else 102 { 103 perror("Connect Failed!"); 104 exit(-1); 105 } 106 mysql_close(&conn); 107 return mfid; 108 } 109 110 int mysql_file_in(struct File file) 111 { 112 int mfid; 113 MYSQL conn; 114 int res; 115 char sql[256]; 116 char tmp[32]; 117 mfid=mysql_get_max_fid()+1; 118 printf("獲取到的最大fid為:%d\n",mfid); 119 mysql_init(&conn); 120 if(mysql_real_connect(&conn,"localhost","root","","filetranslate",0,NULL,CLIENT_FOUND_ROWS)) 121 { 122 //insert into files values(mfid,file.filename,file.md5); 123 //insert into files values(file.uid,mfid); 124 memset(sql,0,sizeof(sql)); 125 strcpy(sql,"insert into files values("); 126 sprintf(tmp,"%d",mfid); 127 strcat(sql,tmp); 128 strcat(sql,",'"); 129 strcat(sql,file.filename); 130 strcat(sql,"','"); 131 strcat(sql,file.md5); 132 strcat(sql,"');"); 133 printf("插入的sql語句: %s\n",sql); 134 res=mysql_query(&conn,sql); 135 if(res) 136 printf("Insert Error!\n"); 137 else 138 printf("Insert Success!\n"); 139 140 memset(sql,0,sizeof(sql)); 141 strcpy(sql,"insert into relations values("); 142 sprintf(tmp,"%d",file.uid); 143 strcat(sql,tmp); 144 strcat(sql,","); 145 sprintf(tmp,"%d",mfid); 146 strcat(sql,tmp); 147 strcat(sql,");"); 148 printf("插入的sql語句: %s\n",sql); 149 res=mysql_query(&conn,sql); 150 if(res) 151 printf("Insert Error!\n"); 152 else 153 printf("Insert Success!\n"); 154 } 155 else 156 { 157 perror("Connect Failed!"); 158 exit(-1); 159 } 160 return 0; 161 } 162 163 164 int main(int argc,char *argv[]) 165 {... 201 while(1) 202 { 203 clientfd=accept(sockfd,(struct sockaddr *)&client_addr,&length); 204 if(clientfd==-1) 205 { 206 perror("accept 失敗"); 207 continue; 208 } 209 printf(">>>>>%s:%d 連接成功,當前所在的ID(fd)號: %d \n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),clientfd); 210 print_time(ch); 211 printf("加入的時間是:%s\n",ch); 212 213 //來一個連接就創建一個進程進行處理 214 pid=fork(); 215 if(pid<0) 216 { 217 perror("fork error"); 218 } 219 else if(pid==0) 220 { 221 recv(clientfd,(char *)&control,sizeof(struct Control),0); 222 printf("用戶 %d 使用命令 %d\n",control.uid,control.control); 223 switch(control.control) 224 { 225 case USER_CHECK_LOGIN: 226 { 227 //身份驗證處理... ...
239 break; 240 } 241 case FILE_PUSH: 242 { 243 char buffer[BUFFER_SIZE]; 244 int data_len; 245 FILE * fp=NULL; 246 struct File file; 247 //獲取文件指紋 248 recv(clientfd,(char *)&file,sizeof(struct File),0); 249 printf("獲取到的用戶名ID: %d 文件名:%s MD5:%s\n",file.uid,file.filename,file.md5); 250 bzero(buffer,BUFFER_SIZE); 251 if((fp=fopen("data","wb"))==NULL) 252 { 253 perror("文件打開失敗"); 254 exit(-1); 255 } 256 //循環接收數據 257 int size=0;//表示有多少個塊 258 while(data_len=recv(clientfd,buffer,BUFFER_SIZE,0)) 259 {... ...
278 } 279 if(size>0) 280 { 281 printf("\n%s:%d的文件傳送完畢\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port)); 282 //如果文件傳輸成功那么就可以寫入數據庫了 283 mysql_file_in(file); 284 } 285 else 286 printf("\n%s:%d的文件傳送失敗\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port)); 287 fclose(fp); 288 rename("data",file.md5);//這里可以修改文件的名字 289 exit(0); 290 break; 291 } 292 case FILE_PULL: 293 { 294 break; 295 } 296 case FILE_LIST: 297 { 298 break; 299 } 300 case FILE_DELECT: 301 { 302 break; 303 } 304 default: 305 { 306 break; 307 } 308 } 309 close(clientfd);//短連接結束 310 exit(0);//退出子進程 311 } 312 } 313 314 return 0; 315 } 316 317 318 //函數定義 319 int mysql_check_login(struct User user) 320 {... ...
377 } 378 ... ...
? 上面已經介紹了如何計算MD5值并寫入數據庫,接下來要做的事就是如何判斷要上傳的文件是否在服務器中已經存在,如果存在就可以不用上傳,而是在relations表中記錄,寫入uid-fid關系,這樣就可以實現網盤的秒傳功能了。而如何區分文件是否相同,我這里使用的是以MD5作為文件指紋。話說秒傳功能也不過如此吧。所以現在應該知道為什么有的文件可以秒傳,有的不可以了吧。關于具體的解釋可以參考杜鑫先生在知乎中的回到。傳送門在第一小節中有。
已加入驗證和秒傳功能的網盤程序
client.cpp修改如下
... ... 188 int file_push(struct Addr addr,struct User user,char *filenames) 189 {... ...
238 //發送文件指紋 239 strcpy(file.filename,filenames); 240 strcpy(file.md5,md5); 241 file.uid=user.uid; 242 if(send(sockfd,(char *)&file,sizeof(struct File),0)<0) 243 { 244 perror("文件指紋發送失敗"); 245 exit(-1); 246 } 247 char ch[64]; 248 if(recv(sockfd,ch,64,0)<0) 249 { 250 perror("error"); 251 } 252 if(ch[0]=='y')//表示已經存在 253 { 254 printf("該文件在服務器中存在,正使用秒傳功能。\n"); 255 printf("傳輸完畢\n"); 256 return 0; 257 } 258 //打開文件 259 if((fp=fopen(filenames,"rb"))==NULL) 260 { 261 perror("文件打開失敗"); 262 exit(-1); 263 } 264 char buffer[BUFFER_SIZE]; 265 bzero(buffer,BUFFER_SIZE); 266 printf("正在傳輸文件"); 267 int len=0; 268 //不斷的讀取文件直到文件結束 269 while((len=fread(buffer,1,BUFFER_SIZE,fp))>0) 270 { 271 if(send(sockfd,buffer,len,0)<0) 272 { 273 perror("發送數據失敗"); 274 exit(-1); 275 } 276 bzero(buffer,BUFFER_SIZE); 277 printf(".");//1K打印一個點//如果要實現百分比,就要計算文件大小,然后再處理即可 278 } 279 280 printf("傳輸完畢\n"); 281 fclose(fp);//關閉文件流 282 close(sockfd);//關閉socket連接 283 284 return 0; 285 } 286 ... ...
server.cpp修改如下
...... 61 int main(int argc,char *argv[]) 62 {...... 110 //來一個連接就創建一個進程進行處理 111 pid=fork(); 112 if(pid<0) 113 { 114 perror("fork error"); 115 } 116 else if(pid==0) 117 { 118 recv(clientfd,(char *)&control,sizeof(struct Control),0); 119 printf("用戶 %d 使用命令 %d\n",control.uid,control.control); 120 switch(control.control) 121 { 122 case USER_CHECK_LOGIN:... ...
138 case FILE_PUSH: 139 { 140 char buffer[BUFFER_SIZE]; 141 int data_len; 142 FILE * fp=NULL; 143 struct File file; 144 //獲取文件指紋 145 recv(clientfd,(char *)&file,sizeof(struct File),0); 146 printf("獲取到的用戶名ID: %d 文件名:%s MD5:%s\n",file.uid,file.filename,file.md5); 147 //對文件進行驗證,如果文件已經存在就不用進行接收了 148 int t=mysql_check_md5(file); 149 char ch[64]={0}; 150 printf("t=%d\n",t); 151 if(t!=0) 152 { 153 printf("該文件存在,使用秒傳功能\n"); 154 strcpy(ch,"yes"); 155 send(clientfd,ch,64,0); 156 mysql_file_in(file.uid,t); 157 continue; 158 } 159 strcpy(ch,"no"); 160 send(clientfd,ch,64,0); 161 printf("md5驗證后得到的fid:%d\n",t); 162 bzero(buffer,BUFFER_SIZE);... ... ...
202 break; 203 }... ...
220 } 221 close(clientfd);//短連接結束 222 exit(0);//退出子進程 223 } 224 } 225 226 return 0; 227 } 228 229 230 //函數定義 231 int mysql_check_md5(struct File file) 232 { 233 MYSQL conn; 234 MYSQL_RES * res_ptr; 235 MYSQL_ROW result_row; 236 int res;int row;int column;int value=0; 237 char sql[256]={0}; 238 strcpy(sql,"select fid from files where md5='"); 239 strcat(sql,file.md5); 240 strcat(sql,"';"); 241 printf("查詢的sql:%s\n",sql); 242 243 mysql_init(&conn); 244 if(mysql_real_connect(&conn,"localhost","root","","filetranslate",0,NULL,CLIENT_FOUND_ROWS)) 245 { 246 res=mysql_query(&conn,sql); 247 if(res) 248 { 249 perror("Select Sql Error!"); 250 exit(-1); 251 } 252 else 253 { 254 res_ptr=mysql_store_result(&conn); 255 if(res_ptr) 256 { 257 column=mysql_num_fields(res_ptr); 258 row=mysql_num_rows(res_ptr)+1; 259 if(row<=1) 260 { 261 ; 262 } 263 else 264 { 265 result_row=mysql_fetch_row(res_ptr); 266 value=atoi(result_row[0]); 267 } 268 } 269 else 270 { 271 printf("沒有查詢到匹配的數據\n"); 272 } 273 } 274 } 275 else 276 { 277 perror("Connect Failed!"); 278 exit(-1); 279 } 280 mysql_close(&conn); 281 return value;//返回fid 282 } 283 284 int mysql_file_in(int uid,int fid) 285 { 286 MYSQL conn; 287 int res; 288 char sql[256]; 289 char tmp[32]; 290 mysql_init(&conn); 291 if(mysql_real_connect(&conn,"localhost","root","","filetranslate",0,NULL,CLIENT_FOUND_ROWS)) 292 { 293 //insert into files values(uid,fid); 294 memset(sql,0,sizeof(sql)); 295 strcpy(sql,"insert into relations values("); 296 sprintf(tmp,"%d",uid); 297 strcat(sql,tmp); 298 strcat(sql,","); 299 sprintf(tmp,"%d",fid); 300 strcat(sql,tmp); 301 strcat(sql,");"); 302 printf("插入的sql語句: %s\n",sql); 303 res=mysql_query(&conn,sql); 304 if(res) 305 printf("Insert Error!\n"); 306 else 307 printf("Insert Success!\n"); 308 } 309 else 310 { 311 perror("Connect Failed!"); 312 exit(-1); 313 } 314 return 0; 315 } 316 ... ...
? 運行時的截圖
? 由上圖可以看出如果該文件在服務器中存在的話,那么下一次上傳同一個文件的話就會跳過上傳的步驟,而是把數據庫中的標識號給用戶uid即可。具體關系可以看下面數據庫數據。
好了,現在的上傳功能已經很完善了。下一節將實現下載功能了。
?
本文地址:?http://www.cnblogs.com/wunaozai/p/3891062.html