广播,组播,单播

本文详细介绍了网络通信中的广播和组播概念。广播是将数据发送给局域网内所有主机,而组播仅发送给加入特定组的主机,以减少网络带宽消耗。文中通过实例展示了广播和组播的实现过程,包括创建套接字、设置发送选项、发送和接收数据等步骤,并提供了服务器和客户端的代码示例。
摘要由CSDN通过智能技术生成

一.广播

1.1概念

主页socket里面简绍的数据包发送方式只有一个接受方,称为单播

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

如果发给局域网中的部分主机,称为组播

注意:同一个套接字只能选择一种发送方式。

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

1.2广播地址

        以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址发到该地址的数据包被所有的主机接收,255.255.255.255在所有网段中都代表广播地址。

广播能发送给所有主机,因为广播的mac地址比较特殊,为全ff, ip地址也比较特殊,使用的是广播的ip地址,将这样的数据包先发送给交换机,然后由交换机发送给所有主机。局域网中每台主机都能收到这个数据包,以UDP为例,网卡收到这个数据包,链路层先校验,发现目的mac地址是广播地址,则可以通过,交给网络层,发现ip地址是广播的ip地址,则也允许通过,到达传输层,只要目的端口号符合,则就能叫到应用层处理。

1.3广播的流程

发送者:

创建套接字 socket( )

设置为允许发送广播权限 setsockopt( )

填充广播信息结构体 sockaddr_in

发送数据 sendto( )

接收者:

创建套接字 socket( )

填充广播信息结构体 sockaddr_in

将套接字与广播信息结构体绑定 bind( )

接收数据 recvfrom( )

1.4代码实现广播

代码说明:这个代码是实现一收一发,服务器收,客户端发,代码相比之前主页socket来说差不多就是改变了方式,设置了一个广播,如需要了解,请参考主页socket的文章,谢谢观看。

设置广播的方式:

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

服务器代码

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

#define ERRLOG(errmsg) do{\
        printf("%s:%s:%d --", __FILE__, __func__, __LINE__);\
        perror(errmsg);\
        exit(-1);\
    }while(0)

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        return -1;
    }

    //创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //填充广播信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    //端口号可以随便指定
    serveraddr.sin_port = htons(atoi(argv[2]));
    //ip地址必须是广播的ip地址
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    //绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len)){
        ERRLOG("bind error");
    }

    //定义结构体保存对方的信息
    struct sockaddr_in client_addr;
    memset(&client_addr, 0, sizeof(client_addr));
    socklen_t client_addr_len = sizeof(client_addr);

    char buff[128] = {0};
    //循环收数据
    while(1){
        if(-1 == recvfrom(sockfd, buff, 128, 0, (struct sockaddr *)&client_addr, &client_addr_len)){
            ERRLOG("recvfrom error");
        }
        printf("客户端[%s:%d]发来数据[%s]\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
        memset(buff, 0, 128);
    }

    close(sockfd);

    return 0;
}

客户端代码

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

#define ERRLOG(errmsg) do{\
        printf("%s:%s:%d --", __FILE__, __func__, __LINE__);\
        perror(errmsg);\
        exit(-1);\
    }while(0)

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        return -1;
    }
    //创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //填充广播信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    //端口号可以随便指定
    serveraddr.sin_port = htons(atoi(argv[2]));
    //ip地址必须是广播的ip地址
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    //设置允许发送广播
    int on = 1;
    if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))){
        ERRLOG("setsockopt error");
    }

    char buff[128] = {0};

    //循环发送数据
    while(1){
        fgets(buff, 128, stdin);
        buff[strlen(buff)-1] = '\0';
        if(-1 == sendto(sockfd, buff, 128, 0, (struct sockaddr *)&serveraddr, serveraddr_len)){
            ERRLOG("sendto error");
        }
        memset(buff, 0, 128);
    }

    return 0;
}

二.组播

2.1概念

单播方式只能发给一个接收方。广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)。

2.2 组播的地址

D类地址(组播地址)不分网络地址和主机地址,第1字节的前4位固定为1110

主播的地址范围:224.0.0.1 – 239.255.255.255

2.3组播的流程

发送者:

创建套接字 socket( )

填充组播信息结构体 sockaddr_in

发送数据 sendto( )

接收者:

创建套接字 scoket( )

填充组播信息结构体 sockaddr_in

将套接字与组播信息结构体绑定 bind( )

设置为加入多播组 setsockopt( )

接收数据 recvfrom( )

2.3设置加入多播组

//使用的下面的结构体
struct ip_mreqn {
    struct in_addr imr_multiaddr; /*多播组ip地址*/
    struct in_addr imr_address;   /*本地ip地址*/
    int            imr_ifindex;   /*接口索引 0表示任意接口*/
};

struct ip_mreqn  my_mreqn;
//填充
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &my_mreqn, sizeof(my_mreqn));

2.4代码实现

服务器(接收端):

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

#define ERRLOG(errmsg) do{\
        printf("%s:%s:%d --", __FILE__, __func__, __LINE__);\
        perror(errmsg);\
        exit(-1);\
    }while(0)

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        return -1;
    }

    //创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //填充广播信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    //端口号可以随便指定
    serveraddr.sin_port = htons(atoi(argv[2]));
    //ip地址是多播组的ip地址!!!!!
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    //绑定套接字与 组播信息结构体
    if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len)){
        ERRLOG("bind error");
    }

    //设置加入多播组
    struct ip_mreqn my_mreqn;
    memset(&my_mreqn, 0, sizeof(my_mreqn));
    my_mreqn.imr_multiaddr.s_addr = inet_addr(argv[1]);
    my_mreqn.imr_address.s_addr = inet_addr("192.168.60.109");
    my_mreqn.imr_ifindex = 0;//任意接口
    if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &my_mreqn, sizeof(my_mreqn))){
        ERRLOG("setsockopt error");
    }

    //定义结构体保存对方的信息
    struct sockaddr_in client_addr;
    memset(&client_addr, 0, sizeof(client_addr));
    socklen_t client_addr_len = sizeof(client_addr);

    char buff[128] = {0};
    //循环收数据
    while(1){
        if(-1 == recvfrom(sockfd, buff, 128, 0, (struct sockaddr *)&client_addr, &client_addr_len)){
            ERRLOG("recvfrom error");
        }
        printf("客户端[%s:%d]发来数据[%s]\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
        memset(buff, 0, 128);
    }

    close(sockfd);

    return 0;
}

客户端(发送发):

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

#define ERRLOG(errmsg) do{\
        printf("%s:%s:%d --", __FILE__, __func__, __LINE__);\
        perror(errmsg);\
        exit(-1);\
    }while(0)

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        return -1;
    }
    //创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //填充广播信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    //端口号可以随便指定
    serveraddr.sin_port = htons(atoi(argv[2]));
    //ip地址广播的地址
    //224.0.0.1 ~ 239.255.255.255
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    char buff[128] = {0};

    //循环发送数据
    while(1){
        fgets(buff, 128, stdin);
        buff[strlen(buff)-1] = '\0';
        if(-1 == sendto(sockfd, buff, 128, 0, (struct sockaddr *)&serveraddr, serveraddr_len)){
            ERRLOG("sendto error");
        }
        memset(buff, 0, 128);
    }

    return 0;
}

  • 23
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值