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

Socket网络编程--小小网盘程序(3)相关推荐

  1. Socket网络编程--小小网盘程序(5)

    http://www.cnblogs.com/wunaozai/p/3893469.html 各位好呀!这一小节应该就是这个小小网盘程序的最后一小节了,这一节将实现最后的三个功能,即列出用户在服务器中 ...

  2. Socket网络编程--小小网盘程序(4)

    在这一小节中实现了文件的下载,具体的思路是根据用户的uid和用户提供的文件名filename联合两张表,取得md5唯一标识符,然后操作这个标识符对应的文件发送给客户端. 实现下载的小小网盘程序 cli ...

  3. Socket网络编程--小小网盘程序(2)

    http://www.cnblogs.com/wunaozai/p/3887728.html 这一节将不会介绍太多的技术的问题,这节主要是搭建一个小小的框架,为了方便接下来的继续编写扩展程序.本次会在 ...

  4. Socket网络编程--小小网盘程序(1)

    http://www.cnblogs.com/wunaozai/p/3886588.html 这个系列是准备讲基于Linux Socket进行文件传输.简单的文件传输就是客户端可以上传文件,可以从服务 ...

  5. Linux C 网络编程 仿照网盘的功能

    Linux C 网络编程 仿照网盘的功能 代码: gitbub 代码,欢迎下载测试 使用概述 启动 server-> make;./server ../conf/serverconf.ini c ...

  6. 5.3linux下C语言socket网络编程简例

    原创文章,转载请注明转载字样和出处,谢谢! 这里给出在Linux下的简单socket网络编程的实例,使用tcp协议进行通信,服务端进行监听,在收到客户端的连接后,发送数据给客户端:客户端在接受到数据后 ...

  7. linux下C语言socket网络编程简例

    转自博文:http://blog.csdn.net/kikilizhm/article/details/7858405 在练习写网络编程时,该例给了我帮助,在写服务器时,我把while逻辑位置想法错了 ...

  8. Linux C++/Java/Web/OC Socket网络编程

    一,Linux C++ Socket网络编程 1.什么是TCP/IP.UDP? TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制 ...

  9. Python Socket网络编程(二)局域网内和局域网与广域网的持续通信

    目录 前言 IP地址 简介 公有IP 私有IP 局域网之间网络通信 前提 功能描述 源码 运行结果 局域网与广域网网络通信 前提 源码 结语 前言 本系列博客是笔者学习Python Socket的过程 ...

最新文章

  1. python 等号报错_Python学习----Python基础
  2. 第六届数学、计算机与生命科学交叉研究青年学者论坛 (大牛云集的生信会议,免注册费且提供午餐)...
  3. Java 地位不保,落后已成定局 ?| 10月编程语言排行
  4. Programming Principles and Practice Using C++ Notes1
  5. 前端学习(2884):dom更新操作的实现
  6. 阿里云智能财务软件好会计进销记账专用软件
  7. Android混淆从入门到精通
  8. keepalived + LVS实现高可用负载均衡集群
  9. Spring源码学习笔记:经典设计模式之观察者模式
  10. yii2 asset.php,Yii2中使用asset压缩js,css文件的方法_php实例
  11. velocity(vm)模板引擎学习介绍及语法
  12. 不定积分 基本积分表
  13. 黑金全部开发板资料(FPGA+ZYNQ)分享
  14. Kafka的消息可靠性(防止消息丢失)
  15. NOTA-NHS ester,1338231-09-6,双功能配体的大环化合物
  16. 智道分析吊瓜子的营养价值
  17. Java微信支付开发之查询订单
  18. 盲打打字php,盲打26键打字口诀是什么
  19. Qt tableWidget导入\导出Excel表格
  20. 司普沃浅谈豆角种植技术与管理方案

热门文章

  1. SHELL编程中如果路径名遇到括号
  2. css控制页面文字不能被选中user-select:none;
  3. Codeforces Round #318 (Div. 2) B Bear and Three Musketeers (暴力)
  4. 2006年2月8日 再见,Borland
  5. mysql什么实务_MysQL是什么类型的据库?
  6. suse10 linux安装,SuSE10.2 安装手记
  7. php5.5 session,(五)ThinkPHP实践之Session驱动-TTLSA
  8. react实现路由跳转_react实现hash路由
  9. ajax回复留言,Ajax 留言板模拟
  10. javaScript DOM编程常用的方法与属性