网络编程-广播组播

广播地址: 主机号最大的地址;

以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址

● 前面介绍的数据包发送方式只有一个接受方,称为单播

● 如果同时发给局域网中的所有主机,称为广播

(同一局域网内的主机都会接收到,如果其他主机没有加入广播站,就会将消息丢弃)

一.广播 (UDP协议)

特点:

只有用户数据报(使用UDP协议)套接字才能广播

● 一般被设计为局域网搜索协议

函数接口

setsockopt 设置套接字的属性

头文件:
  #include<sys.socket.h>    
  #include<sys/types.h>      
  #include<sys/time.h>
  
原型:  int setsockopt(int sockfd,int level,int optname,\
                         void *optval,socklen_t optlen)
功能: 获得/设置套接字属性
参数:
     sockfd:套接字描述符
     level:协议层
     optname:选项名
     optval:选项值
     optlen:选项值大小

返回值:  成功 0    失败-1

socket属性

选项名称

说明

数据类型

==== SOL_SOCKET 应用层 ====

SO_BROADCAST

允许发送广播数据

int

SO_DEBUG

允许调试

int

SO_DONTROUTE

不查找路由

int

SO_ERROR

获得套接字错误

int

SO_KEEPALIVE

保持连接

int

SO_LINGER

延迟关闭连接

struct linger

SO_OOBINLINE

带外数据放入正常数据流

int

SO_RCVBUF

接收缓冲区大小

int

SO_SNDBUF

发送缓冲区大小

int

SO_RCVLOWAT

接收缓冲区下限

int

SO_SNDLOWAT

发送缓冲区下限

int

SO_RCVTIMEO

接收超时

struct timeval

SO_SNDTIMEO

发送超时

struct timeval

SO_REUSEADDR

允许重用本地地址和端口

int

SO_TYPE

获得套接字类型

int

SO_BSDCOMPAT

与BSD系统兼容

int

==== IPPROTO_IP IP层/网络层 ====

IP_HDRINCL

在数据包中包含IP首部

int

IP_OPTINOS

IP首部选项

int

IP_TOS

服务类型

int

IP_TTL

生存时间

int

IP_ADD_MEMBERSHIP

将指定的IP加入多播组

struct ip_mreq

==== IPPRO_TCP 传输层 ====

TCP_MAXSEG

TCP最大数据段的大小

int

TCP_NODELAY

不使用Nagle算法

int

端口与地址复用:

  int opt = 1;
 setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

开通广播权限:

int optval =1;
setsockopt(sockfd,SOL_SOCKKET,SO_BROADCAST,&optval,sizeof(optval));

广播代码流程

广播的发送者

1. 创建用户数据报套接字;

sockfd = socket(AF_INET,SOCK_DGRAM,0);

2.setsockopt可以设置套接字属性,先设定该套接字允许发送广播

int optval = 1;

// SOL_SOCKET 传输层 SO_BROADCAST 允许发送广播

setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeo(optval));

3. 接收方地址指定为广播地址,指定端口号

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(atoi(argv[2]));

addr.sin_addr.s_addr = inet_addr(argv[1]);

4. 发送数据包

sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&addr,sizeof(addr));

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        fprintf(stderr, "用法: %s <广播地址> <端口>\n", argv[0]);
        return -1;
    }

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if (sockfd < 0)
    {
        perror("socket错误");
        return -1;
    }

    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));

    struct sockaddr_in broadcast_addr;
    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_port = htons(atoi(argv[2]));
    broadcast_addr.sin_addr.s_addr = inet_addr(argv[1]);

    char buf[128] = "";
    while (1)
    {
        sendto(sockfd, buf, sizeof(buf), 0,
               (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr));

        printf("广播消息已发送。\n");
        sleep(2); // 根据需要调整延迟
    }

    close(sockfd);
    return 0;
}

广播的接收者:

(基本无需改动)

1. 创建用户数据报套接字

sockfd = socket(AF_INET,SOCK_DGRAM,0);

2. 绑定IP地址(广播IP或0.0.0.0)和端口

struct sockaddr_in saddr,caddr;

saddr.sin_family = AF_INET;

saddr.sin_port = htons(atoi(argv[2]));

saddr.sin_addr.s_addr = inet_addr("0.0.0.0");

//广播地址或0.0.0.0

//0.0.0.0 是一个特殊的IP地址,用于表示服务器端将监听所有可用的网络接口

// 而不仅仅是IP地址,广播地址也会监听。

socklen_t len = sizeof(caddr);

bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));

3. 等待接收数据

recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&caddr,&len);

//填充结构体
struct sockaddr_in saddr,caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
//接收者需要加入广播站
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
int len = sizeof(caddr);

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "用法: %s <端口>\n", argv[0]);
        return -1;
    }

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if (sockfd < 0)
    {
        perror("socket错误");
        return -1;
    }

    struct sockaddr_in local_addr;
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(atoi(argv[1]));
    local_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
    {
        perror("bind错误");
        return -1;
    }

    char buf[128];
    struct sockaddr_in sender_addr;
    socklen_t sender_len = sizeof(sender_addr);

    while (1)
    {
        int recv_bytes = recvfrom(sockfd, buf, sizeof(buf), 0,
                                  (struct sockaddr *)&sender_addr, &sender_len);
        if (recv_bytes < 0)
        {
            perror("接收错误");
            return -1;
        }

        buf[recv_bytes] = '\0';
        printf("来自IP: %s  端口: %d  消息: %s\n", inet_ntoa(sender_addr.sin_addr),
               ntohs(sender_addr.sin_port), buf);
    }

    close(sockfd);
    return 0;
}

广播的缺点:

广播方式发给所有的主机,过多的广播会大量的占用网络带宽,造成广播风暴,影响正常的通信

二.组播(多播)

特点:

  1. 单播方式只能发给一个接收方
  2. 组播是一个人发送,加入到多播组的主机接收数据
  3. 多播方式既可以发给多个主机,又能避免像广播一样造成过多的负载

组播的地址:

IP的二级划分中 D类IP:

第一字节的前四位固定为 1110

D类IP : 224.0.0.1 - 239.255.255.255

组播的流程

组播的接收者:

1. 创建用户数据报套接字 UDP

2. 加入多播组 : 接收者加入多播组

3. 绑定组播IP地址(绑定组播IP或0.0.0.0)和端口

4. 等待接收数据

//多播结构体

struct ip_mreq{

struct in_addr imr_multiaddr; //指定多播组IP

struct in_addr imr_interface; //本地IP,通常指定为 INADDR_ANY--0.0.0.0

}

struct in_addr{

_be32 s_addr; //IP地址(大端)

}

//核心代码 ------------------------------------
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(argv[1]); //填充多播组
mreq.imr_interface.s_addr = inet_addr("0.0.0.0");  //自动获取本机IP
//改变套接字属性
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq));
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        fprintf(stderr, "用法: %s <组播地址> <端口>\n", argv[0]);
        return -1;
    }

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if (sockfd < 0)
    {
        perror("socket错误");
        return -1;
    }

    struct sockaddr_in multicast_addr;
    multicast_addr.sin_family = AF_INET;
    multicast_addr.sin_port = htons(atoi(argv[2]));
    multicast_addr.sin_addr.s_addr = inet_addr(arg v[1]);

    // 将套接字加入到组播组
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
    mreq.imr_interface.s_addr = INADDR_ANY;

    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt错误");
        return -1;
    }

    char buf[128];
    struct sockaddr_in sender_addr;
    socklen_t sender_len = sizeof(sender_addr);

    while (1)
    {
        int recv_bytes = recvfrom(sockfd, buf, sizeof(buf), 0,
                                  (struct sockaddr *)&sender_addr, &sender_len);
        if (recv_bytes < 0)
        {
            perror("接收错误");
            return -1;
        }

        buf[recv_bytes] = '\0';
        printf("来自IP: %s  端口: %d  消息: %s\n", inet_ntoa(sender_addr.sin_addr),
               ntohs(sender_addr.sin_port), buf);
    }

    close(sockfd);
    return 0;
}

组播的发送者:

1. 创建用户数据报套接字

2. 指定接收方地址指定为组播地址(224.0.0.1 - 239.255.255.255都可以)

指定接收端端口信息

3. 发送数据包

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    if (argc != 4)
    {
        fprintf(stderr, "用法: %s <组播地址> <端口> <消息>\n", argv[0]);
        return -1;
    }

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if (sockfd < 0)
    {
        perror("socket错误");
        return -1;
    }

    struct sockaddr_in multicast_addr;
    multicast_addr.sin_family = AF_INET;
    multicast_addr.sin_port = htons(atoi(argv[2]));
    multicast_addr.sin_addr.s_addr = inet_addr(argv[1]);

    char buf[128];
    snprintf(buf, sizeof(buf), "%s", argv[3]);

    while (1)
    {
        sendto(sockfd, buf, strlen(buf), 0,
               (struct sockaddr *)&multicast_addr, sizeof(multicast_addr));

        printf("消息已发送。\n");
        sleep(2); // 根据需要调整延迟
    }

    close(sockfd);
    return 0;
}

组播和广播的区别:

● 单播方式只能发给一个接收方。

广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响通信。

组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。

组播方式既可以发给多个主机,又能避免像广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值