client
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <netinet/in.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <linux/if.h> #include <arpa/inet.h> #include <openssl/md5.h> #define TCP_SERV_PORT 2360 //设置TCP服务器监听端口号 #define UDP_SERV_PORT 2370 //设置UDPP服务器监听端口号 #define MCAST_PORT 2380 //发现侦听端口 #define SIZE 128 //设置缓冲区长度 #define IP_LEN 20 //设置存储IP地址的长度 #define IFNAME "ens33" //设置网卡名 int connect_TCP (char IP_ADDR[IP_LEN]); //声明TCP连接函数 int connect_UDP (char IP_ADDR[IP_LEN]); //声明UDP连接函数 void tcp_send_file(int client_cend_sockfd, char filename[SIZE]); //声明TCP文件发送函数 void udp_send_file(int client_cend_sockfd, char filename[SIZE]); //声明UDP文件发送函数 void tcp_recv_file(int client_recv_sockfd); //声明tcp接收文件函数 void udp_recv_file(int client_recv_sockfd); //声明udp接收文件函数 void IPFound(); //声明探测IP函数 char close_serer_flag[5] = "no"; //定义是否关闭客户端标志。 struct sockaddr_in servaddr; //服务器的IP地址和端口号信息 unsigned int addrlen = sizeof(struct sockaddr_in); int main(int argc, char **argv) { int func_type; //定义传输功能,上传还是下载 char IP_ADDR[IP_LEN]; //存储IP地址 int trans_type; //定义传输类型,TCP还是UDP int sockfd; //定义socket描述符 char c_send_file_path[SIZE]; //客户端发送文件路径 if(2 != argc) { printf("参数错误:请运行时选择连接TCP还是UDP。\n"); exit(1); } if(0 == strcmp("TCP", argv[1])) { trans_type = 1; } else if(0 == strcmp("UDP", argv[1])) { trans_type = 2; } else { printf("请在运行时输入正确的参数!"); exit(1); } printf("Hello! 欢迎使用本系统,正在探测服务器。\n\n"); IPFound(); printf("\n请输入IP连接服务器:\n"); scanf("%s", IP_ADDR); switch (trans_type) { case 1: //TCP sockfd = connect_TCP(IP_ADDR); //连接TCP printf("TCP连接成功!\n"); printf("请选择文件上传还是下载:\t1、上传\t2、下载\n"); scanf("%d", &func_type); switch (func_type) { case 1: //上传 bzero(c_send_file_path, sizeof(c_send_file_path)); printf("请输入当前目录下的完整文件名:\n"); scanf("%s", c_send_file_path); tcp_send_file(sockfd, c_send_file_path); //发送数据 close(sockfd); //关闭套接字 printf("文件发送成功!是否关闭服务器?(yes/no)\n"); scanf("%s", close_serer_flag); if(0 == strcmp(close_serer_flag, "yes")) { //sockfd = connect_UDP(IP_ADDR); //连接UDP //sendto(sockfd, "CLOSE", SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器关闭UDP线程 sockfd = connect_TCP(IP_ADDR); //连接TCP send(sockfd, "CLOSE", SIZE, 0); //通知服务器关闭TCP线程 close(sockfd); //关闭套接字 } else if(0 == strcmp(close_serer_flag, "no")) { } else { printf("输入错误!退出客户端!\n"); } break; case 2: //下载 tcp_recv_file(sockfd); //接收数据 close(sockfd); //关闭套接字 printf("文件下载完成!是否关闭服务器?(yes/no)\n"); scanf("%s", close_serer_flag); if(0 == strcmp(close_serer_flag, "yes")) { //sockfd = connect_UDP(IP_ADDR); //连接UDP //sendto(sockfd, "CLOSE", SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器关闭UDP线程 sockfd = connect_TCP(IP_ADDR); //连接TCP send(sockfd, "CLOSE", SIZE, 0); //通知服务器关闭TCP线程 close(sockfd); //关闭套接字 } else if(0 == strcmp(close_serer_flag, "no")) { } else { printf("输入错误!退出客户端!\n"); } break; default: printf("请输入 1 或 2\n"); break; } break; case 2: //UDP sockfd = connect_UDP(IP_ADDR); //连接UDP printf("UDP连接成功!\n"); printf("请选择文件上传还是下载:\t1、上传\t2、下载\n"); scanf("%d", &func_type); switch (func_type) { case 1: //上传 bzero(c_send_file_path, sizeof(c_send_file_path)); printf("请输入当前目录下的完整文件名:\n"); scanf("%s", c_send_file_path); udp_send_file(sockfd, c_send_file_path); //发送数据 close(sockfd); //关闭套接字 printf("文件发送成功!是否关闭服务器?(yes/no)\n"); scanf("%s", close_serer_flag); if(0 == strcmp(close_serer_flag, "yes")) { sockfd = connect_UDP(IP_ADDR); //连接UDP sendto(sockfd, "CLOSE", SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器关闭UDP线程 close(sockfd); //关闭套接字 //sockfd = connect_TCP(IP_ADDR); //连接TCP //send(sockfd, "CLOSE", SIZE, 0); //通知服务器关闭TCP线程 } else if(0 == strcmp(close_serer_flag, "no")) { } else { printf("输入错误!退出客户端!\n"); } break; case 2: //下载 udp_recv_file(sockfd); //接收数据 close(sockfd); //关闭套接字 printf("文件下载完成!是否关闭服务器?(yes/no)\n"); scanf("%s", close_serer_flag); if(0 == strcmp(close_serer_flag, "yes")) { sockfd = connect_UDP(IP_ADDR); //连接UDP sendto(sockfd, "CLOSE", SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器关闭UDP线程 close(sockfd); //关闭套接字 //sockfd = connect_TCP(IP_ADDR); //连接TCP //send(sockfd, "CLOSE", SIZE, 0); //通知服务器关闭TCP线程 } else if(0 == strcmp(close_serer_flag, "no")) { } else { printf("输入错误!退出客户端!\n"); } break; default: printf("请输入 1 或 2\n"); break; } break; default: break; } return 0; } int connect_TCP (char IP_ADDR[IP_LEN]) { int tcp_sockfd; //定义TCP socket描述符 tcp_sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建套接字 if (-1 == tcp_sockfd) //如果创建套接字失败,则输出错误信息并退出 { perror("创建套接字失败!\n"); exit(1); } //向服务器发出连接请求 servaddr.sin_family = AF_INET; //TCP/IP协议 servaddr.sin_port = htons(TCP_SERV_PORT); //将服务器端口号转换为网络字节顺序 servaddr.sin_addr.s_addr = inet_addr(IP_ADDR); //设置IP地址 bzero(&(servaddr.sin_zero), 8); //清零 int res = connect(tcp_sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)); //建立连接 if(-1 == res) //如果连接失败,则输出错误信息并退出 { perror("连接失败!\n"); exit(1); } else { //printf("连接成功!\n"); } return tcp_sockfd; } int connect_UDP (char IP_ADDR[IP_LEN]) { int udp_sockfd; //定义UDP socket描述符 udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建套接字 if (-1 == udp_sockfd) //如果创建套接字失败,则输出错误信息并退出 { perror("创建套接字失败!\n"); exit(1); } servaddr.sin_family = AF_INET; //TCP/IP协议 servaddr.sin_port = htons(UDP_SERV_PORT); //将服务器端口号转换为网络字节顺序 servaddr.sin_addr.s_addr = inet_addr(IP_ADDR); //服务器IP地址 bzero(&(servaddr.sin_zero), 8); //清零 return udp_sockfd; } void tcp_send_file(int client_cend_sockfd, char filename[SIZE]) { int nread; struct stat st; char buf[SIZE]; //定义缓冲区 unsigned char md5[SIZE]; //用来存储md5值 MD5_CTX CtxLocal; unsigned char Md5Temp[16]; //存储md5原始值 int fq = open(filename, O_RDONLY); //打开文件并获取文件描述符 if( fq < 0 ) { perror("打开文件失败!"); exit(1); } MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fq, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; close(fq); send(client_cend_sockfd, "UPLOAD", SIZE, 0); //通知服务器为上传文件 send(client_cend_sockfd, filename, SIZE, 0); //发送文件名 send(client_cend_sockfd, md5, SIZE, 0); //发送MD5值 fq = open(filename, O_RDONLY); //再打开一次文件,上次打开的被读尽了。 if( fq < 0 ) { perror("打开文件失败!"); exit(1); } stat(filename,&st); //获取文件大小 int len = st.st_size; if(sendfile(client_cend_sockfd,fq,0,len) < 0) //发送文件并判断是否成功 { perror("发送失败!"); exit(1); } close(fq); return; } void udp_send_file(int client_cend_sockfd, char filename[SIZE]) { int nread; MD5_CTX CtxLocal; unsigned char Md5Temp[16]; char *buffer; //定义缓冲区 unsigned char md5[SIZE]; //用来存储md5值 unsigned char md5_server[SIZE]; //用来服务器传来的md5值 buffer = (char *)malloc(sizeof(char) *SIZE); bzero(buffer, SIZE); int fileTrans; int fq = open(filename, O_RDONLY); //打开文件并获取文件描述符 if( -1 == fq) { perror("打开文件失败!\n"); exit(1); } MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fq, buffer, sizeof(buffer))) > 0) { MD5_Update(&CtxLocal, buffer, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; close(fq); sendto(client_cend_sockfd, "UPLOAD", SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器为上传文件 sendto(client_cend_sockfd, filename, SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //发送文件名 fq = open(filename, O_RDONLY); //再打开一次文件,上次打开的被读尽了。 if( fq < 0 ) { perror("打开文件失败!"); exit(1); } while((fileTrans = read(fq, buffer, SIZE)) > 0) //将文件信息存入缓存区 { sleep(0.001); if(-1 == sendto(client_cend_sockfd, buffer, fileTrans, 0, (struct sockaddr *) &servaddr, addrlen)) //发送文件并判断是否成功 { perror("发送失败!\n"); exit(1); } if(fileTrans < SIZE) break; bzero(buffer, SIZE); } close(fq); //关闭文件 sendto(client_cend_sockfd, "", 0, 0, (struct sockaddr *) &servaddr, addrlen); //给服务器发送空字节,告知传输完成 recvfrom(client_cend_sockfd, md5_server, SIZE, 0, (struct sockaddr *) &servaddr, &addrlen); //接收服务器返回来的MD5值 if(0 != strcmp(md5, md5_server)) { printf("文件校验失败!请重新传输!\n"); exit(1); } return; } void tcp_recv_file(int client_recv_sockfd) { int nread; MD5_CTX CtxLocal; unsigned char Md5Temp[16]; unsigned char md5[SIZE]; //用来存储md5值 unsigned char md5_server[SIZE]; //用来服务器传来的md5值 char file_exist[10]; //定义数组判断要下载的文件在服务器是否存在 char recv_file_name[SIZE]; char buf[SIZE]; send(client_recv_sockfd, "DOWNLOAD", SIZE, 0); //通知服务器为下载文件 bzero(recv_file_name, sizeof(recv_file_name)); printf("请输入需要下载的文件:\n"); scanf("%s", recv_file_name); send(client_recv_sockfd, recv_file_name, SIZE, 0); //通知服务器需要下载的文件名。 recv(client_recv_sockfd, file_exist, 10, 0); //从服务器接收文件是否存在 if(0 == strcmp(file_exist, "NOT_EXIST")) { printf("服务器不存在此文件!\n"); exit(1); } recv(client_recv_sockfd, md5_server, SIZE,0); // 接收服务器端的MD5值 int fp = open(recv_file_name, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU); //创建新文件 while(1) { int cnt = recv(client_recv_sockfd, buf, SIZE,0); if(cnt < 0) { printf( "文件下载失败!\n"); exit(1); } if(cnt != 0) { write(fp, buf,cnt); bzero(buf,sizeof(buf)); } else { //printf("文件下载成功!\n"); break; } } close(fp); fp = open(recv_file_name, O_RDONLY); //打开文件并获取文件描述符 MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fp, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; if(0 != strcmp(md5, md5_server)) { printf("文件校验失败!请重新传输!"); exit(1); } close(fp); return; } void udp_recv_file(int client_recv_sockfd) { int nread; MD5_CTX CtxLocal; unsigned char Md5Temp[16]; unsigned char md5[SIZE]; //用来存储md5值 unsigned char md5_serer[SIZE]; //用来服务器传来的md5值 int cnt; char buf[SIZE]; //定义缓冲区 char file_exist[10]; //定义数组判断要下载的文件在服务器是否存在 char recv_file_name[SIZE]; sendto(client_recv_sockfd, "DOWNLOAD", SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器为下载文件 bzero(recv_file_name, sizeof(recv_file_name)); printf("请输入需要下载的文件:\n"); scanf("%s", recv_file_name); sendto(client_recv_sockfd, recv_file_name, SIZE, 0, (struct sockaddr *) &servaddr, addrlen); //通知服务器需要下载的文件名。 recvfrom(client_recv_sockfd, file_exist, 10, 0, (struct sockaddr *) &servaddr, &addrlen); if(0 == strcmp(file_exist, "NOT_EXIST")) { printf("服务器不存在此文件!\n"); exit(1); } int fp = open(recv_file_name, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU); //创建新文件 while(1) { cnt = recvfrom(client_recv_sockfd, buf, SIZE, 0, (struct sockaddr *) &servaddr, &addrlen); if(-1 == cnt) { printf( "文件下载失败!\n"); exit(1); } write(fp, buf, cnt); bzero(buf, sizeof(buf)); if(0 == cnt) { //printf("文件下载成功!\n"); break; } } close(fp); recvfrom(client_recv_sockfd, md5_serer, SIZE, 0, (struct sockaddr *) &servaddr, &addrlen); fp = open(recv_file_name, O_RDONLY); //打开文件并获取文件描述符 MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fp, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; close(fp); if(0 != strcmp(md5, md5_serer)) { printf("文件校验失败!请重新传输!"); } return; } void IPFound() { #define BUFFER_LEN 32 char IP_FOUND[BUFFER_LEN] = "IP_FOUND"; char IP_FOUND_ACK[BUFFER_LEN] = "IP_FOUND_ACK"; int ret = -1; int sock = -1; int so_broadcast = 1; struct ifreq ifr; struct sockaddr_in broadcast_addr; //本机地址 struct sockaddr_in from_addr; //服务器端地址 int from_len = sizeof(struct sockaddr); int count = -1; fd_set readfd; char buff[BUFFER_LEN]; struct timeval timeout; timeout.tv_sec = 2; //超时时间2s timeout.tv_usec = 0; sock = socket(AF_INET, SOCK_DGRAM, 0); //建立数据报套接字 if(sock < 0) { printf("建立套接字失败!\n"); return; } setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); //设置套接字超时 strcpy(ifr.ifr_name, IFNAME); //将需要使用的网络接口字符串名字复制到结构中。 if(-1 == ioctl(sock, SIOCGIFBRDADDR, &ifr)) //发送命令获取网络接口的IP地址 { perror("ioctl 失败!"); exit(1); } memcpy(&broadcast_addr, &ifr.ifr_broadaddr, sizeof(struct sockaddr_in)); //将获得的广播地址复制给变量broadcast_addr broadcast_addr.sin_port = htons(MCAST_PORT); //设置广播端口 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast)); //设置套接字文件描述符sock为可以进行广播操作 ret = sendto(sock, IP_FOUND, strlen(IP_FOUND), 0, (struct sockaddr*) &broadcast_addr, sizeof(broadcast_addr)); //广播发送服务器地址请求 if(-1 == ret) { printf("广播发送请求失败!\n"); } FD_ZERO(&readfd); //清空文件描述符集合 FD_SET(sock, &readfd); //将套接字描述符加入读集合 ret = select(sock+1, &readfd, NULL, NULL, &timeout); //select侦听是否有数据到来 switch(ret) { case -1: printf("侦听错误!\n"); exit(1); //发生错误 case 0: printf("超时!未检测到服务器!\n"); exit(1); default: //有数据到来 if(FD_ISSET(sock, &readfd)) { while(1) //持续侦听多个服务器发来的信息,直到超时 { bzero(buff, sizeof(buff)); count = recvfrom(sock, buff, BUFFER_LEN, 0, (struct sockaddr*) &from_addr, &from_len); if(count > -1) { if(strstr(buff, IP_FOUND_ACK)) //判断是否吻合 { //flag = 0; printf("IP地址:%s\n", inet_ntoa(from_addr.sin_addr)); } } else { break; } } } } return; }
server
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <arpa/inet.h> #include <pthread.h> #include <fcntl.h> #include <netinet/in.h> #include <openssl/md5.h> #define TCP_SERV_PORT 2360 //设置服务器监听端口号 #define UDP_SERV_PORT 2370 //设置UDPP服务器监听端口号 #define MCAST_PORT 2380 ////发现侦听端口 #define LENGTH 10 //请求队列的长度数 #define SIZE 128 //设置缓冲区长度 void *TCP_thread(void *arg); void *UDP_thread(void *arg); void *HandleIPFound(void *arg); pthread_t pth_tcp; //定义TCP的线程标识符 pthread_t pth_udp; //定义UDP的线程标识符 pthread_t pth_found; //定义found的线程标识符 void *pth_tcp_result; //定义指针,用来存储TCP线程的返回值 void *pth_udp_result; //定义指针,用来存储UDP线程的返回值 void *pth_found_result; //定义指针,用来存储FOUND线程的返回值 int main(int argc, char **argv) { int res_tcp = pthread_create(&pth_tcp, NULL, (void *)TCP_thread, NULL); //创建TCP的线程,并判断是否创建成功 if (0 != res_tcp) { printf("TCP线程创建失败!"); exit(1); } int res_udp = pthread_create(&pth_udp, NULL, (void *)UDP_thread, NULL); //创建UDP的线程,并判断是否创建成功 if (0 != res_udp) { printf("UDP线程创建失败!"); exit(1); } int res_found = pthread_create(&pth_found, NULL, (void *)HandleIPFound, NULL); //创建UDP的线程,并判断是否创建成功 if (0 != res_found) { printf("FOUND线程创建失败!"); exit(1); } //主线程阻塞,等待其余返回。 pthread_join(pth_tcp, &pth_tcp_result); pthread_join(pth_udp, &pth_udp_result); pthread_join(pth_found, &pth_found_result); return 0; } void *TCP_thread(void *arg) { int nread; MD5_CTX CtxLocal; unsigned char md5[SIZE]; //用来存储md5值 unsigned char Md5Temp[16]; //存储md5原始值 unsigned char md5_client[SIZE]; //用来客户端传来的md5值 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //设置线程的取消状态 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); //设置线程的取消类型 int sockfd; //定义监听socket描述符 int clientfd; //定义数据传输描述符 struct sockaddr_in hostaddr; //定义本机IP地址和端口号 struct sockaddr_in clientaddr; //定义客户端IP地址和端口号 char filename[SIZE]; //接收文件名 char Trans_info[SIZE]; //接收传输信息 char close_tcp_thread_flag[10] = "FALSE"; //定义是否关闭TCP的标志。 unsigned int addrlen; char buf[SIZE]; //定义缓冲区 int cnt; sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建套接字 if (-1 == sockfd) //如果套接字创建失败,则输出错误信息并退出。 { perror("创建套接字失败\n"); exit(1); } //将套接字于IP地址和端口进行绑定 hostaddr.sin_family = AF_INET; //TCP/IP协议 hostaddr.sin_port = htons(TCP_SERV_PORT); //随机选择一个未被占用的端口号 hostaddr.sin_addr.s_addr = INADDR_ANY; //本机IP地址 bzero(&(hostaddr.sin_zero), 8); //清零 int res = bind(sockfd, (struct sockaddr *) &hostaddr, sizeof(struct sockaddr)); //绑定 if(-1 == res) //如果套接字绑定失败,则输出错误信息并退出 { perror("套接字绑定失败\n"); exit(1); } res = listen(sockfd, LENGTH); //将套接字设为监听模式,以等待连接请求 if (-1 == res) { perror("设置监听模式错误\n"); exit(1); } printf("等待客户端\n"); //请求到来时,接受连接请求,并接收数据 addrlen = sizeof(struct sockaddr_in); while(1) { clientfd = accept(sockfd, (struct sockaddr *) &clientaddr, &addrlen); //接受连接请求 if (-1 == clientfd) { perror("接受连接请求错误\n"); } //printf("客户端IP:%s\n", inet_ntoa(clientaddr.sin_addr)); //输出客户端IP地址 recv(clientfd, Trans_info, SIZE, 0); //接收上传还是下载信息 if(0 == strcmp(Trans_info, "UPLOAD")) //判定如果时上传信息,则接收来自客户端的文件 { recv(clientfd, filename, SIZE, 0); //接收文件名 recv(clientfd, md5_client, SIZE, 0); //接收客户端传来的MD5值 int fp = open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU); //创建新文件, if(-1 == fp) { printf( "创建文件失败!\n "); exit(1); } while(1) { cnt = recv(clientfd, buf, SIZE, 0); if(cnt < 0) { printf( "数据接收失败!\n"); exit(1); } if(cnt != 0) { write(fp, buf, cnt); bzero(buf, sizeof(buf)); } else { //printf("接收完成!\n"); break; //退出文件接收的小循环 } } close(fp); //关闭文件描述符 fp = open(filename, O_RDONLY); //打开文件并获取文件描述符 MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fp, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; if(0 != strcmp(md5, md5_client)) { printf("文件校验失败!\n"); } close(clientfd); //关闭数据传输描述符 bzero(filename, sizeof(filename)); //清空文件名,防止再次连接时使用上次文件名。比如客户端未发过来有效文件名,而建立了连接 } else if(0 == strcmp(Trans_info, "DOWNLOAD")) //判定为下载信息 { recv(clientfd, filename, SIZE, 0); //接收需要发送的文件名 int fq = open(filename, O_RDONLY); //打开文件并获取文件描述符 if( fq < 0 ) { send(clientfd, "NOT_EXIST", 10, 0); //告知客户端文件不存在 } else { send(clientfd, "", 10, 0); //为了适配客户端接收信息。 MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fq, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; close(fq); send(clientfd, md5, SIZE, 0); //给客户端发送原始MD5值。 fq = open(filename, O_RDONLY); //再打开一次文件描述符 struct stat st; stat(filename, &st); //获取文件大小 int len = st.st_size; if(sendfile(clientfd, fq, 0, len) < 0) //发送文件并判断是否成功 { perror("发送文件失败!\n"); exit(1); } /* else { //printf("发送完成!\n"); } */ } close(fq); //关闭文件描述符 close(clientfd); //关闭数据传输描述符 } else if(0 == strcmp(Trans_info, "CLOSE")) //判断为需要退出线程 { close(sockfd); //关闭套接字 break; //退出整个TCP线程的大循环 } } pthread_cancel(pth_udp); //当退出TCP线程前,先关闭UDP线程 pthread_cancel(pth_found); //当退出TCP线程前,先关闭侦听线程 pthread_exit("TCP线程退出。\n"); } void *UDP_thread(void *arg) { int nread; MD5_CTX CtxLocal; unsigned char md5[SIZE]; //用来存储md5值 unsigned char Md5Temp[16]; //存储md5原始值 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //设置线程的取消状态 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); //设置线程的取消类型 int sockfd; //定义socket描述符 struct sockaddr_in hostaddr; //定义本机IP地址和端口号 struct sockaddr_in clientaddr; //定义客户端IP地址和端口号 char filename[SIZE]; //接收文件名 char Trans_info[SIZE]; //接收传输信息 unsigned int addrlen; char buf[SIZE]; //定义缓冲区 int cnt; sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建套接字 if (-1 == sockfd) //如果套接字创建失败,则输出错误信息并退出。 { perror("创建套接字失败\n"); exit(1); } //将套接字于IP地址和端口进行绑定 hostaddr.sin_family = AF_INET; //TCP/IP协议 hostaddr.sin_port = htons(UDP_SERV_PORT); //随机选择一个未被占用的端口号 hostaddr.sin_addr.s_addr = INADDR_ANY; //本机IP地址 bzero(&(hostaddr.sin_zero), 8); //清零 int res = bind(sockfd, (struct sockaddr *) &hostaddr, sizeof(struct sockaddr)); //绑定 if(-1 == res) //如果套接字绑定失败,则输出错误信息并退出 { perror("套接字绑定失败\n"); exit(1); } addrlen = sizeof(struct sockaddr_in); while(1) { recvfrom(sockfd, Trans_info, SIZE, 0, (struct sockaddr *) &clientaddr, &addrlen); //接收上传还是下载信息 //printf("客户端IP:%s\n", inet_ntoa(clientaddr.sin_addr)); //输出客户端IP地址 if(0 == strcmp(Trans_info, "UPLOAD")) //判定如果是上传信息,则接收来自客户端的文件 { recvfrom(sockfd, filename, SIZE, 0, (struct sockaddr *) & clientaddr, &addrlen); //接收文件名 int fp = open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU); //创建新文件,并判断是否创建成功 if(-1 == fp) { printf( "创建文件失败!\n "); exit(1); } while(1) { cnt = recvfrom(sockfd, buf, SIZE, 0, (struct sockaddr *) &clientaddr, &addrlen); if(-1 == cnt) { printf( "数据接收失败!\n"); exit(1); } write(fp, buf, cnt); bzero(buf, sizeof(buf)); if(0 == cnt) { //printf("接收完成!\n"); break; //退出文件接收的小循环 } } close(fp); //关闭文件描述符 fp = open(filename, O_RDONLY); //打开文件并获取文件描述符 MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fp, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; close(fp); sendto(sockfd, md5, SIZE, 0, (struct sockaddr *) &clientaddr, addrlen); //为了适配客户端接收信息。 bzero(filename, sizeof(filename)); //清空文件名,防止再次连接时使用上次文件名。比如客户端未发过来有效文件名,而建立了连接 } else if(0 == strcmp(Trans_info, "DOWNLOAD")) //判定为下载信息 { recvfrom(sockfd, filename, SIZE, 0, (struct sockaddr *) &clientaddr, &addrlen); //接收需要发送的文件名 int fq = open(filename, O_RDONLY); //打开文件并获取文件描述符 if(fq < 0) { sendto(sockfd, "NOT_EXIST", 10, 0, (struct sockaddr *) &clientaddr, addrlen); //告知客户端文件不存在 } else { sendto(sockfd, "", 10, 0, (struct sockaddr *) &clientaddr, addrlen); //为了适配客户端接收信息。 MD5_Init(&CtxLocal); //初始化MD5校验 while((nread = read(fq, buf, sizeof(buf))) > 0) { MD5_Update(&CtxLocal, buf, nread); //更新MD5校验 } MD5_Final(Md5Temp, &CtxLocal); //结束MD5校验 int i= 0;; for (i = 0; i < 16; ++i) { sprintf(md5 + 2*i, "%02x", Md5Temp[i]); } md5[2*i] = '\0'; close(fq); fq = open(filename, O_RDONLY); //打开文件并获取文件描述符 int fileTrans; while((fileTrans = read(fq, buf, SIZE)) > 0) //将文件信息存入缓存区 { sleep(0.001); if(-1 == sendto(sockfd, buf, fileTrans, 0, (struct sockaddr *) &clientaddr, addrlen)) //发送文件并判断是否成功 { perror("发送失败!\n"); exit(1); } if(fileTrans < SIZE) break; bzero(buf, SIZE); } } close(fq); //关闭文件描述符 sendto(sockfd, "", 0, 0, (struct sockaddr *) &clientaddr, addrlen); //给客户端发送空字节,告知传输完成 sendto(sockfd, md5, SIZE, 0, (struct sockaddr *) &clientaddr, addrlen); //给客户端发送原始MD5值 } else if(0 == strcmp(Trans_info, "CLOSE")) //判断为需要退出线程 { close(sockfd); //关闭套接字 break; //退出整个TCP线程的大循环 } } pthread_cancel(pth_tcp); //当退出UDP线程前,先关闭TCP线程 pthread_cancel(pth_found); //当退出UDP线程前,先关闭侦听线程 pthread_exit("UDP线程退出。\n"); } void *HandleIPFound(void *arg) { pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //设置线程的取消状态 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); //设置线程的取消类型 #define BUFFER_LEN 32 char IP_FOUND[BUFFER_LEN] = "IP_FOUND"; char IP_FOUND_ACK[BUFFER_LEN] = "IP_FOUND_ACK"; int ret = -1; int sock = -1; struct sockaddr_in local_addr; //本地地址 struct sockaddr_in from_addr; //客户端地址 unsigned int from_len = sizeof(struct sockaddr); int count = -1; fd_set readfd; char buff[BUFFER_LEN]; struct timeval timeout; timeout.tv_sec = 2; //超时时间2s timeout.tv_usec = 0; sock = socket(AF_INET, SOCK_DGRAM, 0); //建立数据报套接字 if(sock < 0) { printf("建立套接字失败!\n"); exit(1); } memset((void *) &local_addr, 0, sizeof(struct sockaddr_in)); //清空内存内容 local_addr.sin_family = AF_INET; //协议族 local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本机地址 local_addr.sin_port = htons(MCAST_PORT); //侦听端口 bzero(&(local_addr.sin_zero), 8); ret = bind(sock, (struct sockaddr*) &local_addr, sizeof(local_addr)); //绑定 if(0 != ret) { printf("绑定失败!\n"); exit(1); } while(1) { FD_ZERO(&readfd); //清空文件描述符集合 FD_SET(sock, &readfd); //将套接字描述符加入读集合 ret = select(sock+1, &readfd, NULL, NULL, &timeout); //select侦听是否有数据到来 switch(ret) { case -1: break; //发生错误 case 0: //超时,可以添加超时所要执行的代码 break; default: //有数据到来 if(FD_ISSET(sock, &readfd)) { bzero(buff, sizeof(buff)); count = recvfrom(sock, buff, BUFFER_LEN, 0, (struct sockaddr*) &from_addr, &from_len); //printf("接收到的数据是: %s\n", buff); if(strstr(buff, IP_FOUND)) //判断是否吻合 { count = sendto(sock, IP_FOUND_ACK, strlen(IP_FOUND_ACK), 0, (struct sockaddr*) &from_addr, from_len); //应答数据发送给客户端 if(-1 != count) { //pthread_exit("IPFOUND线程退出。\n"); //break; } } } } } //pthread_exit("UDP线程退出。\n"); }