程序猿问科比:你为什么这么成功?
科比:你知道洛杉矶早晨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;
}
应用场景:
- 对于无屏显示的设备,可以过多播模式,来接收客户机的命令,返回自己的IP地址。
- 可以将日志通过多播发送出来,监听端加入多播组即可接收日志。