乐趣区

关于c:Linux网络开发必学教程17深入-UDP-数据收发-下

问题:UDP 是否还有其余一对多的数据发送形式?

UDP 通信中的多播

多播是 向特定组中的所有主机传输数据的办法,多播也称之为组播

多播数据传输的特点:

  • 多播发送者针对特定的多播组,只发送 1 次数据,组内主机均可接管到数据
  • 主机退出特定组,即可接管该组中的多播数据
  • 多播组可在 IP 地址范畴内任意增加

关键问题:如何收发多播数据

多播组是一个 D 类地址(224.0.0.0 – 239.255.255.255)

“ 退出多播组 ” 可了解为 UDP 网络程序进行的申请

  • 如:申请接管发往 239.234.111.222 的多播数据
  • 即:设置属性(IPPROTO_IP, IP_ADD_MEMBERSHP

发送多播数据的形式,与发送一般 UDP 数据的形式雷同

  • 准备操作:设置属性,如:(IPPROTO_IP, IP_MULTICAST_TTL

注意事项

退出同一个多播组的主机不肯定在同一个网络中
因而,必须设置多播数据的最多转发次数(TTL)

  • TTL(即:Time To Live)是决定数据传输间隔的次要因素
  • TTL 用整数示意,并且每通过 1 个路由就缩小 1
  • 当 TTL 变为 0 时,数据无奈持续传递,只能销毁

多播程序设计:发送端

IP_MULTCAST_TTL: 用于设置多播数据的”最远传输间隔“,默认 1
IP_MULTICAST_IF: 用于设置多播数据从哪一个网络接口(网卡)发送进来,默认:0.0.0.0

默认 0.0.0.0 状况下操作系统会自主抉择应用哪个网络接口,但在 [多网卡主机] 的[理论工程利用]中,最好手工指定!!

IP_MULTCAST_LOOP: 用于设置多播数据是否发送回本机,默认 1(1,发送回本机)

remote.sin_family = AF_INET;
remote.sin_addr.s_addr = inet_addr("224.1.1.168");
remote.sin_port = htons(8888);

setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));

do {len = sizeof(remote);
    r = sendto(sock, buf, strlen(buf), 0, (struct sockaddr*)&remote, len);
    sleep(1);
}while(r > 0);

多播程序设计:接收端

IP_ADD_MEMBRESHIP: 用于申请加入多播组,参数为:多播组和本机地址

struct ip_mreq {
    // group address
    struct in_addr inmr_multiaddr;
    // local host address
    struct in_addr imr_interface
}

👇

struct ip_mreq group = {0};

group.imr_multiaddr.s_addr = inet_addr("224.1.1.168");
group.imr_interface.s_addr = htonl(INADDR_ANY);  // 监听所有网卡

setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));

do {len = sizeof(remote);
    r = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&remote, &len);
    buf[r] = 0;
    printf("r = %d\n", r);
    printf("msg = %s\n", buf);
}while (r > 0)

退出多播组

quit = setsockopt(sock, IP_PROTO_IP, IP_DROP_MEMBERSHIP, &group, sizeof(group));

if (quit == 0) {break;}

编程试验:UDP 多播程序设计

mul_tx.c

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = 0;
    struct sockaddr_in remote = {0};
    socklen_t len = 0;
    char buf[128] = "D.T.Software";
    struct in_addr addr = {0};

    int ttl = 0;
    int loop = 0;

    sock = socket(PF_INET, SOCK_DGRAM, 0);

    if (sock == -1) {printf("socket error\n");
        return -1;
    }

    remote.sin_family = AF_INET;
    remote.sin_addr.s_addr = inet_addr("224.1.1.168");
    remote.sin_port = htons(8888);

    //-------------------------
    ttl = 0;
    len = sizeof(ttl);
    getsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, &len);  // 能够不设置,ttl 默认为 1 
    printf("default ttl = %d\n", ttl);

    ttl = 32;
    len = sizeof(ttl);
    setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, len); 
    printf("current ttl = %d\n", ttl);

    loop = 0;
    len = sizeof(loop);
    getsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &len);    // 能够不设置,ttl 默认为 1,即本机也会收到
    printf("default loop = %d\n", loop);

    // addr.s_addr = inet_addr("192.168.3.221");  // 具体指定应用哪一块网卡进行数据播送
    addr.s_addr = htonl(INADDR_ANY);              // 交由操作系统进行抉择
    len = sizeof(addr);
    setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, len);    // 能够不设置,默认为 INADDR_ANY 即 "0.0.0.0", 交由操作系统抉择应用哪个网卡
    printf("current if = %s\n", inet_ntoa(addr));                // 当主机有多个网卡,在理论工程应用时,最好手工指定应用那块网卡播送数据
    //-------------------------

    while (1) {len = sizeof(remote);

        sendto(sock, buf, strlen(buf), 0, (struct sockaddr*)&remote, len);

        sleep(1);
    }

    close(sock);

    return 0;
}

mul_rx.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    struct sockaddr_in remote = {0};
    struct ip_mreq group = {0};
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_DGRAM, 0);

    if (server == -1) {printf("server socket error");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {printf("udp server bind error\n");
        return -1;
    }

    printf("udp server start sucess\n");

    group.imr_multiaddr.s_addr = inet_addr("224.1.1.168");
    group.imr_interface.s_addr = htonl(INADDR_ANY);

    setsockopt(server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));

    while (1) {len = sizeof(remote);

        r = recvfrom(server, buf, sizeof(buf), 0, (struct sockaddr*)&remote, &len);

        if (r > 0) {buf[r] = 0;

            printf("Recvive: %s\n", buf);
        } else {break;}
    }

    close(server);

    return 0;
}

小结

单播:一对一数据发送,即:指定指标主机发送数据

播送(必须同一局域网)

  • 本地播送:本地局域网播送数据,所有主机均可接收数据
  • 间接播送:指定网络播送数据,指标网络中的主机均可接收数据

多播(组播)(可不同一局域网)

  • 向指定的多播地址发送数据,”订阅“该地址的主机均可接收数据
退出移动版