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");
}
原文:https://www.cnblogs.com/homio/p/11328715.html