ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

TCP/UDP

2019-08-19 09:54:49  阅读:139  来源: 互联网

标签:UDP struct int TCP sockfd SIZE md5 MD5


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");
}

  

标签:UDP,struct,int,TCP,sockfd,SIZE,md5,MD5
来源: https://www.cnblogs.com/homio/p/11328715.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有