UDP的点对点,广播,组播

大耗子 2020年03月02日 227次浏览

文章链接:https://codemouse.online/archives/2020-03-01-235956

UDP应用

  • UDP的点对点通信

    路由器是不转发广播包的

    交换机会转发广播数据包

  • UDP广播数据包

    广播只能在一个广播域(局域网)中传播,而不能跨网段传播

  • 组播数据包

    能够在组播组中进行传播的,并且路由器可以进行组播数据转发

  • 如何识别广播包

    MAC: 目的MAC是ff:ff:ff:ff:ff:ff

    IP: 10.0.0.0/8 ----> 10.255.255.255这个网段

    ​ 192.168.0.1/24 ---->192.168.0.255这个网段

设置UDP

  • 对socket进行设置(组播,广播...)

    int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
    int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
    
  • 通过tcpdump语句,抓包

    tcpdump -i eth0 -w ./xxx.pacp

一个简单的udp-Demo点对点

  • 服务端

    // 创建sock
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    // 初始化对端地址信息
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(7838);
    s_addr.sin_addr.s_addr = INADDR_ANY;
    int addr_len;
    // 绑定
    bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr));
    // 接收
    char buff[128];
    struct sockaddr_in c_addr;
    addr_len = sizeof(c_addr);
    recvfrom(sock, buff, sizeof(buff) - 1, 0,(struct sockaddr *) &c_addr, &addr_len);
    printf("recive come from %s:%d message:%s\n\r",
    			inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port), buff);
    
  • 客户端:

    // 创建sock
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    // 初始化对端地址信息
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(7838);
    s_addr.sin_addr.s_addr = inet_addr(argv[1]);
    // 发送
    int addr_len;
    char *buff = "i am here";
    sendto(sock, buff, strlen(buff), 0,(struct sockaddr *) &s_addr, addr_len);
    
    

通过udp实现一个广播

  • 服务器如上,没有什么区别

  • 客户端:在普通udp基础上添加一个广播包设置

    // 创建sock
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    // 初始化对端地址信息
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(7838);
    s_addr.sin_addr.s_addr = inet_addr(argv[1]);
    // 设置为广播包
    int yes = 1;
    setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));	
    // 发送
    int addr_len;
    char *buff = "i am here";
    sendto(sock, buff, strlen(buff), 0,(struct sockaddr *) &s_addr, addr_len);
    

通过udp实现组播

  • 解析域名得到的hostent结构体

    #include <netdb.h>
    struct hostent {
        char  *h_name;            /* official name of host */
        char **h_aliases;         /* alias list */
        int    h_addrtype;        /* host address type */
        int    h_length;          /* length of address */
        char **h_addr_list;       /* list of addresses */
    }
    
  • 服务端:解析域名写法

    struct sockaddr_in peeraddr;
    struct in_addr ia;
    unsigned int socklen;
    struct hostent *group;
    struct ip_mreq mreq;
    
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    bzero(&mreq, sizeof(struct ip_mreq));
    // 解析域名
    if (argv[1]) 
    {
        if ((group = gethostbyname(argv[1])) == (struct hostent *) 0) 
        {
            perror("gethostbyname");
            exit(EXIT_FAILURE);
        }
    } 
    else 
    {
        printf("you should give me a group address, 224.0.0.0-239.255.255.255\n");
        exit(EXIT_FAILURE);
    }
    // 获取到组播地址
    bcopy((void *) group->h_addr, (void *) &ia, group->h_length);
    bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr));
    // 设置入口地址
    if (argv[2]) {
        // inet_pton 转换ip为二进制
        if (inet_pton(AF_INET, argv[2], &mreq.imr_interface.s_addr) <= 0) 
        {	
            printf("Wrong dest IP address!\n");
            exit(EXIT_FAILURE);
        }
    } 
    else
        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    
    // 设置组播,将自己的的地址加入到组播中
    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq)) == -1) 
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    // 绑定自己的地址
    socklen = sizeof(struct sockaddr_in);
    memset(&peeraddr, 0, socklen);
    peeraddr.sin_family = AF_INET;
    peeraddr.sin_port = htons(7838);
    bcopy((void *) group->h_addr, (void *) &peeraddr.sin_addr, group->h_length);
    
    if (bind(sockfd, (struct sockaddr *) &peeraddr,sizeof(struct sockaddr_in)) == -1) 
    {
        printf("Bind error\n");
        exit(EXIT_FAILURE);
    }
    // 获取对端信息
    #define BUFFLEN 1024
    while(1)
    {
    	char recmsg[BUFLEN+1] = {0};
    	socklen_t addrLen = sizeof(struct sockaddr);
    	printf("ready 2 recv\n");
    	int recvRet = recvfrom(sockfd, recmsg, BUFLEN, 0,(struct sockaddr *) &peeraddr, &socklen);
    	if (-1 == recvRet)
    	{
    		perror("recvfrom fail\n");
    		break;
    	}
    	printf("recvfrom recmsg is %s\n",recmsg);
    }
    
    
  • 服务器:直接利用地址写法

    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if (-1 == sockfd)
    {
        perror("socket fail\n");
        return -3;
    }
    printf("socket succ\n");
    
    struct ip_mreq mreq;
    memset(&mreq,0,sizeof(struct ip_mreq));
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    // 设置组播地址
    mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
    // 将设定的地址绑定到组播中
    int setoptret = setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(struct ip_mreq));
    if (-1 == setoptret)
    {
        perror("setsockopt fail\n");
        return -4;
    }
    unsigned int socklen;
    struct sockaddr_in peeraddr;
    memset(&peeraddr,0,sizeof(struct sockaddr_in));
    peeraddr.sin_family = AF_INET;
    peeraddr.sin_addr.s_addr = inet_addr(argv[2]);
    peeraddr.sin_port = htons(7838);
    
    if (-1 == bind(sockfd,(struct sockaddr *)(&peeraddr),sizeof(struct sockaddr)))
    {
        perror("bind fail\n");
        return -5;
    }
    
    #define BUFFLEN 1024
    while(1)
    {
        char recmsg[BUFLEN+1] = {0};
        printf("ready 2 recv\n");
        int recvRet = recvfrom(sockfd, recmsg, BUFLEN, 0,(struct sockaddr *) &peeraddr, &socklen);
        if (-1 == recvRet)
        {
            perror("recvfrom fail\n");
            break;
        }
        printf("recvfrom recmsg is %s\n",recmsg);
    }
    
    
  • 客户端

    #deine BUFLEN 1024
    struct sockaddr_in peeraddr, myaddr;
    char recmsg[BUFLEN + 1];
    unsigned int socklen;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);	
    if (sockfd < 0)
    {
        printf("socket creating error\n");
        exit(EXIT_FAILURE);
    }
    // 先设置组播地址,和端口
    socklen = sizeof(struct sockaddr_in);
    memset(&peeraddr, 0, socklen);
    peeraddr.sin_family = AF_INET;
    peeraddr.sin_port = htons(7838);	
    //peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
    if (argv[1]) {					
        if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
            printf("wrong group address!\n");
            exit(EXIT_FAILURE);
        }
    } 
    else {
        printf("no group address!\n");
        exit(EXIT_FAILURE);
    }
    // 设置自己的地址,等待接受
    memset(&myaddr, 0, socklen);
    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(23456);	
    //myaddr.sin_addr.s_addr = inet_addr(argv[2]);
    if (argv[2]) {				
        if (inet_pton(AF_INET, argv[2], &myaddr.sin_addr) <= 0) 
        {
            printf("self ip address error!\n");
            exit(EXIT_FAILURE);
        }
    } else
        myaddr.sin_addr.s_addr = INADDR_ANY;
    
    // 绑定自己的地址
    if (bind(sockfd, (struct sockaddr *) &myaddr,sizeof(struct sockaddr_in)) == -1) 
    {								
        printf("Bind error\n");
        exit(EXIT_FAILURE);
    }
    for (;;) {
        bzero(recmsg, BUFLEN + 1);
        printf("input message to send:");
        if (fgets(recmsg, BUFLEN, stdin) == (char *) EOF)	
            exit(EXIT_FAILURE);;
        if (sendto(sockfd, recmsg, strlen(recmsg), 0,(struct sockaddr *) &peeraddr,
                   sizeof(struct sockaddr_in)) < 0) 			
        {
            printf("sendto error!\n");
            exit(EXIT_FAILURE);;
        }
        printf("sned message:%s", recmsg);	
    }