一个简单的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)文件一直在那里等待接收,除非对方主动取消传输。
以上就是这个程序的所有功能和实现方式,后边会陆续附上程序中主要函数代码及其详细的注释。
代码及其注释(收发文件夹的部分没有分析注释,感觉作者实现的实在麻烦):
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 }
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 }