打印

广播、组播 socket编程,附带可以编译运行的例子

[复制链接]
635|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
沙发
keer_zu|  楼主 | 2023-9-8 13:37 | 只看该作者
首先,什么是单播:

(1) 单播

之前在进行UDP和TCP编程的时候,客户端把数据发送到指定IP地址,此时接收方只有一个,这种数据包发送方式称为“单播”。



使用特权

评论回复
板凳
keer_zu|  楼主 | 2023-9-8 13:38 | 只看该作者
什么是组播?

(2) 多播(组播)
如果是把数据发送给某个局域网中的一组IP地址,这种发送方式称为“多播”,这个组称为“多播组”,只有加入多播组的主机才能收到数据。

广播是发给某一局域网中的所有主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
多播是一种折中的方式,既能发送给多个主机,又能避免象广播那样带来过多的负载


使用特权

评论回复
地板
keer_zu|  楼主 | 2023-9-8 13:39 | 只看该作者
什么是广播?

(3) 广播
如果是把数据发送某个局域网中的所有主机,这种数据包的发送方式称为“广播”

发送出去的数据会被 广播地址所在网段的所有主机接收
每个局域网的最大主机地址代表该网段的广播地址。
以192.168.1.0 (255.255.255.0) 网段为例,192.168.1.255代表该网段的广播地址
255.255.255.255 在所有网段中都代表广播地址。
如果主机A向 255.255.255.255 发数据,那么当前局域网里的所有主机都会收到数据。



使用特权

评论回复
5
keer_zu|  楼主 | 2023-9-8 13:43 | 只看该作者
2、多播 socket编程(只能是UDP通信)
多播socket编程的侧重点在接收方(服务端),接收方要创建一个多播组(类似于QQ群),然后把当前套接字加入到多播组中;而发送方发送数据的目标地址不是服务端的IP地址,而是多播组的IP地址。

创建数据报套接字
绑定IP地址和端口号
创建多播组(类似于创建QQ群)
将当前套接字加入到多播组(类似于加群)
接收数据

#define SERV_PORT 9090
#define MULTICAST_IP "192.168.11.170"
#define BUFSIZE 128

int fd = -1;

/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {        //udp程序
    perror ("socket");
    exit (1);
}

/* 2. 绑定IP地址和端口号 */
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin))
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);                 // 网络字节序的端口号
sin.sin_addr.s_addr = htonl(INADDR_ANY);    // 服务端可以绑定任意IP
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
    perror ("bind");
    exit (1);
}

/* 3. 创建多播组,初始化多播组结构体 */
struct ip_mreq mreq;
memset(&mreq, 0, sizeof(mreq))
mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);

/* 4. 把当前套接字加入到多播组 */
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

/* 5. 接收数据 */
char buf[BUFSIZE ];
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
recvfrom (fd, buf, BUFSIZE - 1, 0, (struct sockaddr *)&cin, &addrlen);


使用特权

评论回复
6
keer_zu|  楼主 | 2023-9-8 13:43 | 只看该作者
3、广播 socket编程(只能是UDP通信)
广播socket编程的侧重点在发送方(客户端),而且只有使用UDP协议才能广播。接收方的代码可以沿用单播的代码。发送方的数据发送步骤如下:

创建用户数据报套接字
套接字默认不允许广播数据包(因为可能引发广播风暴),需要使用 setsockopt设置属性
目标地址(接收方地址) 指定为广播地址
指定目标端口
发送数据包


#define DST_PORT 9090

/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {        //UDP编程
    perror ("socket");
    exit (1);
}

/* 2. 允许广播设置 */
int b_br = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &b_br, sizeof(int));

/*3. 指定目标IP和端口号填充 */
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin))
sin.sin_family = AF_INET;
sin.sin_port = htons (SERV_PORT);                          //网络字节序的端口号
sin.sin_addr.s_addr = inet_addr("192.168.11.255");    // 广播地址


/*4. 发送数据 */
char buf[128] = {'1','2','3','4','5','6','\0'};
sendto (fd, buf, strlen(buf), 0, (struct sockaddr *)&sin, sizeof(sin));


使用特权

评论回复
7
keer_zu|  楼主 | 2023-9-8 13:56 | 只看该作者
LINUX SOCKET PART 13: MULTICAST

概述

组播提供了在网络中进行一对多的发送的机制,组播可以是在一个网段内,也可以是跨网段的,不过跨网段需要交换机、路由器等网络设备支持组播。
Hosts可以在任何时间加入或者离开组播组,对于组播组的成员没有所处位置的限制,也没有数量的限制,D类互联网地址是用于组播的:224.0.0.0 - 239.255.255.255。
通过无连接Socket编程可以实现组播数据的发送和接收。组播数据只能通过一个网络接口发送,即使设备上有多个网络接口。

组播是一对多的传输机制,不能通过面向连接的Socket实现组播。


使用特权

评论回复
8
icecut| | 2023-9-8 13:59 | 只看该作者
组播,  是路由器的功能? 还是服务器的功能?
路由器记录组播是不是有成本?

使用特权

评论回复
评论
keer_zu 2023-9-8 14:04 回复TA
不是路由器的功能,应该看接受地址是不是组播地址吧,这个我也在研究。以前很少用到这个,现在必须处理组播包了。很多交换设备具备设置过滤组播包的功能,比如rel9072,可以通过配置和更新固件实现功能,除此之外还有vlan设置等,vlan是可以反映在数据包的字段里的。还有ACL 
9
keer_zu|  楼主 | 2023-9-8 14:00 | 只看该作者
一个组播包发送的例子:

/* Send Multicast Datagram code example. */

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <stdio.h>

#include <stdlib.h>

struct in_addr localInterface;

struct sockaddr_in groupSock;

int sd;

char databuf[1024] = "Multicast test message lol!";

int datalen = sizeof(databuf);



int main (int argc, char *argv[ ])

{

/* Create a datagram socket on which to send. */

sd = socket(AF_INET, SOCK_DGRAM, 0);

if(sd < 0)

{

  perror("Opening datagram socket error");

  exit(1);

}

else

  printf("Opening the datagram socket...OK.\n");



/* Initialize the group sockaddr structure with a */

/* group address of 225.1.1.1 and port 5555. */

memset((char *) &groupSock, 0, sizeof(groupSock));

groupSock.sin_family = AF_INET;

groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");

groupSock.sin_port = htons(4321);



/* Disable loopback so you do not receive your own datagrams.

{

char loopch = 0;

if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)

{

perror("Setting IP_MULTICAST_LOOP error");

close(sd);

exit(1);

}

else

printf("Disabling the loopback...OK.\n");

}

*/



/* Set local interface for outbound multicast datagrams. */

/* The IP address specified must be associated with a local, */

/* multicast capable interface. */

localInterface.s_addr = inet_addr("203.106.93.94");

if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)

{

  perror("Setting local interface error");

  exit(1);

}

else

  printf("Setting the local interface...OK\n");

/* Send a message to the multicast group specified by the*/

/* groupSock sockaddr structure. */

/*int datalen = 1024;*/

if(sendto(sd, databuf, datalen, 0, (struct sockaddr*)&groupSock, sizeof(groupSock)) < 0)

{perror("Sending datagram message error");}

else

  printf("Sending datagram message...OK\n");



/* Try the re-read from the socket if the loopback is not disable

if(read(sd, databuf, datalen) < 0)

{

perror("Reading datagram message error\n");

close(sd);

exit(1);

}

else

{

printf("Reading datagram message from client...OK\n");

printf("The message is: %s\n", databuf);

}

*/

return 0;

}



使用特权

评论回复
10
keer_zu|  楼主 | 2023-9-8 17:00 | 只看该作者
icecut 发表于 2023-9-8 13:59
组播,  是路由器的功能? 还是服务器的功能?
路由器记录组播是不是有成本?
...

下面这个程序可以对多个组播地址发数据,刚刚写的,测试过了:

#include "fun_interface.h"
#include <unistd.h>
#include <string>
struct in_addr localInterface;
struct sockaddr_in groupSock;
int sd;
char databuf[1024] = "Multicast test message lol!";

int datalen = sizeof(databuf);

typedef struct {
    std::string dip;
    int sock;
    struct sockaddr_in gsock;
} test_item_t;

test_item_t sock_items[] = {
{
    .dip = "225.0.0.128"
},
{
    .dip = "225.0.0.120"
},
{
    .dip = "225.0.0.111"
},
{
    .dip = "225.0.0.110"
},
{
    .dip = "239.202.0.11"
},
{
    .dip = "239.202.255.251"
}
};

int init_sock()
{
    int sd;
    int items_size = sizeof(sock_items)/sizeof(test_item_t);
    printf("== size:%d\n",items_size);
    for(int i = 0;i < items_size;i ++) {
        sd = socket(AF_INET, SOCK_DGRAM, 0);
        if(sd < 0) {
            perror("Opening datagram socket error");
            return -1;
        } else {
            printf("open socket ....ok\n");
            sock_items[i].sock = sd;
        }

        memset((char *) &sock_items[i].gsock, 0, sizeof(sock_items[i].gsock));
        sock_items[i].gsock.sin_family = AF_INET;
        //groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
        sock_items[i].gsock.sin_addr.s_addr = inet_addr(sock_items[i].dip.c_str());
        sock_items[i].gsock.sin_port = htons(4321);

        localInterface.s_addr = inet_addr("192.168.1.12");
        //localInterface.s_addr = inet_addr("172.16.16.90");

        if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0){
            perror("Setting local interface error");
            return -1;
        }else
            printf("Setting the local interface...OK\n");
    }

    return 0;
}

int main()
{
    int items_size = sizeof(sock_items)/sizeof(test_item_t);
   
    if(init_sock() != 0) {
        exit(1);
    }
    /* Send a message to the multicast group specified by the*/
    /* groupSock sockaddr structure. */
    /*int datalen = 1024;*/
    for(int i = 0;i < 100;i ++) {
        for(int j = 0;j < items_size;j ++ ) {
            if(sendto(sock_items[j].sock, databuf, datalen, 0, (struct sockaddr*)&sock_items[j].gsock, sizeof(sock_items[j].gsock)) < 0) {
                perror("Sending datagram message error");
            } else
                printf("Sending datagram message...OK\n");
        }
        sleep(3);
    }
   
    return 0;
}


使用特权

评论回复
11
keer_zu|  楼主 | 2023-9-8 17:06 | 只看该作者
只收到两个,其它地址被rtl9072过滤掉了。

使用特权

评论回复
12
keer_zu|  楼主 | 2023-9-8 17:14 | 只看该作者

这个是没有过滤的版本

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1352

主题

12436

帖子

53

粉丝