UDP组播的说明与使用

2.5k words

概述

说明

网络通讯中,数据传输方式有:单播,广播和组播。单播是一对一的通信,广播则是对网络中所有主机进行通信。在需要对网络中某些主机进行通信时,单播会多次重复发送数据,广播则会造成资源浪费与安全问题。而组播是将信息发送到某个组播组,只有加入组播组的主机才能接收到消息,这种方式解决了单播情况下数据的重复拷贝及带宽的重复占用,也解决了广播方式下带宽资源的浪费。

组播中数据发送者和接收者之间是一对多点的连接方式,多个接收者加入同一个组播组,共享同一个IP地址,同时组播组中的成员是动态的,某个成员的加入和退出并不影响原有的组播组。当网络中的某些用户需要特定数据时,组播数据发送者(即组播源)仅发送一次数据,借助组播路由协议为组播数据包建立组播分发树,被传递的数据到达距离用户端尽可能近的节点后才开始复制和分发。

组播地址

组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。

224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。

使用

配置流程

  1. 创建套接字

    由于UDP与TCP都是全双工的,所以可以使用同一个套接字进行同时收发操作。

    1
    2
    3
    4
    5
    6
    #define UDP_MCAST_PORT 37102        //组播端口
    #define UDP_MCAST_ADDR "224.0.1.88" //组播地址

    auto socket = socket(AF_INET,SOCK_DGRAM,0);
    if (socket <= 0)
    return false;
  1. 设置端口复用

    1
    2
    3
    int opt = 1;
    if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR ,(char*)&opt, sizeof(opt)) < 0)
    return false;
  2. 绑定组播端口

    1
    2
    3
    4
    5
    6
    7
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(UDP_MCAST_PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    return false;
  3. 加入组播

    1
    2
    3
    4
    5
    6
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(UDP_MCAST_ADDR);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) < 0)
    return false;
  4. 发送与接收

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //发送
    struct sockaddr_in addrSrv;
    addrSrv.sin_family=AF_INET;
    addrSrv.sin_addr.s_addr = inet_addr(UDP_MCAST_ADDR);
    addrSrv.sin_port=htons(UDP_MCAST_PORT);

    if (sendto(socket, data, dataLen, 0, (struct sockaddr *)&addrSrv,sizeof(addrSrv)) > 0)
    return true;

    //接收
    struct sockaddr_in group_addr;
    socklen_t addr_len = sizeof(group_addr);

    int len = recvfrom(socket, buff, buffLen, 0, (struct sockaddr *) &group_addr, (int *)&addr_len);
    recvIP = inet_ntoa(group_addr.sin_addr);
    recvPort = group_addr.sin_port;

多网卡配置流程

多网卡组播需要在加入组播时指定本地IP地址,并且需要在最后多加一步指定组播的出口网卡:

  1. 加入组播

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(UDP_MCAST_ADDR);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    //指定接口接收组播信息
    mreq.imr_interface.s_addr = inet_addr(localIP);

    if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) < 0)
    return false;
  2. 指定组播出口网卡

    1
    2
    3
    4
    5
    6
    struct in_addr addr;
    memset(&addr, 0, sizeof(struct in_addr));

    addr.s_addr=inet_addr(localIP);
    if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (char*)&addr, sizeof(addr)) < 0)
    return false;

参考

Comments