程序猿问科比:你为什么这么成功?
科比:你知道洛杉矶早晨4点钟是什么样子吗?
程序猿:知道,一般那个时候我刚刚下班,怎么了?
科比:……没事,随便问问~~~


UDP广播:

发送方设置SO_BROADCAST选项,并将发送地址设置为广播地址即可。

int flag = 1;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,&flag,sizeof(flag));

广播地址:
子网广播:{子网ID, 255} 例如:192.168.9.255
受限广播(路由器不转发): 255.255.255.255

接收方无需额外设置,显示的源地址为发送方的单播地址。


多播/组播:

只设置接收方加入多播组,发送方发送时指定目标地址为多播组地址即可。
可以同时加入多个多播分组。
组播地址取值范围:224.0.0.0~239.255.255.255

/*
 *  基于多播的UDP接收程序
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//-------------------------------------------------------------------- 
#define MCAST "224.0.0.88"   // 多播组号
#define PORT   12345         // 本机端口号
//-------------------------------------------------------------------- 
/*
 * 加入多播组
 */
int mutilcast_join(int s, struct ip_mreq* mreq)
{
  int ret;
    
  // 设置多播的TTL值,如果转发的次数等于指定值,则不再转发
  int ttl = 10;
  ret = setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));
  if(ret < 0){
    perror("IP_MULTICAST_TTL");
    return -1;
  }
  // 设置数据是否发送到本地回环接口: 不发送
  int loop = 0;
  ret = setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));
  if(ret < 0){
    perror("IP_MULTICAST_LOOP");
    return -2;
  }
  
  // 加入多播组
  ret = setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,mreq,sizeof(struct ip_mreq));
  if(ret < 0){
    perror("IP_ADD_MEMBERSHIP");
    return -3;
  }     
  return 0;
}
//-------------------------------------------------------------------- 
/*
 * 离开多播组
 */
int mutilcast_leave(int s, struct ip_mreq* mreq)
{
    int ret;    
  ret = setsockopt(s,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(mreq));
  if(ret < 0){
    perror("IP_DROP_MEMBERSHIP");
  }   
  return ret;   
}
//-------------------------------------------------------------------- 
/*
 * 主程序入口
 */
int main(int argc,char*argv[])
{
  int s; 
  int ret;
  int size;
  char rxbuf[1024];            // 接收数据缓冲区
  char ipbuf[20];              // IP地址转字符串      
  struct sockaddr_in localaddr;    // 本机地址
  struct sockaddr_in fromaddr;     // 远端地址
  struct ip_mreq mreq;         // 多播参数
  
  // 建立udp套接字
  s = socket(AF_INET,SOCK_DGRAM,0);
  if(s < 0){
   perror("socket error");
    return -1;
  }
  
  // 绑定本机端口
  localaddr.sin_family = AF_INET;
  localaddr.sin_port   = htons(PORT);
  localaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  //绑定地址结构到套接字
  ret = bind(s,(struct sockaddr*)&localaddr,sizeof(localaddr));
  if(ret < 0){
    perror("bind error");
    return -1;
  }
              
  //加入多播组  
  mreq.imr_multiaddr.s_addr = inet_addr(MCAST);   //多播组的IP
  mreq.imr_interface.s_addr = htonl(INADDR_ANY);  //本机的默认接口IP
  mutilcast_join(s, &mreq);
    
  //循环接收多播组的消息
  while(1)
  {
    int len = sizeof(fromaddr);
    memset(rxbuf,0,sizeof(rxbuf));
    size = recvfrom(s,rxbuf,1024,0,(struct sockaddr*)&fromaddr,&len);
    if(size < 0){
      perror("recvfrom ");
      return -1;
    }
    
    // 显示收到的信息   
    printf("receive message from:%s:%d\r\n",
           inet_ntop(AF_INET, &fromaddr.sin_addr, ipbuf, sizeof(ipbuf)),
           ntohs(fromaddr.sin_port));
    printf("%s",rxbuf);
    
    // 向发送端发送回应
    size = sendto(s,"OK",2,0,(struct sockaddr*)&fromaddr,sizeof(fromaddr));
  }
  
  mutilcast_leave(s, &mreq);  // 离开多播组
  close(s);                   // 关闭套接字
  return 0;
}

应用场景:

  1. 对于无屏显示的设备,可以过多播模式,来接收客户机的命令,返回自己的IP地址。
  2. 可以将日志通过多播发送出来,监听端加入多播组即可接收日志。