网络编程(二)组播与广播

组播与广播网络编程详解

组播

概念简介

  • 组播定义:组播是一种点对多点的通信方式,数据包只发送给加入特定组播组的主机。

  • 组播地址范围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: UDPUDP 通信
绑定端口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: 目标组播地址发送端不必加入组播组
设置 TTLsetsockopt(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控制是否接收到自己发送的组播数据

使用流程

接收端:

  1. 创建 UDP 套接字

  2. 绑定本地端口(可用 INADDR_ANY

  3. 加入组播组(IP_ADD_MEMBERSHIP

  4. 调用 recvfrom() 接收数据

  5. 可选:离开组播组(IP_DROP_MEMBERSHIP

发送端:

  1. 创建 UDP 套接字

  2. 设置 TTL 和回环(可选)

  3. 直接发送数据到组播地址(不必加入组播组)


注意事项

  1. 接收端必须加入组播组,发送端不必加入。

  2. 默认使用主网卡,可通过 imr_interface 指定网卡。

  3. TTL 决定组播数据能否跨网段发送,默认值 1 表示仅在本地网段。

  4. 通过 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: UDPUDP 通信
绑定端口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

使用流程

接收端:

  1. 创建 UDP 套接字

  2. 绑定本地端口(可用 INADDR_ANY

  3. 调用 recvfrom() 接收广播数据

发送端:

  1. 创建 UDP 套接字

  2. 打开广播权限(SO_BROADCAST

  3. 指定广播地址并调用 sendto() 发送数据


注意事项

  1. 发送广播必须设置 SO_BROADCAST,否则发送失败。

  2. 接收端通常不需要做特殊配置,只需绑定端口。

  3. 广播数据通常局限在局域网内,路由器可能会阻止跨网段广播。

  4. 避免过度广播,以免占用网络带宽。

代码案例

 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值