广播和组播(多播)

广播

概述

        广播(broadcast)是指封包在计算机网络中传输时,目的地址为网络中所有设备的一种传输方式。实际上,这里所说的“所有设备”也是限定在一个范围之中,称为“广播域”。并非所有的计算机网络都支持广播,例如 X.25 网络和帧中继都不支持广播,而且也没有在“整个互联网范围中”的广播。IPv6 亦不支持广播,广播相应的功能由多播代替。通常,广播都是限制在局域网中的,比如以太网或令牌环网络。因为广播在局域网中造成的影响远比在广域网中小得多。只有传输层协议是 UDP 时,才支持广播功能,因为 TCP 是端对端,广播是一对所有

广播地址

广播的 IP 地址是将 IP 地址中主机部分全部置为 1,即 xxx.xxx.xxx.255
局域网内发送广播:
例:
        IP:192.168.2.3
        netmask:255.255.255.0
        广播地址:192.168.2.255
        IP:192.168.2.3
        netmask:255.255.0.0
        广播地址:192.168.255.255
广播的优点:
        1.网络设备简单,维护简单,布网成本低廉 。
        2.由于服务器不用向每个客户机单独发送数据,所以服务器流量负载极低。
缺点:
        1.无法针对每个客户的要求和时间及时提供个性化服务。
        2.网络允许服务器提供数据的带宽有限,客户端的最大带宽=服务总带宽。例如有线电视的客户端的线路支持 100 个频道(如果采用数字压缩技术,理论上可以提供 500 个频道),即使服务商有更大的财力配置更多的发送设备、改成光纤主干,也无法超过此极限。也就是说无法向众多客户提供更多样化、更加个性化的服务。
        3.广播禁止在 Internet 宽带网上传输。
广播的编程步骤:
广播发送者:
        1.创建一个套接字(UDP)
        2.使能广播(开启套接字选项)
        3.使用 sendto 发送消息到广播地址(IP+端口号)
        4.也可以接收消息
        5.关闭套接字
广播接收者:
        1.创建一个套接字(UDP)
        2.使能广播(开启套接字选项)
        3.接收者从哪一个地址接收广播信息呢?绑定你要从哪一个地址接收广播信息(IP+ 端口)
        4.也可以使用 sendto 发送消息到广播地址(IP+端口)5.关闭套接字

        5.关闭套接字

程序实例

该实例程序一端负责向广播地址上发送消息,一端负责在广播地址上读取消息

消息发送端程序:

#include
#include
#include  /* See NOTES */
#include 
#include 
#include //为了使用 IPV4 地址结构体
#include 
#include 
#include 

//./main ip port
int main(int argc,char *argv[])
{
    if(argc != 3)
    {
        printf("please input ip + port!\n");
        return 0;
    }

    //1.创建一个 UDP 套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM,0); //创建一个 IPV4 的数据包套接字
    if(-1 == sockfd)
    {
        perror("create socket failed");
        exit(-1);
    }

    //2.使能广播
    int optval = 1; 
    int ret = setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,(void*)&optval,sizeof(optval));   //设置套接字的属性
    if(ret <= 0)
    {
        perror("setsockopt error");
    }

    //可以绑定本地的 socket 的地址
    //直接发送消息(定义一个接收消息的地址)
    struct sockaddr_in addr; //保存接收消息的地址(IP+port)
    memset(&addr,0,sizeof(struct sockaddr_in)); //清空结构体
    addr.sin_family = AF_INET;
    inet_aton(argv[1], &addr.sin_addr);
    addr.sin_port = htons(atoi(argv[2]));
    char buf[1024] = {"da jia dou shi liang zai!!!"};
    while(1)
    {
        sleep(1);
        memset(buf,0,1024);
        fgets(buf,1024,stdin);
        ret = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&addr,sizeof(addr)); //发送广播
        if(ret <= 0)
        {
            perror("sendto error");
        }
        printf("sendto size:%d\n",ret);
    }
    //关闭套接字
    shutdown(sockfd,SHUT_RDWR);
    close(sockfd);
    return 0;
}

消息读取端程序:

#include
#include
#include  /* See NOTES */
#include 
#include 
#include //为了使用 IPV4 地址结构体
#include 
#include 
#include 

//./main ip port
int main(int argc,char *argv[])
{
    if(argc != 3)
    {
        printf("please input ip + port!\n");
        return 0;
    }

    //1.创建一个 UDP 套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM,0); //创建一个 IPV4 的数据包套接字
    if(-1 == sockfd)
    {
        perror("create socket failed");
        exit(-1);
    }

    //2.使能广播
    int optval = 1; 
    int ret = setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,(void*)&optval,sizeof(optval));//设置套接字的属性
    if(ret == -1)
    {
        perror("setsockopt error");
    }
    //从指定的地址接收消息(定义一个接收消息的地址)
    struct sockaddr_in addr; //保存接收消息的地址(IP+port)
    memset(&addr,0,sizeof(struct sockaddr_in)); //清空结构体
    addr.sin_family = AF_INET;
    inet_aton(argv[1], &addr.sin_addr);
    addr.sin_port = htons(atoi(argv[2]));

    //绑定一个 IP 地址
    ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)); //可不绑定
    if(ret == -1)
    {
        perror("bind error");
        exit(-1);
    }
    printf("bind success\n");
    char buf[1024] = {0};
    struct sockaddr_in send_addr; //用来保存消息发送方的地址
    while(1)
    {
        memset(buf,0,1024);
        memset(&send_addr,0,sizeof(struct sockaddr_in)); //清空结构体
        socklen_t len = sizeof(send_addr);
        ret = recvfrom(sockfd,buf,1024,0,(struct sockaddr*)&send_addr,&len); //接收广播
        if(ret <= 0)
        {
            perror("recv from error");
        }
        
        printf("recv size:%d\n",ret);
        printf("recv data:%s\n",buf);
    }
    //关闭套接字
    shutdown(sockfd,SHUT_RDWR);
    close(sockfd);
    return 0;
}

组播(又称多播)

概述

        单播用于两个主机之间端对端的通信,例如我们的 TCP/UDP 消息通信,广播用于一个主机对整个局域网上所有主机的数据通信,单播和广播其实是两个极端,实际情况下,我们需要对网络上面一组特定的主机通信,这就是多播/组播,多播只有传输层协议为 UDP 的时候,才支持多播功 能,
        多播是指把信息同时传递给一组目的地址。它使用策略是最高效的,因为消息在每条网络链路上只需传递一次,而且只有在链路分叉的时候,消息才会被复制。与多播相比,常规的点到单点的传递被称作单播。当以单播的形式把消息传递给多个接收方时,必须向每个接收者都发送一份数据副本。由此产生的多余副本将导致发送方效率低下,且缺乏可扩展性。不过,许多流行的协议,例如 XMPP 用限制接收者数量的方法弥补了这一不足。
多播地址
        多播的地址是特定的,D 类 IP 地址,即 1110-多播组号
                224.0.0.0 ~ 239.255.255.255 之间的 IP 都能用作多播组号
        把上面的 IP 地址划分为:
                局部多播地址 224.0.0.0~224.0.0.255
                预留多播地址 224.0.1.0~238.255.255.255
                管理权限多播地址 239.0.0.0~239.255.255.255
多播优点:
        1.需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载。
        2.由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。IP 协议允许有 2 亿 6 千多万个(268435456)组播,所以其提供的服务可以非常丰富。
        3.此协议和单播协议一样允许在 Internet 宽带网上传输。
多播缺点:
        1.与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和 QOS 加以弥补。
        2.现行网络虽然都支持组播的传输,但在客户认证、QOS(指一个网络能够利用各种基础技 术,为指定的网络通信提供更好的服务能力,是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。)等方面还需要完善。
多播编程流程:
多播发送者(往多播地址发消息)
        (1).创建一个 UDP 套接字(socket)
        (2).可以绑定也可以不绑定(bind)
        (3).往多播组(多播地址 + 端口号)发送消息(sendto)
        (4).也可以接收消息(recvfrom)
        (5).关闭套接字(close)
多播接收者(从一个多播地址收消息)
        (1).创建一个 UDP 套接字
        (2).加入多播组(使用 setsockopt 函数设置)
        (3).绑定多播地址(从哪一个多播地址收消息)
        (4).接收多播组上面的消息(recvfrom)
        (5).也可以发送消息
        (6).关闭套接字

程序实例

该实例程序一端负责向多播地址上发送消息,一端负责在多播地址上读取消息
消息发送端程序:
#include
#include
#include  /* See NOTES */
#include 
#include 
#include //为了使用 IPV4 地址结构体
#include 
#include 
#include 

//./main ip port
int main(int argc,char *argv[])
{
    if(argc != 3)
    {
        printf("please input ip + port!\n");
        return 0;
    }

    //1.创建一个 UDP 套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM,0); //创建一个 IPV4 的数据包套接字
    if(-1 == sockfd)
    {
        perror("create socket failed");
        exit(-1);
    }

    //可以绑定本地的 socket 的地址
    //直接发送消息(定义一个接收消息的地址)
    struct sockaddr_in addr; //保存接收消息端地址信息(IP+port)
    memset(&addr,0,sizeof(struct sockaddr_in)); //清空结构体
    addr.sin_family = AF_INET;
    inet_aton(argv[1], &addr.sin_addr);
    addr.sin_port = htons(atoi(argv[2]));
    char buf[1024] = {0};
    while(1)
    {
        memset(buf,0,1024);
        fgets(buf,1024,stdin);
        int ret = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&addr,sizeof(addr));       //发送消息
        if(ret <= 0)
        {
            perror("sendto error");
        }
        printf("sendto size:%d\n",ret);
    }
    //关闭套接字
    shutdown(sockfd,SHUT_RDWR);
    close(sockfd);
    return 0;
}
消息接收端程序:
#include
#include
#include  /* See NOTES */
#include 
#include 
#include //为了使用 IPV4 地址结构体
#include 
#include 
#include 

//struct ip_mreq {
// struct in_addr imr_multiaddr; /* IP multicast group address */
// struct in_addr imr_address; /* IP address of local interface */
//};

//./main MultiIP Port

int main(int argc,char *argv[])
{
    if(argc != 3)
    {
        printf("please input ip + port!\n");
        return 0;
    }

    //1.创建一个 UDP 套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM,0); //创建一个 IPV4 的数据包套接字
    if(-1 == sockfd)
    {
        perror("create socket failed");
        exit(-1);
    }

    //2.加入指定的多播组
    //描述你要加入的多播组
    struct ip_mreq mreq;
    memset(&mreq,0,sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr(argv[1]); //多播地址
    //mreq.imr_interface = inet_addr(argv[2]); //本地网址
    mreq.imr_interface.s_addr = htonl(INADDR_ANY); //让内核自动选择一个网卡
    int ret = setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)&mreq,sizeof(mreq));     //设置套接字属性
    if(ret == -1)
    {
        perror("setsockopt error");
    }

    //3.绑定多播地址
    //从指定的多播地址接收消息(定义一个接收消息的地址)
    struct sockaddr_in addr; 
    memset(&addr,0,sizeof(struct sockaddr_in)); //清空结构体
    addr.sin_family = AF_INET;
    inet_aton(argv[1], &addr.sin_addr);
    addr.sin_port = htons(atoi(argv[2]));
    //绑定一个 IP 地址
    ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
    if(ret == -1)
    {
        perror("bind error");
        exit(-1);
    }
    printf("bind success\n");
    char buf[1024] = {0};
    struct sockaddr_in send_addr; //用来保存消息发送方的地址
    while(1)
    {
        memset(buf,0,1024);
        memset(&send_addr,0,sizeof(struct sockaddr_in)); //清空结构体
        socklen_t len = sizeof(send_addr);
        ret = recvfrom(sockfd,buf,1024,0,(struct sockaddr*)&send_addr,&len); //接收消息
        if(ret <= 0)
        {
            perror("recv from error");
        }
        printf("sendIP:%s,sendPort:%d\n",inet_ntoa(send_addr.sin_addr),ntohs(send_addr.sin_port));
        printf("recv data:%s\n",buf);
    }
    //关闭套接字
    shutdown(sockfd,SHUT_RDWR);
    close(sockfd);
    return 0;
}
在上面例子中接收多播消息要使用 setsockopt 函数将套接字加入多播组,使用 ip_mreq 结构体设置,其中 ip_mreq 结构体如下:
struct ip_mreq
{
    struct in_addr imr_multiaddr; /* IP multicast group address */
//多播组的 IP 地址(你要接收哪一个组上的信息)
    struct in_addr imr_address; /* IP address of local interface */
//本地的 IP 地址,多播组的数据包收发通过本地的哪一个网卡
};

你可能感兴趣的:(LINUX网络编程分享,网络,tcp/ip,unix)