组播
概念简介
-
组播定义:组播是一种点对多点的通信方式,数据包只发送给加入特定组播组的主机。
-
组播地址范围:
224.0.0.0
~239.255.255.255
-
典型应用:
-
视频会议、在线直播
-
股票行情分发
-
多点传感器数据汇聚
-
核心结构体
1. 套接字地址结构体
struct sockaddr_in {
sa_family_t sin_family; // 地址族, AF_INET
uint16_t sin_port; // 16位端口号
struct in_addr sin_addr; // IP地址
char sin_zero[8];// 填充用, 不使用
};
2. 组播专用结构体
struct ip_mreq {
struct in_addr imr_multiaddr; // 组播组IP
struct in_addr imr_interface; // 本地接口IP
};
-
imr_multiaddr
:组播地址 -
imr_interface
:本地网卡 IP,通常可用INADDR_ANY
表示默认网卡
核心函数与选项
功能 | 函数/宏 | 参数说明 | 备注 |
---|---|---|---|
创建套接字 | socket(AF_INET, SOCK_DGRAM, 0) | AF_INET: IPv4, SOCK_DGRAM: UDP | UDP 通信 |
绑定端口 | bind(sock, (struct sockaddr*)&addr, sizeof(addr)) | addr: 本地 IP + 端口 | 接收组播必须绑定端口 |
加入组播 | setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) | mreq: struct ip_mreq | 接收组播前必须加入组播组 |
离开组播 | setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) | mreq: struct ip_mreq | 退出组播组 |
接收数据 | recvfrom(sock, buf, len, flags, (struct sockaddr*)&addr, &addrlen) | buf: 接收缓冲区 | 广播和组播接收通用 |
发送数据 | sendto(sock, buf, len, flags, (struct sockaddr*)&addr, addrlen) | addr: 目标组播地址 | 发送端不必加入组播组 |
设置 TTL | setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) | ttl: 数据跨网段跳数 | 默认 TTL=1(只在本地网段) |
设置回环 | setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) | loop: 0/1 | 控制是否接收到自己发送的组播数据 |
使用流程
接收端:
-
创建 UDP 套接字
-
绑定本地端口(可用
INADDR_ANY
) -
加入组播组(
IP_ADD_MEMBERSHIP
) -
调用
recvfrom()
接收数据 -
可选:离开组播组(
IP_DROP_MEMBERSHIP
)
发送端:
-
创建 UDP 套接字
-
设置 TTL 和回环(可选)
-
直接发送数据到组播地址(不必加入组播组)
注意事项
-
接收端必须加入组播组,发送端不必加入。
-
默认使用主网卡,可通过
imr_interface
指定网卡。 -
TTL
决定组播数据能否跨网段发送,默认值 1 表示仅在本地网段。 -
通过
IP_MULTICAST_LOOP
控制自己是否能收到自己发送的组播数据。
代码案例
sender
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#define DEST_IP_ADDR "224.1.1.1"
#define DEFT_PORT (8999)
int main(int argc, char const *argv[])
{
int sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (-1 == sockFd)
{
perror("socket failed!");
exit(EXIT_FAILURE);
}
char *str = "往前走吧";
struct sockaddr_in destAddr;
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(DEFT_PORT);
inet_pton(AF_INET, DEST_IP_ADDR, &destAddr.sin_addr.s_addr);
socklen_t addrlen = sizeof(destAddr);
int count = sendto(sockFd, str, strlen(str), 0, (const struct sockaddr *)&destAddr, addrlen);
if (-1 == count)
{
perror("sendto failed");
close(sockFd);
exit(EXIT_FAILURE);
}
close(sockFd);
return 0;
}
reciver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MULTICAST_IP_ADDR "224.1.1.1"
#define HOST_PORT (8999)
#define BUFF_SIZE (256)
int main(int argc, char const *argv[])
{
int sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (-1 == sockFd)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(HOST_PORT);
addr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrlen = sizeof(addr);
if (bind(sockFd, (const struct sockaddr *)&addr, addrlen))
{
perror("bind failed");
close(sockFd);
exit(EXIT_FAILURE);
}
struct ip_mreq optval;
optval.imr_interface.s_addr = INADDR_ANY;
inet_pton(AF_INET, MULTICAST_IP_ADDR, &optval.imr_multiaddr.s_addr);
int ret = setsockopt(sockFd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&optval, sizeof(struct ip_mreq));
if (ret)
{
perror("setsockopt failed!");
close(sockFd);
exit(EXIT_FAILURE);
}
char buffer[BUFF_SIZE] = "";
struct sockaddr_in sender_addr;
socklen_t sender_addr_len;
char sender_ip_addr_str[16] = "";
ssize_t count = recvfrom(sockFd,
buffer,
BUFF_SIZE,
0,
(struct sockaddr *)&sender_addr,
&sender_addr_len);
if (count > 0)
{
printf("Data from %s:%u, Data : %s\n",inet_ntop(AF_INET, &(sender_addr.sin_addr.s_addr), sender_ip_addr_str, 16),ntohs(sender_addr.sin_port),
buffer);
}
close(sockFd);
return 0;
}
广播
概念简介
-
广播定义:广播是一种点对所有主机的通信方式,数据包会发送到网络上的每一台主机。
-
广播地址:
-
局域网广播:
255.255.255.255
-
子网广播:如
192.168.1.255
-
-
典型应用:
-
局域网设备发现
-
自动配置与心跳检测
-
简单的点对多点消息通知
-
核心结构体
1. 套接字地址结构体
struct sockaddr_in {
sa_family_t sin_family; // 地址族, AF_INET
uint16_t sin_port; // 16位端口号
struct in_addr sin_addr; // IP地址
char sin_zero[8];// 填充用, 不使用
};
核心函数与选项
功能 | 函数/宏 | 参数说明 | 备注 |
---|---|---|---|
创建套接字 | socket(AF_INET, SOCK_DGRAM, 0) | AF_INET: IPv4, SOCK_DGRAM: UDP | UDP 通信 |
绑定端口 | bind(sock, (struct sockaddr*)&addr, sizeof(addr)) | addr: 本地 IP + 端口 | 接收广播通常需要绑定端口 |
打开广播权限 | setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) | opt=1 打开广播 | 必须在发送广播前设置 |
接收数据 | recvfrom(sock, buf, len, flags, (struct sockaddr*)&addr, &addrlen) | buf: 接收缓冲区 | 广播接收通用 |
发送数据 | sendto(sock, buf, len, flags, (struct sockaddr*)&addr, addrlen) | addr: 广播地址 | 发送广播必须打开 SO_BROADCAST |
使用流程
接收端:
-
创建 UDP 套接字
-
绑定本地端口(可用
INADDR_ANY
) -
调用
recvfrom()
接收广播数据
发送端:
-
创建 UDP 套接字
-
打开广播权限(
SO_BROADCAST
) -
指定广播地址并调用
sendto()
发送数据
注意事项
-
发送广播必须设置
SO_BROADCAST
,否则发送失败。 -
接收端通常不需要做特殊配置,只需绑定端口。
-
广播数据通常局限在局域网内,路由器可能会阻止跨网段广播。
-
避免过度广播,以免占用网络带宽。
代码案例
sender
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#define BOARDCAST_IP_ADDR "255.255.255.255"
#define BOARDCAST_PORT (8999)
int main(int argc, char const *argv[])
{
int sockFd = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
if(sockFd == -1)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
int broadcastEnable = 1;
if( setsockopt(sockFd, SOL_SOCKET, SO_BROADCAST,
&broadcastEnable, sizeof(broadcastEnable)))
{
perror("setsockopt failed");
close(sockFd);
exit(EXIT_FAILURE);
}
struct sockaddr_in destAddr;
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(BOARDCAST_PORT);
inet_pton(AF_INET,BOARDCAST_IP_ADDR,&destAddr.sin_addr.s_addr);
socklen_t addrlen = sizeof(destAddr);
char *str = "你应该为了自己而活";
int count = sendto(sockFd, (const void *)str,strlen(str), 0,
(const struct sockaddr *)&destAddr, addrlen);
if( -1 == count)
{
perror("sendto failed");
close(sockFd);
exit(EXIT_FAILURE);
}
return 0;
}
reciver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#define BOARDCAST_IP_ADDR "255.255.255.255"
#define BOARDCAST_PORT (8999)
#define BUFFSIZE 512
int main(int argc, char const *argv[])
{
int sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sockFd == -1)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(BOARDCAST_PORT);
inet_pton(AF_INET, BOARDCAST_IP_ADDR, &addr.sin_addr.s_addr);
socklen_t addrlen = sizeof(addr);
if (bind(sockFd, (const struct sockaddr *)&addr,
addrlen))
{
perror("bind failed");
exit(EXIT_FAILURE);
}
char buffer[BUFFSIZE] = "";
struct sockaddr_in sender_addr;
socklen_t sender_addr_len;
char sender_ip_addr_str[16] = "";
ssize_t count = recvfrom(sockFd,
buffer,
BUFF_SIZE,
0,
(struct sockaddr *)&sender_addr,
&sender_addr_len);
if (count > 0)
{
printf("Data from %s:%u, Data : %s\n",inet_ntop(AF_INET, &(sender_addr.sin_addr.s_addr), sender_ip_addr_str, 16),ntohs(sender_addr.sin_port),
buffer);
}
close(sockFd);
return 0;
}