UDP特性之组播(多播)

实例

实例-1-车小猿

适用于Ubuntu与Cygwin

发送端代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <string.h>
  #include <arpa/inet.h>

  #define GROUP_IP "224.0.1.0"
  //#define GROUP_IP "239.0.1.10"

  int main()
  {
	  // 1. 创建通信的套接字
	  int fd = socket(AF_INET, SOCK_DGRAM, 0);
	  if(fd == -1)
	  {
		  perror("socket");
		  exit(0);
	  }

	  // 2. 设置组播属性 (经测试可以不设置发送端组播属性也能正常发送)
	  struct in_addr opt;
	  // 将组播地址初始化到这个结构体成员中即可
	  inet_pton(AF_INET, GROUP_IP, &opt.s_addr);
	  setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));

	  char buf[1024];
	  char sendaddrbuf[64];
	
	  socklen_t len = sizeof(struct sockaddr_in);
	  struct sockaddr_in sendaddr;
	
	  struct sockaddr_in cliaddr;
	  cliaddr.sin_family = AF_INET;
	  cliaddr.sin_port = htons(9999); // 接收端需要绑定9999端口
	  // 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
	  inet_pton(AF_INET, GROUP_IP, &cliaddr.sin_addr.s_addr);
						
	  // 3. 通信
	  int num = 0;
	  while(1)
	  {
		  memset(buf, 0, sizeof(buf));
		  sprintf(buf, "hello, client...%d\n", num++);
		  // 数据广播
		  sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
		  printf("发送的组播的数据: %s\n", buf);
		  memset(buf, 0, sizeof(buf));
		  recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sendaddr, &len);
		  printf("sendaddr:%s, prot:%d\n", inet_ntop(AF_INET, &sendaddr.sin_addr.s_addr,  sendaddrbuf, sizeof(sendaddrbuf)),  sendaddr.sin_port);
		  printf("接收到的组播消息: %s\n", buf);
	  }
	  close(fd);
	  return 0;
  }

接收端代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <string.h>
  #include <arpa/inet.h>

  #define GROUP_IP "224.0.1.0"
  //#define GROUP_IP "239.0.1.10"


  int main()
  {
	  // 1. 创建通信的套接字
	  int fd = socket(AF_INET, SOCK_DGRAM, 0);
	  if(fd == -1)
	  {
		  perror("socket");
		  exit(0);
	  }

	  // 2. 通信的套接字和本地的IP与端口绑定
	  struct sockaddr_in addr;
	  addr.sin_family = AF_INET;
	  addr.sin_port = htons(9999);    // 大端
	  addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 0.0.0.0
	  int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
	  if(ret == -1)
	  {
		  perror("bind");
		  exit(0);
	  }

	  // 3. 加入到多播组
  #if 0 //使用struct ip_mreqn或者 struct ip_mreq 设置接收端组播属性都可以正常接收
	  struct ip_mreqn opt;
	  // 要加入到哪个多播组, 通过组播地址来区分
	  inet_pton(AF_INET, GROUP_IP, &opt.imr_multiaddr.s_addr);
	  opt.imr_address.s_addr = htonl(INADDR_ANY);
	  opt.imr_ifindex = if_nametoindex("ens33");
	  setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
  #else
	  struct ip_mreq mreq; // 多播地址结构体
	  mreq.imr_multiaddr.s_addr=inet_addr(GROUP_IP);
	  mreq.imr_interface.s_addr = htonl(INADDR_ANY);	
	  ret=setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
  #endif

	  char buf[1024];
	  char sendaddrbuf[64];
	  socklen_t len = sizeof(struct sockaddr_in);
	  struct sockaddr_in sendaddr;

	  // 3. 通信
	  while(1)
	  {
		  // 接收广播消息
		  memset(buf, 0, sizeof(buf));
		  // 阻塞等待数据达到
		  recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sendaddr, &len);
		  printf("sendaddr:%s, prot:%d\n", inet_ntop(AF_INET, &sendaddr.sin_addr.s_addr,  sendaddrbuf, sizeof(sendaddrbuf)),  sendaddr.sin_port);
		  printf("接收到的组播消息: %s\n", buf);
		  sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr *)&sendaddr, len);
	  }
	  close(fd);
	  return 0;
  }

编译方式

1
2
  make send
  make recv

运行方式

先运行接收端,再运行发送端

接收端
1
  ./recv
发送端
1
  ./send

实例-2-苏丙榅

适用于Ubuntu

发送端代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <string.h>
  #include <arpa/inet.h>

  int main()
  {
	  // 1. 创建通信的套接字
	  int fd = socket(AF_INET, SOCK_DGRAM, 0);
	  if(fd == -1)
	  {
		  perror("socket");
		  exit(0);
	  }

	  // 2. 设置组播属性
	  struct in_addr opt;
	  // 将组播地址初始化到这个结构体成员中即可
	  inet_pton(AF_INET, "239.0.1.10", &opt.s_addr);
	  setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));

	  char buf[1024];
	  struct sockaddr_in cliaddr;
	  int len = sizeof(cliaddr);
	  cliaddr.sin_family = AF_INET;
	  cliaddr.sin_port = htons(9999); // 接收端需要绑定9999端口
	  // 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
	  inet_pton(AF_INET, "239.0.1.10", &cliaddr.sin_addr.s_addr);
	  // 3. 通信
	  int num = 0;
	  while(1)
	  {
		  sprintf(buf, "hello, client...%d\n", num++);
		  // 数据广播
		  sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
		  printf("发送的组播的数据: %s\n", buf);
		  sleep(1);
	  }

	  close(fd);

	  return 0;
  }

接收端代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <string.h>
  #include <arpa/inet.h>
  #include <net/if.h>

  int main()
  {
	  // 1. 创建通信的套接字
	  int fd = socket(AF_INET, SOCK_DGRAM, 0);
	  if(fd == -1)
	  {
		  perror("socket");
		  exit(0);
	  }

	  // 2. 通信的套接字和本地的IP与端口绑定
	  struct sockaddr_in addr;
	  addr.sin_family = AF_INET;
	  addr.sin_port = htons(9999);    // 大端
	  addr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
	  int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
	  if(ret == -1)
	  {
		  perror("bind");
		  exit(0);
	  }

	  // 3. 加入到多播组
	  struct ip_mreqn opt;
	  // 要加入到哪个多播组, 通过组播地址来区分
	  inet_pton(AF_INET, "239.0.1.10", &opt.imr_multiaddr.s_addr);
	  opt.imr_address.s_addr = INADDR_ANY;
	  opt.imr_ifindex = if_nametoindex("ens33");
	  setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));

	  char buf[1024];
	  // 3. 通信
	  while(1)
	  {
		  // 接收广播消息
		  memset(buf, 0, sizeof(buf));
		  // 阻塞等待数据达到
		  recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
		  printf("接收到的组播消息: %s\n", buf);
	  }

	  close(fd);

	  return 0;
  }

编译方式

1
2
  make send
  make recv

运行方式

先运行接收端,再运行发送端

接收端
1
  ./recv
发送端
1
  ./send

查看Linux系统是否支持多播:

方法A

ifconfig出现 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric

方法B

查看/boot/config-x.x.xx 文件中是否有如下内容

1
2
3
  CONFIG_IP_MULTICAST=y
  CONFIG_NET_IPIP=m
  CONFIG_IP_MROUTE=y