zoukankan      html  css  js  c++  java
  • 一个简单的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 }

     

  • 相关阅读:
    CSS-常用hack
    CSS触发haslayout的方法
    CSS最大最小宽高兼容
    CSS-文字超出自动显示省略号
    [LeetCode][JavaScript]Number of Islands
    [LeetCode][JavaScript]Search a 2D Matrix II
    [LeetCode][JavaScript]Search a 2D Matrix
    [LeetCode][JavaScript]Candy
    [LeetCode][JavaScript]Wildcard Matching
    [LeetCode][JavaScript]Sliding Window Maximum
  • 原文地址:https://www.cnblogs.com/Franck/p/2770485.html
Copyright © 2011-2022 走看看