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

[复制链接]
 楼主| keer_zu 发表于 2023-9-8 13:36 | 显示全部楼层 |阅读模式
本帖最后由 keer_zu 于 2023-9-8 17:03 编辑

有个任务需要组播的socket编程

平台qnx,借鉴一下linux的方法。

开始......


 楼主| keer_zu 发表于 2023-9-8 13:37 | 显示全部楼层
首先,什么是单播:

(1) 单播

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

6448464fab30095af8.png



 楼主| keer_zu 发表于 2023-9-8 13:38 | 显示全部楼层
什么是组播?

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

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

 楼主| 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 发数据,那么当前局域网里的所有主机都会收到数据。

3122664fab37ba0c4a.png

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

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

  1. #define SERV_PORT 9090
  2. #define MULTICAST_IP "192.168.11.170"
  3. #define BUFSIZE 128

  4. int fd = -1;

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

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

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

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

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


 楼主| keer_zu 发表于 2023-9-8 13:43 | 显示全部楼层
3、广播 socket编程(只能是UDP通信)
广播socket编程的侧重点在发送方(客户端),而且只有使用UDP协议才能广播。接收方的代码可以沿用单播的代码。发送方的数据发送步骤如下:

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


  1. #define DST_PORT 9090

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

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

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


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


 楼主| keer_zu 发表于 2023-9-8 13:56 | 显示全部楼层
LINUX SOCKET PART 13: MULTICAST

概述

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

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


icecut 发表于 2023-9-8 13:59 | 显示全部楼层
组播,  是路由器的功能? 还是服务器的功能?
路由器记录组播是不是有成本?

评论

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

  1. /* Send Multicast Datagram code example. */

  2. #include <sys/types.h>

  3. #include <sys/socket.h>

  4. #include <arpa/inet.h>

  5. #include <netinet/in.h>

  6. #include <stdio.h>

  7. #include <stdlib.h>

  8. struct in_addr localInterface;

  9. struct sockaddr_in groupSock;

  10. int sd;

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

  12. int datalen = sizeof(databuf);



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

  14. {

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

  16. sd = socket(AF_INET, SOCK_DGRAM, 0);

  17. if(sd < 0)

  18. {

  19.   perror("Opening datagram socket error");

  20.   exit(1);

  21. }

  22. else

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



  24. /* Initialize the group sockaddr structure with a */

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

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

  27. groupSock.sin_family = AF_INET;

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

  29. groupSock.sin_port = htons(4321);



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

  31. {

  32. char loopch = 0;

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

  34. {

  35. perror("Setting IP_MULTICAST_LOOP error");

  36. close(sd);

  37. exit(1);

  38. }

  39. else

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

  41. }

  42. */



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

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

  45. /* multicast capable interface. */

  46. localInterface.s_addr = inet_addr("203.106.93.94");

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

  48. {

  49.   perror("Setting local interface error");

  50.   exit(1);

  51. }

  52. else

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

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

  55. /* groupSock sockaddr structure. */

  56. /*int datalen = 1024;*/

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

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

  59. else

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



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

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

  63. {

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

  65. close(sd);

  66. exit(1);

  67. }

  68. else

  69. {

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

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

  72. }

  73. */

  74. return 0;

  75. }



 楼主| keer_zu 发表于 2023-9-8 17:00 | 显示全部楼层
icecut 发表于 2023-9-8 13:59
组播,  是路由器的功能? 还是服务器的功能?
路由器记录组播是不是有成本?
...

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

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

  8. int datalen = sizeof(databuf);

  9. typedef struct {
  10.     std::string dip;
  11.     int sock;
  12.     struct sockaddr_in gsock;
  13. } test_item_t;

  14. test_item_t sock_items[] = {
  15. {
  16.     .dip = "225.0.0.128"
  17. },
  18. {
  19.     .dip = "225.0.0.120"
  20. },
  21. {
  22.     .dip = "225.0.0.111"
  23. },
  24. {
  25.     .dip = "225.0.0.110"
  26. },
  27. {
  28.     .dip = "239.202.0.11"
  29. },
  30. {
  31.     .dip = "239.202.255.251"
  32. }
  33. };

  34. int init_sock()
  35. {
  36.     int sd;
  37.     int items_size = sizeof(sock_items)/sizeof(test_item_t);
  38.     printf("== size:%d\n",items_size);
  39.     for(int i = 0;i < items_size;i ++) {
  40.         sd = socket(AF_INET, SOCK_DGRAM, 0);
  41.         if(sd < 0) {
  42.             perror("Opening datagram socket error");
  43.             return -1;
  44.         } else {
  45.             printf("open socket ....ok\n");
  46.             sock_items[i].sock = sd;
  47.         }

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

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

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

  61.     return 0;
  62. }

  63. int main()
  64. {
  65.     int items_size = sizeof(sock_items)/sizeof(test_item_t);
  66.    
  67.     if(init_sock() != 0) {
  68.         exit(1);
  69.     }
  70.     /* Send a message to the multicast group specified by the*/
  71.     /* groupSock sockaddr structure. */
  72.     /*int datalen = 1024;*/
  73.     for(int i = 0;i < 100;i ++) {
  74.         for(int j = 0;j < items_size;j ++ ) {
  75.             if(sendto(sock_items[j].sock, databuf, datalen, 0, (struct sockaddr*)&sock_items[j].gsock, sizeof(sock_items[j].gsock)) < 0) {
  76.                 perror("Sending datagram message error");
  77.             } else
  78.                 printf("Sending datagram message...OK\n");
  79.         }
  80.         sleep(3);
  81.     }
  82.    
  83.     return 0;
  84. }


 楼主| keer_zu 发表于 2023-9-8 17:06 | 显示全部楼层
516364fae4098a943.png 只收到两个,其它地址被rtl9072过滤掉了。
 楼主| keer_zu 发表于 2023-9-8 17:14 | 显示全部楼层
4017964fae5e52d901.png
这个是没有过滤的版本
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

1478

主题

12917

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部
个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1478

主题

12917

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部