一个简单的IPmsg程序源码分析(二)

  离上篇一个简单的IPmsg程序源码分析(一)已经基本半个月(上篇最初发布在点点上面,后边纠结了一下还是选择了博客园),利用空闲的时间终于把源码的构架和一些细节基本都搞清楚了,总的来说是很简单的一个客户端。所以就只用这一篇文章全部说明吧。

  整个程序的核心是几个全局变量:

 1 typedef struct filenode filenode;
 2 
 3 typedef struct command
 4 {
 5   unsigned int version;
 6   unsigned int packetNo;
 7   char         senderName[NAMELEN];
 8   char         senderHost[NAMELEN];
 9   unsigned int commandNo;
10   char         additional[MSGLEN];
11   struct sockaddr_in peer;
12   filenode *   fileList;
13   struct command *next;
14 }command;
15 
16 struct filenode //文件序号:文件名:大小(单位:字节):最后修改时间:文件属性 [: 附加属性=val1[,val2…][:附加信息=…]]:\a文件序号…
17 {
18   int    tcpSock;
19   unsigned int    fileNo;
20   char   fileName[FILENAME];
21   char   fileSize[NAMELEN];
22   char   mtime[NAMELEN];
23   int    fileType;
24   char   otherAttrs[2*NAMELEN];
25   struct filenode* next;
26 };
27 
28 typedef struct gsNode
29 {
30   int tcpSock;
31   struct sockaddr_in peer;
32   unsigned int packetNo;
33   int     transferring;
34   int    cancelled;
35   char  *targetDir;
36   filenode fileList;
37   struct gsNode *next;
38 } gsNode;
39 
40 typedef struct msgList
41 {
42   command comHead;
43   command *comTail;
44 } msgList;
45 
46 
47 extern const char allHosts[]; //广播用地址
48 extern int msgSock; //消息
49 extern int tcpSock; //文件
50 extern struct passwd* pwd; 
51 extern struct utsname sysName;
52 extern char workDir[FILENAME];
53 extern int utf8; //系统的LC_CTYPE
54 
55 extern gsNode sendFHead, getFHead; //发送和接收文件列表
56 extern msgList mList; //接受到的消息列表
57 
58 extern pthread_mutex_t sendFMutex; //发送文件
59 extern pthread_mutex_t getFMutex;  //接收文件
60 extern pthread_mutex_t msgMutex;   //消息队列
61 extern pthread_mutex_t usrMutex;   //消息队列
62 extern sem_t waitNonEmpty, waitNonFull; //消息列表信号量

  最核心是下面这3个全局结构体变量,程序几个线程都基本都是在读取和写入这3个结构体:

1 extern gsNode sendFHead, getFHead; //发送和接收文件列表
2 extern msgList mList; //接受到的消息列表

  程序运行是有4个主要线程:

1 pthread_create(&procer, NULL, &processor, NULL); 
2   pthread_create(&recver, NULL, &receiver, &msgSock);
3   pthread_create(&iter, NULL, &interacter, NULL);
4   pthread_create(&cler, NULL, &cleaner, NULL);

  processor循环从mList中读取收到的消息,解析消息,并根据协议规定方式处理,如果是发送带有附件文件,则在getFHead中加入待接受文件的节点信息。

  receiver负责循环从msgSock套接字接受消息,然后把消息存入mList消息列表中。

  interacter线程是提供和用户的交互,供用户选择自己需要的操作,包裹:查看在线的主机、给在线的主机发送消息、发送文件,接受文件等(自动接受消息,在processor中会把接受到的消息和发消息的主机名直接输出到终端)。

  cleaner线程是清理sendFHead和getFHead中已经发送/接受完成的节点,释放内存空间,没半分钟执行一次。

 

  消息的接收和发送:

  接收:线程receiver循环从msgSock套接字接收并写入mList列表中,线程processor处理mList,把消息输出在终端上面。

  发送:线程interacter一直阻塞在fgets函数上面,等待用户的输入,当用户输入:talk或者tk(大小写无所谓,程序会自动把输入转换为小写)的时候,调用listUsers函数列出当前在线的主机,然后输入要发送消息的主机号,回车就是显示输入提示符:输入消息并回车就可以把消息发送给对方。

 

  文件(文件夹)的发送和接收:

  发送:感觉作者的文件发送方式很有意思(不过也可能是我读的代码少的原因),用户输入sf(sendfile)后,提示用户输入要发送的文件(夹),输入文件路径,确定,就会把文件发送给对方。在main函数中有下面一段代码:

 1 while(1)
 2   {
 3     if ((tmp=accept(tcpSock, NULL, NULL))<0)
 4       printf("Tcp accept error.\n");
 5     else
 6     {
 7       fSock = (int *)malloc(sizeof(int));
 8       *fSock = tmp;
 9       pthread_create(&fler, &attr, &sendData, fSock);
10     }
11     
12   }

  循环accept全局的tcpSock套接字,然后启动发送数据线程。发送进程从sendFHead列表中读取存放有需要发送的文件信息的节点。上边说的用户输入文件名字后是把信息写入到sendFHead里面就返回了。这样实现的好处是简单,不用考虑麻烦的通信还有线程同步问题。不过感觉这样扩展性不好(个人感觉)。还有一个ceaseSend(cs)命令用于取消传输,就是查看还没有传输成功的文件,shutdown相应传输线程的套接字。

  接收:processor读取到一条设置了 IPMSG_FILEATTACHOPT标志位的 IPMSG_SENDMSG命令时,说明对方发送了文件,文件信息在这条命令的附件部分,processor线程就把命令信息输出到终端,同时把待接受的文件信息写入getFHead列表中。用户要接受文件,就输入gf(getfile),interacter线程就调用recvfiles函数,此函数读取getFHead列表中待接收的文件信息并输出到界面,让用户选择需要接收的文件。然后创建getData线程接受文件(夹)(接收线程用的TCP套接字在getData线程里面创建)。作者没有实现拒绝接收选项,你不管的话(不 getfile)文件一直在那里等待接收,除非对方主动取消传输。

 

    以上就是这个程序的所有功能和实现方式,后边会陆续附上程序中主要函数代码及其详细的注释。

 代码及其注释(收发文件夹的部分没有分析注释,感觉作者实现的实在麻烦):

main
  1 /**********************************************************
  2  *Filename: main.c
  3  *Author:   星云鹏
  4  *Date:     2008-05-15
  5  *
  6  *主程序、各个线程
  7  *********************************************************/
  8 
  9 
 10 #include "users.h"
 11 #include "ipmsg.h"
 12 #include "send_receive.h"
 13 #include "coms.h"
 14 #include <langinfo.h>
 15 #include <locale.h>
 16 #include <pthread.h>
 17 
 18 extern void destroyer();
 19 
 20 //用户输入
 21 void* interacter(void* option)
 22 {
 23   char com[20], *fileName;
 24   int count;
 25 
 26   while(1)
 27   {
 28     printf("\n(ipmsg):");
 29     fgets(com, sizeof(com), stdin);
 30 
 31     transfStr(com, 1); //去掉开头结尾空白字符,变成全小写
 32 
 33     if (!strcmp(com, "list") ||
 34         !strcmp(com, "ls"))
 35     {
 36       pthread_mutex_lock(&usrMutex);
 37       count = listUsers(NULL, &userList, 2, 1);//只是列出在线的主机列表
 38       pthread_mutex_unlock(&usrMutex);
 39     }
 40     else if (!strcmp(com, "quit") ||
 41              !strcmp(com, "q"))
 42     {
 43       logout(); //下线,向广播地址发送IPMSG_BR_EXIT
 44       destroyer(); //清理数据
 45       exit(0);
 46     }
 47     else if (!strcmp(com, "refresh") ||
 48              !strcmp(com, "rf"))
 49     {
 50       login();//登录
 51       pthread_mutex_lock(&usrMutex);
 52       count = listUsers(NULL, &userList, 2, 1);//只是列出
 53       pthread_mutex_unlock(&usrMutex);
 54     }
 55     else if (!strcmp(com, "talk") ||
 56              !strcmp(com, "tk"))
 57     {
 58       saySth();    //向在线主机发送消息
 59     }
 60     else if (!strcmp(com, "sendfile") ||
 61              !strcmp(com, "sf"))
 62     {
 63       selectFiles();    //选择要发送的文件(夹)
 64     }
 65     else if (!strcmp(com, "getfile") ||
 66              !strcmp(com, "gf"))
 67     {
 68       recvFiles();    //接收文件(夹)
 69     }
 70     else if (!strcmp(com, "ceaseSend") ||
 71              !strcmp(com, "cs"))
 72     {
 73       ceaseSend();    //取消发送文件
 74     }
 75     else if (!strcmp(com, "help") ||
 76              !strcmp(com, "h"))
 77       printf(IMHELP);    //输出帮助信息
 78   }
 79 
 80 }
 81 
 82 //接收udp包
 83 void* receiver(void *option)
 84 {
 85   command *peercom;
 86   struct sockaddr_in peer;
 87   int mSock = *(int*)option, len;
 88   char buf[COMLEN];
 89   
 90   //从msgSock循环接收消息
 91   while(1)
 92   {
 93     if (recvfrom(mSock, buf, sizeof(buf), 0, (struct sockaddr*)&peer, &len)<0)    //接收
 94       continue;
 95     peercom = (command*)malloc(sizeof(command));
 96     bzero(peercom, sizeof(command));
 97     msgParser(buf, sizeof(buf), peercom);    //解析接收到的消息
 98     memcpy(&peercom->peer, &peer, sizeof(peercom->peer));
 99 
100     sem_wait(&waitNonFull);
101     pthread_mutex_lock(&msgMutex);
102 
103     //把解析好的命令加入mList列表
104     mList.comTail->next = peercom;
105     mList.comTail = peercom;
106 
107     sem_post(&waitNonEmpty); 
108     pthread_mutex_unlock(&msgMutex);
109   }
110 }
111 
112 //处理接收的udp包
113 void* processor(void *option)
114 {
115   command *peercom, com;
116   int comMode, comOpt, temp;
117   int len;
118   user *cur;
119   filenode *head, *curFile;
120   gsNode *preSend, *curSend, *curGet, *preGet;
121 
122   initCommand(&com, IPMSG_NOOPERATION);
123   while(1)
124   {
125     sem_wait(&waitNonEmpty);
126     pthread_mutex_lock(&msgMutex);
127    
128     //从mList列表中读取待处理的信息
129     peercom = mList.comHead.next;
130     mList.comHead.next = mList.comHead.next->next;
131     if (mList.comHead.next == NULL)
132       mList.comTail = &mList.comHead;
133     
134     sem_post(&waitNonFull); 
135     pthread_mutex_unlock(&msgMutex);
136     
137     memcpy(&com.peer, &peercom->peer, sizeof(com.peer));
138     
139     //得到信息中的命令模式和标识(协议规定,见ipmsg.h)
140     comMode = GET_MODE(peercom->commandNo);
141     comOpt = GET_OPT(peercom->commandNo);
142 
143     //如果设置了IPMSG_SENDCHECKOPT,就是给发送方发送回去确认收到信息
144     if (comOpt & IPMSG_SENDCHECKOPT)
145     {
146       com.packetNo = (unsigned int)time(NULL);
147       snprintf(com.additional, MSGLEN, "%d", peercom->packetNo);
148       com.commandNo = IPMSG_RECVMSG;
149       sendMsg(&com); //发送回应
150     }
151    
152 
153     //解析命令
154     switch (comMode)
155     {
156     case IPMSG_SENDMSG: //发送命令
157     
158         //如果命令包的扩展部分(消息内容在这个部分)不为空,输出消息发送方的名字和主机名,然后输出消息
159       if (strlen(peercom->additional)>0)
160       {
161         printf("\nGet message from: %s(%s)\n", peercom->senderName, peercom->senderHost);
162         puts(peercom->additional);
163       }
164 
165       //如果命令里面设置了IPMSG_FILEATTACHOPT(表示有发送文件请求)
166       if (comOpt & IPMSG_FILEATTACHOPT)
167       {
168         printf("\nGet Files from: %s(%s).\nInput \"getfile(gf)\" to download.\n",
169                peercom->senderName, peercom->senderHost);
170         //建立接收文件信息的新节点
171         curGet = (gsNode*)malloc(sizeof(gsNode));
172         initGsNode(curGet);
173         //把命令中所含信息填入节点
174         memcpy(&curGet->peer, &peercom->peer, sizeof(curGet->peer));
175         curGet->packetNo = peercom->packetNo;
176         curGet->fileList.next = peercom->fileList;
177         peercom->fileList = NULL; //
178         
179         //把新节点加入getFHead列表中
180         preGet = &getFHead;
181         pthread_mutex_lock(&getFMutex);
182         while ((preGet->next!=NULL) &&
183                (preGet->next->packetNo!=curGet->packetNo))
184           preGet = preGet->next;
185         
186         if (preGet->next==NULL)
187           preGet->next = curGet;
188         
189         pthread_mutex_unlock(&getFMutex);
190       }
191       
192       break;
193       
194       //通知新上线命令,把用户信息建立一个新节点加入userList列表里
195     case IPMSG_ANSENTRY: //
196       cur = (user*)malloc(sizeof(user));
197       memcpy(&cur->peer, &peercom->peer, sizeof(cur->peer));
198       strncpy(cur->name, peercom->senderName, NAMELEN);
199       strncpy(cur->host, peercom->senderHost, NAMELEN);
200       strncpy(cur->nickname, peercom->additional, NAMELEN);
201       cur->inUse = 0;
202       cur->exit = 0;
203       cur->next = NULL;
204       pthread_mutex_lock(&usrMutex); //lock
205       if (insertUser(&userList, cur)<0)
206         free(cur);
207       pthread_mutex_unlock(&usrMutex); //unlock
208       break;
209     case IPMSG_BR_ENTRY://处理同IPMSG_ANSENTRY,这个是第一次上线,要发送回应,即向对方发送IPMSG_ANSENTRY
210       com.packetNo = (unsigned int)time(NULL);
211       com.commandNo = IPMSG_ANSENTRY;//
212       strncpy(com.additional, pwd->pw_name, MSGLEN);
213       sendMsg(&com);
214 
215       cur = (user*)malloc(sizeof(user));
216       memcpy(&cur->peer, &peercom->peer, sizeof(cur->peer));
217       strncpy(cur->name, peercom->senderName, NAMELEN);
218       strncpy(cur->host, peercom->senderHost, NAMELEN);
219       strncpy(cur->nickname, peercom->additional, NAMELEN);
220       cur->inUse = 0;
221       cur->exit = 0;
222       cur->next = NULL;
223       pthread_mutex_lock(&usrMutex);
224       if (insertUser(&userList, cur)<0)
225         free(cur);
226       pthread_mutex_unlock(&usrMutex);
227       break;
228     case IPMSG_RECVMSG:
229       //
230       break;
231     case IPMSG_RELEASEFILES: //应该验证一下peer
232     //取消发送文件请求(在发送列表中找到节点,shutdown它的TCP套接字)
233       preSend = &sendFHead;
234       pthread_mutex_lock(&sendFMutex);
235       curSend = sendFHead.next;
236       while ((curSend!=NULL)&&(curSend->packetNo!=atoi(peercom->additional)))
237       {
238         preSend = preSend->next;
239         curSend = curSend->next;
240       }
241       
242       if (curSend!=NULL)
243       {
244         curSend->cancelled = 1;
245         if (curSend->tcpSock>0)
246           shutdown(curSend->tcpSock, SHUT_RDWR);
247       }
248       pthread_mutex_unlock(&sendFMutex);
249       break;
250     case IPMSG_BR_EXIT://下线通知。
251       pthread_mutex_lock(&usrMutex);
252       delUser(&userList, peercom);
253       pthread_mutex_unlock(&usrMutex);
254       break;
255     case IPMSG_NOOPERATION:
256       //
257       break;
258     default:
259       printf("\nno handle, %x\n", peercom->commandNo);
260       break;
261     }
262     deCommand(peercom);
263     free(peercom);
264     peercom = NULL;
265   }
266   
267 }
268 
269 //数据清理,清理已发送完成的文件信息节点,释放内存空间
270 void destroyer()
271 {
272   gsNode *preSend, *curSend, *preGet, *curGet;
273   filenode *curFile;
274   user *curUsr, *preUsr;
275   
276   preSend = &sendFHead;
277   pthread_mutex_lock(&sendFMutex);
278   curSend = sendFHead.next;
279   while (curSend!=NULL)
280   {
281     if ((curSend->cancelled == 1) && (curSend->transferring==0))
282     {
283       preSend->next = curSend->next;
284       deGsNode(curSend);
285       free(curSend);
286     }
287     else preSend = preSend->next;
288     
289     curSend = preSend->next;
290   }
291   pthread_mutex_unlock(&sendFMutex);
292   
293   
294   preGet = &getFHead;
295   pthread_mutex_lock(&getFMutex);
296   curGet = getFHead.next;
297   while (curGet!=NULL)
298   {
299     if ((curGet->cancelled==1) &&(curGet->transferring==0))
300     {
301       preGet->next = curGet->next;
302       deGsNode(curGet);
303       free(curGet);
304     }
305     else preGet = preGet->next;
306     
307     curGet = preGet->next;
308   }
309   pthread_mutex_unlock(&getFMutex);
310 
311   preUsr = &userList;
312   pthread_mutex_lock(&usrMutex);
313   curUsr = userList.next;
314   while (curUsr!=NULL)
315   {
316     if ((curUsr->exit==1) && (curUsr->inUse==0))
317     {
318       preUsr->next = curUsr->next;
319       free(curUsr);
320     }
321     else preUsr = preUsr->next;
322     
323     curUsr = preUsr->next;
324     
325   }
326   pthread_mutex_unlock(&usrMutex);
327 }
328 
329 
330 
331 void* cleaner(void *option)
332 {
333   gsNode *preSend, *curSend, *preGet, *curGet;
334   filenode *curFile;
335   user *curUsr, *preUsr;
336   
337   while(1)
338   {
339     sleep(30); //半分钟一次
340     destroyer();
341   }
342   
343 }
344 
345 //初始化udp和tcp
346 int initSvr()
347 {
348   struct sockaddr_in server;
349   char targetHost[NAMELEN];
350   const int on=1;
351 
352   msgSock = socket(AF_INET, SOCK_DGRAM, 0);  //UDP for Msg
353   
354   tcpSock = socket(AF_INET, SOCK_STREAM, 0); //TCP for File
355   
356   server.sin_family = AF_INET;
357   server.sin_port = htons(IPMSG_DEFAULT_PORT);
358   server.sin_addr.s_addr = htonl(INADDR_ANY);
359   
360   if (setsockopt(msgSock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))<0)
361   {
362     printf("initSvr: Socket options set error.\n");
363     exit(1);
364   }
365 
366   if (bind(msgSock, (struct sockaddr*)&server, sizeof(server))<0)
367   {
368     printf("initSvr: Udp socket bind error.\n");
369     exit(1);
370   }
371 
372   if (bind(tcpSock, (struct sockaddr*)&server, sizeof(server))<0)
373   {
374     printf("initSvr: Tcp socket bind error.\n");
375     exit(1);
376   }
377 
378   if (listen(tcpSock, 10)<0)
379   {
380     printf("initSvr: Tcp listen error.\n");
381     exit(1);
382   }
383   
384   //printf("Commands: list(ls) talk(tk) sendfile(sf)\n"
385   //"Commands: getfile(gf) refresh(rf) ceaseSend(cs) quit(q)\n");
386   printf(IMHELP);
387 }
388 
389 int main (int argc, char *argv [])
390 {
391   pthread_t procer, recver, iter, fler, cler;
392   int *fSock;
393   int tmp;
394   pthread_attr_t attr;
395   
396   uname(&sysName);    //得到系统名
397   pwd = getpwuid(getuid());        //得到用户登录信息
398   getcwd(workDir, sizeof(workDir));        //得到用户当前工作目录
399 
400   utf8 = 0;
401   if (setlocale(LC_CTYPE, ""))
402     if (!strcmp(nl_langinfo(CODESET), "UTF-8"))        //设置系统字符集
403       utf8 = 1;
404   
405   initGsNode(&sendFHead);        //初始化文件发送队列
406   initCommand(&mList.comHead, IPMSG_NOOPERATION);        //初始化消息发送队列
407   mList.comTail = &mList.comHead;        
408   userList.next = NULL;        //初始化用户列表
409   sem_init(&waitNonEmpty, 0, 0);
410   sem_init(&waitNonFull, 0, MSGLIMIT);
411 
412   initSvr();    //初始化套接字
413   
414   //主要线程的建立
415   pthread_create(&procer, NULL, &processor, &msgSock); 
416   pthread_create(&recver, NULL, &receiver, &msgSock);
417   pthread_create(&iter, NULL, &interacter, NULL);
418   pthread_create(&cler, NULL, &cleaner, NULL);
419   login();    //登录,向广播地址发送IPMSG_BR_ENTRY命令
420 
421   //发送文件线程的建立
422   pthread_attr_init(&attr);
423   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
424   while(1)
425   {
426     if ((tmp=accept(tcpSock, NULL, NULL))<0)
427       printf("Tcp accept error.\n");
428     else
429     {
430       fSock = (int *)malloc(sizeof(int));
431       *fSock = tmp;
432       pthread_create(&fler, &attr, &sendData, fSock);
433     }
434     
435   }
436 
437   //同步线程
438   pthread_join(procer, NULL);
439   pthread_join(recver, NULL);
440   pthread_join(iter, NULL);
441   pthread_join(cler, NULL);
442   return 0;
443 }
send_receive
   1 /**********************************************************
   2  *Filename: send_receive.c
   3  *Author:   星云鹏
   4  *Date:     2008-05-15
   5  *
   6  *消息、文件(夹)的发送和接收
   7  *********************************************************/
   8 
   9 #include "send_receive.h"
  10 #include "ipmsg.h"
  11 #include "users.h"
  12 #include "coms.h"
  13 
  14 #include <sys/utsname.h>
  15 #include <string.h>
  16 #include <sys/stat.h>
  17 #include <sys/types.h>
  18 #include <dirent.h>
  19 #include <errno.h>
  20 #include <pwd.h>
  21 #include <sys/utsname.h>
  22 #include <signal.h>
  23 #include <pthread.h>
  24 
  25 
  26 //////////////////////////////////////////////////
  27 //发送udp包
  28 void* sendMsg(command* com)
  29 {
  30   char buf[COMLEN];
  31   int len, temp;
  32   
  33   msgCreater(buf, com, sizeof(buf)); //建立发送的包(包装com结构体为字符串)
  34   len = strlen(buf);
  35   if (com->commandNo & IPMSG_FILEATTACHOPT) //如果带有附件(发送文件请求)
  36       temp = strlen(buf+len+1);
  37       
  38       sendto(msgSock, buf, len+temp+2, 0,
  39              (struct sockaddr*)&com->peer, sizeof(com->peer));
  40      }
  41      //发送普通信息
  42   else sendto(msgSock, buf, len+1, 0,
  43               (struct sockaddr*)&com->peer, sizeof(com->peer));
  44 }
  45 
  46 //////////////////////////////////////////////////
  47 //聊天
  48 int saySth()
  49 {
  50   char buf[100]; //临时输入区
  51   char gMsg[MSGLEN];
  52   command com;
  53   user *cur=NULL;
  54   int remainder; //扩展区剩余空间
  55   int pos;       //扩展区的当前输入位置
  56   int who, temp, count;
  57   int sended;      //标记此包是否已经发过
  58   user *pusers[50];
  59 
  60   //列出并且选择要聊天的用户
  61   //列出并且选择要聊天的用户
  62   printf("\n*Talking mode\n"
  63          "*Continuous Enter to send msg,\n"
  64          "*Ctrl+D to quit conversation.\n");
  65   
  66   pthread_mutex_lock(&usrMutex);
  67   count = listUsers(pusers, &userList, sizeof(pusers)/sizeof(pusers[0]), 0);
  68   pthread_mutex_unlock(&usrMutex);
  69   
  70   who = inputNo(1, count, 1, "Please input user No.[1]:");
  71   
  72   if (who>0)
  73   {
  74     cur = pusers[who-1];
  75     //初始化消息命令
  76     initCommand(&com, IPMSG_SENDMSG|IPMSG_SENDCHECKOPT);
  77     memcpy(&com.peer, &cur->peer, sizeof(com.peer));
  78     
  79     remainder = MSGLEN;
  80     pos = 0;
  81     sended = 1;
  82     
  83     while(1)
  84     {
  85       printf("(talking with %s):", cur->name);
  86       if (fgets(buf, sizeof(buf), stdin)==NULL) //等待用户输入消息
  87         break;
  88       if (buf[0] != '\n')
  89       {
  90       //把消息放入发送命令的扩展区
  91         strncpy(com.additional+pos, buf, remainder);
  92         temp = strlen(com.additional+pos);
  93         pos += temp;
  94         remainder -= temp;
  95         sended = 0;
  96       }
  97 
  98       if ((buf[0]=='\n') || (remainder<=1))
  99       {
 100         if (!sended) //消息填写完成,调用sendMsg函数发送包含消息的UDP包
 101         {
 102           com.packetNo = (unsigned int)time(NULL);
 103           sendMsg(&com);
 104           sended = 1;
 105           printf("Message sent.\n");
 106         }
 107         remainder = sizeof(com.additional); //恢复扩展区大小
 108         pos = 0;
 109       }
 110       
 111     }
 112   }
 113   
 114     puts("\nEnd conversation.\n");
 115     
 116     pthread_mutex_lock(&usrMutex);
 117     unListUsers(pusers, count);
 118     pthread_mutex_unlock(&usrMutex);
 119   
 120 }
 121 
 122 /////////////////////////////////////////////////
 123 //文件发送
 124 //选择要传输的文件
 125 
 126 int selectFiles()
 127 {
 128   command com;
 129   user *cur=NULL;
 130   int  who, count, flag, fileType;
 131   unsigned int fileNo;
 132   char fileName[FILENAME];
 133   struct stat fileAttr;
 134   char *strtmp;
 135   filenode *fntmp, *head, *tail;
 136   gsNode *newTask;
 137   user *pusers[50];
 138   
 139   printf("\n*Sending mode\n"
 140          "*Continuous Enter to send file,\n"
 141          "*Ctrl+D to quit.\n"); //输出提示
 142   
 143   pthread_mutex_lock(&usrMutex);
 144   count = listUsers(pusers, &userList, sizeof(pusers)/sizeof(pusers[0]), 0); //列出用户列表
 145   pthread_mutex_unlock(&usrMutex);
 146 
 147   //选择要发送的用户,默认第一个
 148   who = inputNo(1, count, 1, "Please input user No.[1]:");
 149 
 150   if (who>0)
 151   {
 152     
 153     cur = pusers[who-1];
 154   //初始化命令,设置IPMSG_SENDMSG|IPMSG_FILEATTACHOPT用于表示有文件发送
 155     initCommand(&com, IPMSG_SENDMSG|IPMSG_FILEATTACHOPT);
 156     memcpy(&com.peer, &cur->peer, sizeof(com.peer));
 157     
 158     printf("To send file to %s(%s).\nPlease select file to send:\n", cur->name, cur->host);
 159     
 160     //建立发送文件信息节点
 161     newTask = (gsNode*)malloc(sizeof(gsNode));
 162     initGsNode(newTask);
 163     newTask->packetNo = com.packetNo;
 164     
 165     fileNo = 0;
 166     head = com.fileList;
 167     tail = com.fileList;
 168     
 169     while (1)
 170     {
 171       if (fgets(fileName, FILENAME, stdin) == NULL) //等待用户输入需要发送的文件(夹)名字。C+d取消发送
 172       {
 173         free(newTask);
 174         newTask = NULL;
 175         while (head!=NULL)
 176         {
 177           tail = head;
 178           head = head->next;
 179           free(tail);
 180         }
 181         break;
 182       }
 183 
 184        transfStr(fileName, 0); //去除前后空白字符
 185        
 186       if (fileName[0]=='\0')
 187         break;
 188       
 189       //读取文件名的信息,写入struct stat结构体
 190      if (lstat(fileName, &fileAttr)<0)
 191       {
 192         printf("Get file attributes error.\n");
 193         continue;
 194       }
 195 
 196       //确定文件类型,是文件还是文件夹
 197       if (S_ISREG(fileAttr.st_mode))
 198         fileType = 1;
 199       else if (S_ISDIR(fileAttr.st_mode))
 200         fileType = 2;
 201       else
 202       {
 203         fileType = -1;
 204         printf("Unsupported file type.\n");
 205         continue;
 206       }
 207       
 208       //建立filenode(文件信息)节点
 209       if (tail == NULL)
 210         head = tail = (filenode*)malloc(sizeof(filenode));
 211       else
 212       {
 213         tail->next = (filenode*)malloc(sizeof(filenode));
 214         tail = tail->next;
 215       }
 216       
 217       //把上边得到的struct stat文件信息填入filenode
 218       tail->next = NULL;
 219       tail->fileNo = fileNo;
 220       strncpy(tail->fileName, fileName, sizeof(tail->fileName));
 221       snprintf(tail->fileSize, sizeof(tail->fileSize), "%x", fileAttr.st_size);
 222       snprintf(tail->mtime, sizeof(tail->mtime), "%x", fileAttr.st_mtime);
 223       tail->fileType = fileType;
 224 
 225       fileNo++;
 226     }
 227     
 228     if (head==NULL)
 229     {
 230       if (newTask!=NULL)
 231         free(newTask);
 232     }
 233     else 
 234     {
 235     //初始化newTask的filenode成员
 236       newTask->fileList.next = com.fileList = head;
 237       pthread_mutex_lock(&sendFMutex); //lock
 238       newTask->next = sendFHead.next; //把newTask加入sendFHead列表
 239       sendFHead.next = newTask;
 240       pthread_mutex_unlock(&sendFMutex); //unlock
 241 
 242       if (newTask->fileList.next!=NULL)
 243       {
 244         sendMsg(&com); //可以放lock外面,发送信息给对方
 245         printf("\nWaiting to transfer.\n");
 246       }
 247     }
 248   }
 249 
 250   pthread_mutex_lock(&usrMutex);
 251   unListUsers(pusers, count);
 252   pthread_mutex_unlock(&usrMutex);
 253   
 254 }
 255 
 256 //文件或文件夹发送
 257 void* sendData(void* option)
 258 {
 259   int fSock = *(int *)option;
 260   char buf[RECFRG], fileName[FILENAME];
 261   int i, commandNo, realCount, offset, curErr;
 262   unsigned int packetNo, fileNo;
 263   filenode *preFile, *curFile;
 264   gsNode *preSend, *curSend;
 265   FILE* sfile;
 266   sigset_t mask, oldmask;
 267 
 268   free(option);
 269   
 270   sigemptyset(&mask);
 271   sigaddset(&mask, SIGPIPE);
 272   if (pthread_sigmask(SIG_BLOCK, &mask, &oldmask) != 0)
 273     printf("SIG_BLOCK error.\n");
 274   
 275   //以tcp的方式接受传输请求
 276   for (i=0;i<4;i++)
 277   {
 278     if (readDelimiter(fSock, buf, RECFRG, ':')<=0)
 279     {
 280       printf("Transfer cancelled.\n");
 281       shutdown(fSock, SHUT_RDWR);
 282       return NULL;
 283     }  
 284   }
 285 
 286   if (readDelimiter(fSock, buf, RECFRG, ':')<=0)
 287   {
 288     printf("Transfer cancelled.\n");
 289     shutdown(fSock, SHUT_RDWR);
 290     return NULL;
 291   }
 292   
 293   commandNo = atoi(buf);
 294 
 295   if (!(commandNo & IPMSG_GETFILEDATA))
 296   {
 297     printf("Invalid request.\n");
 298     shutdown(fSock, SHUT_RDWR);
 299     return NULL;
 300   }
 301   
 302   
 303   if (readDelimiter(fSock, buf, RECFRG, ':')<=0)
 304   {
 305     printf("Transfer cancelled.\n");
 306     shutdown(fSock, SHUT_RDWR);
 307     return NULL;
 308   }
 309   
 310   sscanf(buf, "%x", &packetNo);
 311 
 312   if (readDelimiter(fSock, buf, RECFRG, ':')<0)
 313   {
 314     printf("Transfer cancelled.\n");
 315     shutdown(fSock, SHUT_RDWR);
 316     return NULL;
 317   }
 318   sscanf(buf, "%x", &fileNo);
 319 
 320   pthread_mutex_lock(&sendFMutex);
 321   
 322   preSend = &sendFHead;
 323   curSend = sendFHead.next;
 324 
 325   while ((curSend != NULL) &&
 326          (curSend->packetNo!=packetNo || curSend->transferring==1 ||
 327          curSend->cancelled==1)){
 328     preSend = preSend->next;
 329     curSend = curSend->next;
 330   }
 331   
 332   if (curSend != NULL)
 333   {
 334     curSend->transferring = 1;
 335     curSend->tcpSock = fSock;
 336     pthread_mutex_unlock(&sendFMutex);
 337     
 338     curFile = curSend->fileList.next;
 339     preFile = &curSend->fileList;
 340     while ((curFile!=NULL) && (curFile->fileNo!=fileNo))
 341     {
 342       preFile = preFile->next;
 343       curFile = curFile->next;
 344     }
 345     
 346     if (curFile != NULL)
 347     {
 348       getFileName(fileName, curFile->fileName, sizeof(fileName));
 349       printf("\nStart transferring %s.\n", fileName);
 350       switch (curFile->fileType)
 351       {
 352       case 1: //发送文件
 353         if (readDelimiter(fSock, buf, RECFRG, ':')<=0) //offset似乎没用上
 354         {
 355           printf("Transfer cancelled.\n");
 356           shutdown(fSock, SHUT_RDWR);
 357           return NULL;
 358         }
 359         sscanf(buf, "%x", &offset);
 360         
 361         sfile = fopen(curFile->fileName, "r");
 362         
 363         while ((realCount = fread(buf, 1, RECFRG, sfile))>0)
 364           if (writen(fSock, buf, realCount)<0)
 365           {
 366             curErr = errno;
 367             break;
 368           }
 369         break;
 370       case 2: //发送文件夹
 371         curErr = traverseDir(fSock, curFile->fileName, sendDir); 
 372         break;
 373       default:
 374         break;
 375       }
 376     }
 377   }
 378   else pthread_mutex_unlock(&sendFMutex);
 379 
 380   pthread_mutex_lock(&sendFMutex);
 381   if ((curSend!=NULL) && (curSend->cancelled==1))
 382   {
 383     preSend->next = curSend->next;
 384     deGsNode(curSend);
 385     free(curSend);
 386     pthread_mutex_unlock(&sendFMutex);
 387     shutdown(fSock, SHUT_RDWR);
 388     printf("Transfer canceled.\n");
 389     return NULL;
 390   }
 391   
 392   if ((curErr<0) || (errno == ECONNRESET) || (errno == EPIPE)) //error or connection reset by peer
 393   {
 394     if (curFile!=NULL)
 395     {
 396       curSend->transferring = 0;
 397       curSend->tcpSock = -1;
 398     }
 399     pthread_mutex_unlock(&sendFMutex);
 400     shutdown(fSock, SHUT_RDWR);
 401     printf("Peer needs retransfer.\n");
 402     return NULL;
 403   }
 404   
 405   if (curFile!=NULL)
 406   {
 407     printf("\n%s is transferred.\n", fileName);
 408     preFile->next = curFile->next;
 409     free(curFile);
 410   }
 411   
 412   
 413   if (curSend!=NULL && curSend->fileList.next == NULL)
 414   {
 415     preSend->next = curSend->next;
 416     deGsNode(curSend);
 417     free(curSend);
 418   }
 419   else if (curSend!=NULL)
 420   {
 421     curSend->transferring = 0;
 422     curSend->tcpSock = -1;
 423   }
 424   pthread_mutex_unlock(&sendFMutex);
 425   
 426   shutdown(fSock, SHUT_RDWR);
 427 }
 428 
 429 //发送文件夹
 430 
 431 int sendDir(int fSock, const char* fullpath, int fileSize, int fileType)
 432 {
 433   char strtmp[FILENAME], strformat[50], strdata[RECFRG], fileName[FILENAME];
 434   int tmp, headLen, packetNo;
 435   FILE *sf;
 436 
 437   if (getFileName(strtmp, fullpath, sizeof(strtmp)) < 0)
 438   {
 439     printf("\nFilename is too long.\n");
 440     return -1;
 441   }
 442 
 443   addColon(strtmp, sizeof(strtmp));
 444   if (utf8)
 445     u2g(strtmp, sizeof(strtmp),
 446         fileName, sizeof(fileName));
 447   else strncpy(fileName, strtmp, sizeof(fileName));
 448   
 449   headLen = (strlen(fileName)+1) + (HL_HEADERSIZE+1) +
 450     (HL_FILETYPE+1) + (HL_FILESIZE+1) + 2*(HL_1416+1);
 451   packetNo = (unsigned int)time(NULL); //简化了,属性值并不准确
 452   snprintf(strformat, sizeof(strformat), "%%0%dx:%%s:%%0%dx:%%0%dx:14=%%0%dx:16=%%0%dx:",
 453            HL_HEADERSIZE, HL_FILESIZE, HL_FILETYPE, HL_1416-3, HL_1416-3);
 454   
 455   tmp = snprintf(strdata, sizeof(strdata), strformat,
 456            headLen, fileName, fileSize, fileType, packetNo, packetNo);
 457   
 458   switch (fileType)
 459   {
 460   case 1:
 461     if ((sf = fopen(fullpath, "r")) == NULL)
 462     {
 463       printf("file open error.\n");
 464       return -1;
 465     }
 466     if (writen(fSock, strdata, tmp)<0)
 467       return -1;
 468     while ((tmp = fread(strdata, 1, RECFRG, sf))>0)
 469     {
 470       if (writen(fSock, strdata, tmp)<0)
 471         return -1;
 472     }
 473     fclose(sf);
 474     break;
 475   case 2:
 476    
 477     //break;
 478   case 3:
 479     if (writen(fSock, strdata, tmp)<0)
 480       return -1;
 481     break;
 482   default:
 483     break;
 484   }
 485   return 0;
 486 }
 487 
 488 //遍历要发送的文件夹,利用上面的回掉函数发送
 489 int traverseDir(int fSock, char* fullpath, Mysnd snd) //FILENAME指定了fullpath的容量
 490 {
 491   struct stat fst;
 492   struct dirent *dirp;
 493   char *ptr;
 494   DIR  *dp;
 495   int tmp;
 496   
 497 
 498   if (lstat(fullpath, &fst)<0)
 499   {
 500     printf("\nDir: get attributes error.\n");
 501     return -1;
 502   }
 503   
 504   if (S_ISREG(fst.st_mode))
 505     return snd(fSock, fullpath, fst.st_size, 1);
 506   else if (S_ISDIR(fst.st_mode))
 507   {
 508     if (snd(fSock, fullpath, 0, 2)<0)
 509       return -1;
 510   }
 511   else return -1;
 512 
 513   tmp = strlen(fullpath);
 514   ptr = fullpath + tmp;
 515   
 516   *ptr++ = '/'; //tmp+1
 517   *ptr = '\0';
 518 
 519   if ((dp=opendir(fullpath)) == NULL)
 520   {
 521     printf("\nDir: open error.\n");
 522     return -1;
 523   }
 524 
 525   while ((dirp=readdir(dp)) != NULL)
 526   {
 527     if (strcmp(dirp->d_name, ".")==0 ||
 528         strcmp(dirp->d_name, "..")==0)
 529       continue;
 530 
 531     strncpy(ptr, dirp->d_name, FILENAME-tmp-1);
 532     if (traverseDir(fSock, fullpath, snd)<0)
 533       return -1;
 534   }
 535 
 536   ptr[-1] = '\0'; //还原fullname
 537   snd(fSock, ".", 0, 3);
 538   if (closedir(dp) < 0)
 539   {
 540     printf("\nDir: close error.\n");
 541     return -1;
 542   }
 543   return 0;
 544 }
 545 
 546 //列举待发送文件
 547 int listSFiles(gsNode **list, gsNode *gs, int size)
 548 {
 549   filenode *curFile;
 550   int tmp=0;
 551 
 552   while ((gs!=NULL)&&(tmp<size))
 553   {
 554     if ((gs->cancelled==0))
 555     {
 556       *list++ = gs;
 557       tmp++;
 558     }
 559     gs = gs->next;
 560   }
 561   
 562   return tmp;
 563   
 564 }
 565 
 566 //取消文件传输
 567 int ceaseSend()
 568 {
 569   gsNode *gsList[CAPACITY], *cur;
 570   int tmp, index, count;
 571   char buf[FILENAME];
 572   filenode *curFile;
 573 
 574   while(1)
 575   {
 576     pthread_mutex_lock(&sendFMutex);
 577     count = listSFiles(gsList, sendFHead.next, sizeof(gsList)/sizeof(gsList[0])-1);
 578     pthread_mutex_unlock(&sendFMutex);
 579     
 580     if (count == 0)
 581       return 0;
 582     else
 583     {
 584       printf("\n++++++++++++++++++++\n");
 585       printf("Files to send:\n");
 586       for(tmp=1;tmp<=count;tmp++)
 587       {
 588         printf("%d. ", tmp);
 589         curFile = (*(gsList+tmp-1))->fileList.next;
 590         while (curFile!=NULL)
 591         {
 592           printf("%s ", curFile->fileName);
 593           curFile = curFile->next;
 594         }
 595         printf("\n");
 596       }
 597       printf("++++++++++++++++++++\n");
 598     }
 599 
 600     index = inputNo(1, count, 1,
 601                     "Which file(s) to cancel?(Ctrl+D to quit)\nNumber[1]:");
 602 
 603     if (index<0)
 604       return -1;
 605 
 606     pthread_mutex_lock(&sendFMutex);
 607     (*(gsList+index-1))->cancelled = 1;
 608     if ((*(gsList+index-1))->tcpSock>=0)
 609       shutdown((*(gsList+index-1))->tcpSock, SHUT_RDWR);
 610     pthread_mutex_unlock(&sendFMutex);
 611   }
 612 }
 613 
 614 
 615 ////////////////////////////////////////////////////
 616 //文件接收
 617 //选择要接收的文件
 618 int recvFiles()
 619 {
 620   gsNode *gsList[CAPACITY], *cur;
 621   int tmp, index, count;
 622   char buf[FILENAME];
 623   struct stat dirAttr;
 624   pthread_t gFile;
 625   filenode *curFile;
 626   pthread_attr_t attr;
 627   
 628   while(1)
 629   {//访问getFHead列表,列出等待接受的文件信息
 630     pthread_mutex_lock(&getFMutex);
 631     //把待接收文件信息节点存入gsList
 632     count = listGFiles(gsList, getFHead.next, sizeof(gsList)/sizeof(gsList[0])-1);
 633     pthread_mutex_unlock(&getFMutex);
 634     
 635     if (count == 0)
 636       return -1;
 637     else
 638     {//待接受文件名和编号
 639       printf("\n++++++++++++++++++++\n");
 640       printf("Files to get:\n");
 641       for(tmp=1;tmp<=count;tmp++)
 642       {
 643         printf("%d. ", tmp);
 644         curFile = (*(gsList+tmp-1))->fileList.next;
 645         while (curFile!=NULL)
 646         {
 647           printf("%s ", curFile->fileName);
 648           curFile = curFile->next;
 649         }
 650         printf("\n");
 651       }
 652       printf("++++++++++++++++++++\n");
 653     }
 654     //选择需要接收的文件,默认第一个。
 655     index = inputNo(1, count, 1,
 656                     "Which file(s) to get?(Ctrl+D to quit)\nNumber[1]:");
 657 
 658     if (index<0)
 659       return -1;
 660     
 661     while(1)
 662     {
 663       printf("Where do you want to save?(Ctrl+D to quit)\nTarget dir[.]:");
 664       if (fgets(buf, sizeof(buf), stdin)==NULL) //输入保存路径,默认当前目录
 665         return -1;
 666 
 667       transfStr(buf, 0); //去除前后空白字符
 668       
 669       if (buf[0]=='\0')
 670       {
 671         buf[0]='.';
 672         buf[1]='\0';
 673       }
 674     //检查存储路径是否合法
 675       if ((stat(buf, &dirAttr)<0) ||
 676           !S_ISDIR(dirAttr.st_mode) ||
 677           (access(buf, W_OK)<0))
 678         printf("Invalid directory. Please input again.\n");
 679       else break;
 680     }
 681     
 682     cur = *(gsList+index-1);
 683     tmp = strlen(buf);
 684     pthread_mutex_lock(&getFMutex);
 685     if (cur->cancelled == 0)
 686     {//设置节点信息
 687       cur->targetDir = (char*)malloc(tmp+1);
 688       strncpy(cur->targetDir, buf, tmp+1);
 689       cur->transferring = 1;
 690     }
 691     //启动getData线程,开始接收文件
 692     pthread_mutex_unlock(&getFMutex);
 693     pthread_attr_init(&attr);
 694     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 695     pthread_create(&gFile, &attr, &getData, cur);
 696   }
 697 
 698 }
 699 
 700 //文件接收线程
 701 void* getData(void* option)
 702 {
 703   gsNode *gList = (gsNode *)option;
 704   filenode *head=&gList->fileList, *cur;
 705   int fileType, tmp;
 706   command com;
 707 
 708   printf("\nStart getting files.\n");
 709   while((cur=head->next) != NULL)
 710   {//查看文件类型(文件还是文件夹),用不同的函数接收
 711     fileType = cur->fileType & 0x03;
 712     switch (fileType)
 713     {
 714     case 1:
 715       tmp=getFile(cur, gList);//接收文件
 716       break;
 717     case 2:
 718       tmp=getDir(cur, gList);//接收文件夹
 719       break;
 720     default:
 721       tmp = -1;
 722       printf("\nUnsupported file type.\n%s fails to be transfered.\n", cur->fileName);
 723       break;
 724     }
 725    //接收错误(出现了套接字建立错误,链接错误,传输错误等) 
 726     if (tmp<0)
 727     {
 728       initCommand(&com, IPMSG_RELEASEFILES); //设置取消传输
 729       snprintf(com.additional, sizeof(com.additional), "%d", gList->packetNo);
 730       memcpy(&com.peer, &gList->peer, sizeof(com.peer));
 731       sendMsg(&com);
 732       pthread_mutex_lock(&getFMutex);
 733       //设置结构体成员标识参数
 734       gList->cancelled = 1;
 735       gList->transferring = 0;
 736       pthread_mutex_unlock(&getFMutex);
 737       printf("\n%s fails to be transfered.\n", cur->fileName);
 738       return NULL;
 739     }
 740     
 741     printf("\n%s is transferred.\n", cur->fileName);
 742     head->next = cur->next;
 743     free(cur);
 744   }
 745 
 746   pthread_mutex_lock(&getFMutex);
 747   gList->cancelled = 1; //其实是已经完成,成功后设置传输成功。
 748   gList->transferring = 0;
 749   pthread_mutex_unlock(&getFMutex);
 750   
 751   printf("File reception done.\n");
 752 }
 753 
 754 //接收文件
 755 int getFile(void* option, gsNode *gList)
 756 {
 757   int sockfd, readBytes;
 758   char buf[COMLEN];
 759   char recvs[RECFRG];
 760   filenode *head = (filenode*)option;
 761   command com;
 762   struct sockaddr_in peer;
 763   long fileSize, offset=0;
 764   FILE* recvFile;
 765     //初始化文件接收命令
 766   initCommand(&com, IPMSG_GETFILEDATA);  //Regular file
 767   snprintf(com.additional, MSGLEN, "%x:%x:%x", gList->packetNo, head->fileNo, offset);
 768   //建立TCP套接字
 769   sockfd = socket(AF_INET, SOCK_STREAM, 0);
 770 
 771   bzero(&peer, sizeof(peer));
 772   peer.sin_family = AF_INET;
 773   peer.sin_port = htons(IPMSG_DEFAULT_PORT);
 774   memcpy(&peer.sin_addr, &gList->peer.sin_addr, sizeof(peer.sin_addr));
 775   //连接
 776   if (connect(sockfd, (struct sockaddr*)&peer, sizeof(peer))<0)
 777   {
 778     printf("File connect error.\n");
 779     return -1;
 780   }
 781   
 782   msgCreater(buf, &com, sizeof(buf));//创建发送消息
 783 
 784   if (writen(sockfd, buf, strlen(buf)+1)<0)    //接收文件命令消息发送给对方
 785     return -1;
 786 
 787   sscanf(head->fileSize, "%x", &fileSize);
 788 
 789   if ((recvFile = fopen(head->fileName, "w+")) == NULL) //建立文件描述符
 790   {
 791     printf("getFile: file create error.\n");
 792     return -1;
 793   }
 794 
 795   while (fileSize>0)
 796   {
 797   //从套接字接收文件内容
 798     readBytes = fileSize < RECFRG ? fileSize:RECFRG;
 799     if ((readBytes = readn(sockfd, recvs, readBytes))<0) //RECFRG
 800     {
 801       printf("getFile: data transfer error.\n");
 802       return -1;
 803     }
 804     //接收到的内容写入文件描述符
 805     fwrite(recvs, 1, readBytes, recvFile);
 806     fileSize -= readBytes;
 807   }
 808   fclose(recvFile); //关闭文件描述符
 809   
 810   close(sockfd); //关闭套接字
 811   return 0;
 812 }
 813 
 814 //接收文件夹时分析,头部
 815 int parseHeader(filenode *pfn, char * recvs)
 816 {
 817   char *strhead, *strtmp, gMsg[FILENAME];
 818   int i=0;
 819   
 820   strhead = recvs;
 821   while (i<3) //分析前3段
 822   {
 823     strtmp = strchr(strhead, ':');
 824     if (strtmp == NULL)
 825       return -1;
 826     *strtmp = '\0';
 827 
 828     switch (i)
 829     {
 830     case 0:
 831       
 832       strncpy(gMsg, strhead, sizeof(gMsg));
 833       delColon(gMsg, sizeof(gMsg));
 834       if (utf8)
 835         g2u(gMsg, sizeof(gMsg),
 836             pfn->fileName, sizeof(pfn->fileName));
 837       else strncpy(pfn->fileName, gMsg, sizeof(pfn->fileName));
 838       break;
 839     case 1:
 840       //sscanf(strhead, "%x", &pfn->fileSize);
 841       strncpy(pfn->fileSize, strhead, sizeof(pfn->fileSize));
 842       break;
 843     case 2:
 844       sscanf(strhead, "%x", &pfn->fileType);
 845       break;
 846       //case 3: //进行了简化
 847       // strncpy(pfh->h14, strhead+3, sizeof(pfh->h14));
 848       //break;
 849       //case 4:
 850       //strncpy(pfh->h16, strhead+3, sizeof(pfh->h16));
 851       //break;
 852     default:
 853       break;
 854     }
 855 
 856     strhead = strtmp+1;
 857     i++;
 858   }
 859 
 860   strncpy(pfn->otherAttrs, strhead, sizeof(pfn->otherAttrs));
 861   
 862   return 0;
 863 }
 864 
 865 //接收文件夹
 866 int getDir(void *option, gsNode *gList)
 867 {
 868   int sockfd, readBytes, headerSize, fileType, dirDepth=0, tmp;
 869   char buf[COMLEN], recvs[RECFRG], hSize[HSIZE], fullPath[2*FILENAME];
 870   filenode *head = (filenode*)option, *cur, fn;
 871   command com;
 872   struct sockaddr_in peer;
 873   long  offset=0, fileSize;
 874   FILE* recvFile;
 875 
 876   strncpy(fullPath, gList->targetDir, sizeof(fullPath)); //文件路径写入fullPath
 877   
 878   initCommand(&com, IPMSG_GETDIRFILES); //Direcotry
 879   
 880   snprintf(com.additional, MSGLEN, "%x:%x:%x", gList->packetNo, head->fileNo, offset);
 881   
 882   sockfd = socket(AF_INET, SOCK_STREAM, 0);
 883 
 884   bzero(&peer, sizeof(peer));
 885   peer.sin_family = AF_INET;
 886   peer.sin_port = htons(IPMSG_DEFAULT_PORT);
 887   memcpy(&peer.sin_addr, &gList->peer.sin_addr, sizeof(peer.sin_addr));
 888   
 889   if (connect(sockfd, (struct sockaddr*)&peer, sizeof(peer))<0)
 890   {
 891     printf("File connect error.\n");
 892     return -1;
 893   }
 894 
 895   msgCreater(buf, &com, sizeof(buf));
 896   
 897   if (writen(sockfd, buf, strlen(buf)+1)<0)
 898     return -1;
 899   //以上处理同getfile
 900   do
 901   {
 902     //检验路径名是否合法
 903     tmp = strlen(fullPath);
 904     if (tmp+1>=sizeof(fullPath))
 905     {
 906       printf("getDir: target directory is too lang.\n");
 907       return -1;
 908     }
 909     if (fullPath[tmp-1]!='/')
 910     {
 911       fullPath[tmp] = '/';
 912       fullPath[tmp+1] = '\0';
 913     }
 914   
 915     readBytes = readDelimiter(sockfd, hSize, HSIZE, ':'); //读取定界符(定界符见协议),读遇到的以一个分界符之前的内容
 916     sscanf(hSize, "%x", &headerSize);//把字符串长度写入headerSize
 917     if (headerSize<=0 || headerSize<=readBytes || headerSize>RECFRG) //保护、防止溢出
 918       return -1;
 919     readn(sockfd, recvs, headerSize-readBytes);//读取
 920     recvs[headerSize-readBytes]='\0';
 921     
 922     if (parseHeader(&fn, recvs)<0) //分析接收到的内容。
 923     {
 924       printf("getDir: Parse protocol failed.\n");
 925       return -1;
 926     }
 927     
 928     switch (fn.fileType & 0x03) //分析文件类型
 929     {
 930     case IPMSG_FILE_REGULAR:
 931 
 932       strncat(fullPath, fn.fileName, sizeof(fullPath)-tmp-1);
 933       if ((recvFile = fopen(fullPath, "w+")) == NULL)
 934       {
 935         printf("Open error.\n");
 936         return -1;
 937       }
 938       
 939       sscanf(fn.fileSize, "%x", &fileSize);
 940       while (fileSize>0)
 941       {
 942         readBytes = fileSize < RECFRG ? fileSize:RECFRG;
 943         if ((readBytes = readn(sockfd, recvs, readBytes))<0) //RECFRG
 944         {
 945           printf("File read error.\n");
 946           return -1;
 947         }
 948         
 949         fwrite(recvs, 1, readBytes, recvFile);
 950         fileSize -= readBytes;
 951       }
 952       fclose(recvFile);
 953       getParentPath(fullPath, sizeof(fullPath));
 954       break;
 955       
 956     case IPMSG_FILE_DIR:
 957       strncat(fullPath, fn.fileName, sizeof(fullPath)-tmp-1);
 958       tmp = strlen(fullPath);
 959       if (tmp+1>=sizeof(fullPath))
 960       {
 961         printf("getDir: target directory is too lang.\n"
 962                "Filename(%s) has been truncated.\n", fn.fileName);
 963         fullPath[tmp-1] = '/';
 964       }
 965       else
 966       {
 967         fullPath[tmp] = '/';
 968         fullPath[tmp+1] = '\0';
 969       }
 970       
 971       if (mkdir(fullPath, S_IRUSR|S_IWUSR|S_IXUSR))
 972       {
 973         printf("getDir: mkdir failed.\n");
 974         return -1;
 975       }
 976       
 977       dirDepth++;
 978       break;
 979       
 980     case IPMSG_FILE_RETPARENT:
 981       getParentPath(fullPath, sizeof(fullPath));
 982       dirDepth--;
 983       break;
 984       
 985     default:
 986       printf("Unsupported file type.\n");
 987       break;
 988     }
 989     
 990   }while(dirDepth>0);
 991 
 992   //close(sockfd);
 993   shutdown(sockfd, SHUT_RDWR);
 994   return 0;
 995 }
 996 
 997 //列举待接收文件
 998 int listGFiles(gsNode **list, gsNode *gs, int size)
 999 {
1000   filenode *curFile;
1001   int tmp=0;
1002 
1003   while ((gs!=NULL)&&(tmp<size))
1004   {
1005     if ((gs->transferring==0)&&(gs->cancelled==0))
1006     {
1007       *list++ = gs;
1008       tmp++;
1009     }
1010     gs = gs->next;
1011   }
1012   
1013   return tmp;
1014   
1015 }
1016 
1017 
1018 /////////////////////////////////////////////////
1019 //登录(上线)
1020 int login()
1021 {
1022   command com;
1023 
1024   initCommand(&com, IPMSG_BR_ENTRY);
1025   
1026   com.peer.sin_family = AF_INET;
1027   com.peer.sin_port = htons(IPMSG_DEFAULT_PORT);
1028   
1029   if (inet_pton(AF_INET, allHosts, &com.peer.sin_addr)<0)
1030     printf("login: Ip error.\n");
1031   
1032   strncpy(com.additional, pwd->pw_name, MSGLEN);
1033   
1034   sendMsg(&com);
1035   
1036 }
1037 
1038 //退出(下线)
1039 int logout()
1040 {
1041   command com;
1042 
1043   initCommand(&com, IPMSG_BR_EXIT);
1044   
1045   com.peer.sin_family = AF_INET;
1046   com.peer.sin_port = htons(IPMSG_DEFAULT_PORT);
1047   
1048   if (inet_pton(AF_INET, allHosts, &com.peer.sin_addr)<0)
1049     printf("logout: error\n");
1050 
1051   strncpy(com.additional, pwd->pw_name, MSGLEN);
1052   
1053   sendMsg(&com);
1054   printf("Bye!\n");
1055   
1056 }

 

 

posted @ 2012-11-15 10:39  風之痕  阅读(1127)  评论(0编辑  收藏  举报