网络编程 ----------- 4、组播与广播


1、广播 broadcast 

    广播是指向同一个网络中所有的主机传输数据只有传输层协议为 UDP协议时,才支持广播 
    TCP是端对端,广播是一对多 ,所以无法符合其要求。

    1)广播地址 

        广播地址的计算: 
                子网掩码取反 再和 ip地址 进行按位或运算 

                例子: 
                         ip : 192.168.31.104
                    netmask : 255.255.255.0 
                ===> 
                    广播地址: 192.168.31.255 

                         ip : 172.4.0.1 
                    netmask :255.255.254.0 
                ===> 
                    广播地址: 172.4.1.255 

                         ip : 192.168.31.104
                    netmask : 255.255.128.0 
                ===> 
                    广播地址: 192.168.127.255 

                全网广播地址: 255.255.255.255  
                    这个没有意义 --> 会造成网络风暴 


    2)广播的编程流程 (与UDP编程流程类似)

        广播的发送者
        广播接接收者 

    3)注意:设置套接字选项 


        谁需要用到广播, 谁就调用 setsockopt() 函数,设置套接字选项 (详细见表)

            使能广播: 
                级别:  SOL_SOCKET 
                选项:  SO_BROADCAST 
                类型:  int  
                             0  禁用 
                            非0 使能       


            写一个程序,实现 广播的发送和接收 

                broadcast_sender.c   /  broadcast_receiver.c 

                    broadcast_sender.c  发送者

                        int main( int argc, char *argv[] )
                        {
                            //创建套接字 UDP 
                            int sock_fd = socket( AF_INET, SOCK_DGRAM, 0 );
                            if( sock_fd == -1 )
                            {
                                perror( "socket error " );
                                return -1;
                            }
                            printf("sock_fd = %d\n", sock_fd );

                            //设置套接字的选项 --> 使能广播 
                            int n = 1;
                            setsockopt( sock_fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n) );

                            //设置广播的ip地址和端口号 
                            struct sockaddr_in  addr;
                            addr.sin_family = AF_INET;                    //协议族 ipv4 
                            addr.sin_port = htons( atoi(argv[2]) );      //端口号 
                            inet_aton( argv[1], &addr.sin_addr );       //广播地址 

                            //发送广播数据 
                            while( 1 )
                            {
                                //输入要发送的数据 
                                char buf[128] = {0};
                                printf("input data : ");
                                fgets( buf, sizeof(buf), stdin );

                                int re = sendto( sock_fd, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr) );
                                if( re == -1 )
                                {
                                    perror( "sendto error " );
                                    break;
                                }

                                //人为定义退出条件
                                if( buf[0] == '#' )
                                {
                                    break;
                                }
                            }

                            //关闭套接字 
                            close( sock_fd );
                        }
 
                    broadcast_receiver.c  接收者(服务器)

                        int main( int argc, char *argv[] )
                        {
                            //创建套接字 UDP 
                            int sock_fd = socket( AF_INET, SOCK_DGRAM, 0 );
                            if( sock_fd == -1 )
                            {
                                perror( "socket error " );
                                return -1;
                            }
                            printf("sock_fd = %d\n", sock_fd );

                            //设置套接字的选项 --> 使能广播 
                            int n = 1;
                            setsockopt( sock_fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n) );

                            //设置接收广播的ip地址和端口号 
                            struct sockaddr_in  addr;
                            addr.sin_family = AF_INET;              //协议族 ipv4 
                            addr.sin_port = htons( atoi(argv[2]) );  //端口号 
                            inet_aton( argv[1], &addr.sin_addr );    //广播地址 

                            //绑定套接字 
                            int re = bind( sock_fd, (struct sockaddr*)&addr, sizeof(addr) );
                            if( re == -1 )
                            {
                                perror( "bind error " );
                                close( sock_fd );
                                return -1;
                            }
                            printf("bind success\n");

                            //接收广播数据 
                            while( 1 )
                            {
                                //接收数据 
                                char buf[128] = {0};
                                struct sockaddr_in  from_addr;
                                socklen_t len = sizeof(from_addr);

                                re = recvfrom( sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&from_addr, &len );
                                if( re > 0 )
                                {
                                    printf("%s : %s\n", inet_ntoa(from_addr.sin_addr), buf );
                                }
                                else
                                {
                                    perror( "recvfrom error " );
                                    break;
                                }

                                //人为定义退出条件
                                if( buf[0] == '#' )
                                {
                                    break;
                                }
                            }

                            //关闭套接字 
                            close( sock_fd );
                        }

  2、组播  (多播) multicast  

    组播是指 将数据发送给 加入到某个组中的主机上 

    特点:

    1)只有传输层协议为 UDP协议时,才支持组播功能 

    2)组播地址 ipv4  D类地址 

        D类地址: 1110 + 多播组号(28bits)
                224.0.0.0 ~ 239.255.255.255  

    3)广播方式 占用带宽,会造成网络风暴 
        组播是一种折中的方式,只有加入到特定的 某个多播组的主机 才能收到数据 

    1)多播的代码实现 

            多播的发送者 
            多播的接收者 

    2)加入多播组 

        设置套接字的选项: 
            级别: IPPROTO_IP 
            选项: IP_ADD_MEMBERSHIP 
            类型: struct ip_mreqn {} 

            ======================================== 
            man 7 ip 进行查看 

                struct ip_mreqn 
                {
                    struct in_addr  imr_multiaddr;  /* 多播组地址(D类地址)  (类似于qq群号)IP multicast group address */
                    struct in_addr  imr_address;    /* 接口(网卡)的地址,多播的数据实际走哪个网卡(类似于qq号) IP address of local interface */
                    ...
                };

                struct in_addr
                {
                    uint32_t  s_addr;  /* 32位IP地址 */
                };            

            例子: 
                把 本机ip 加入多播组 224.0.0.1 

                struct ip_mreqn  mreq;
                mreq.imr_multiaddr.s_addr = inet_addr( "224.0.0.1" );  //多播组地址 
                mreq.imr_address.s_addr = htonl( INADDR_ANY );           //接口地址 
                    //mreq.imr_address.s_addr = inet_addr( argv[1] );  

                setsockopt( sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq) ); 

   注意: 
        要支持多播,需要设置路由表,让数据包从正确的网卡出去,而不是从默认网卡出去 

            首先 ifconfig 查看本机ip和网卡名字  (如: eth0,  eth1, ens33, ... )

                sudo route add -net 224.0.0.0 netmask 240.0.0.0  ens33          //加入路由表 
                sudo route add default gw 172.4.1.1  dev ens33                  //设置默认网关 

            查看内核的IP路由标 
                route -n  
       

练习: 
            写一个程序,实现多播的功能 

                multicast_sender.c   /  multicast_receiver.c 

                    multicast_sender.c  发送者
                    multicast_receiver.c  接收者

                        multicast_sender.c  发送者 

                            int main( int argc, char *argv[] )
                            {
                                //创建套接字 UDP 
                                int sock_fd = socket( AF_INET, SOCK_DGRAM, 0 );
                                if( sock_fd == -1 )
                                {
                                    perror( "socket error " );
                                    return -1;
                                }
                                printf("sock_fd = %d\n", sock_fd );

                                //设置 多播的ip和端口号 
                                struct sockaddr_in  addr;
                                addr.sin_family = AF_INET;                    //协议族 ipv4 
                                addr.sin_port = htons( atoi(argv[2]) );      //端口号 
                                inet_aton( argv[1], &addr.sin_addr );       //多播地址 224.0.0.1 

                                //发送数据
                                while(1)
                                {
                                    //发送数据 
                                    char buf[128] = {0};
                                    fgets( buf, sizeof(buf), stdin );

                                    int re = sendto( sock_fd, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr) );
                                    if( re == -1 )
                                    {
                                        perror( "sendto error " );
                                        break;
                                    }

                                    if( buf[0] == '#' )
                                    {
                                        break;
                                    }
                                }

                                //关闭套接字
                                close( sock_fd );
                            }

                        multicast_receiver.c  接收者 

                            int main( int argc, char *argv[] )
                            {
                                //创建套接字 UDP 
                                int sock_fd = socket( AF_INET, SOCK_DGRAM, 0 );
                                if( sock_fd == -1 )
                                {
                                    perror( "socket error " );
                                    return -1;
                                }
                                printf("sock_fd = %d\n", sock_fd );

                                //设置套接字的选项 --> 把本机ip加入到多播组 224.0.0.1  
                                struct ip_mreqn  mreq;
                                mreq.imr_multiaddr.s_addr = inet_addr( argv[1] );   //多播组地址 224.0.0.1 
                                mreq.imr_address.s_addr = htonl( INADDR_ANY );      //接口地址 (本机地址)

                                int re = setsockopt( sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq) ); 
                                if( re == -1 )
                                {
                                    perror("add membership error ");
                                    close( sock_fd );
                                    return -1;
                                }
                                printf("add membership success\n");


                                //设置 多播的ip和端口号 
                                struct sockaddr_in  addr;
                                addr.sin_family = AF_INET;                    //协议族 ipv4 
                                addr.sin_port = htons( atoi(argv[2]) );      //端口号 
                                inet_aton( argv[1], &addr.sin_addr );       //多播地址 224.0.0.1 

                                //设置端口号重用 
                                int n = 1;
                                setsockopt( server_sock, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n) );

                                //绑定套接字 
                                re = bind( sock_fd, (struct sockaddr*)&addr, sizeof(addr) );
                                if( re == -1 )
                                {
                                    perror( "bind error " );
                                    close( sock_fd );
                                    return -1;
                                }
                                printf("bind success\n");

                                //接收数据
                                while(1)
                                {
                                    //接收数据 
                                    char buf[128] = {0};
                                    struct sockaddr_in  from_addr;
                                    socklen_t len = sizeof(from_addr);

                                    re = recvfrom( sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&from_addr, &len );
                                    if( re > 0 )
                                    {
                                        printf("%s : %s\n", inet_ntoa(from_addr.sin_addr), buf );
                                    }
                                    else 
                                    {
                                         perror( "recvfrom error " );
                                         break;
                                    }

                                    //人为定义退出条件
                                    if( buf[0] == '#' )
                                    {
                                        break;
                                    }
                                }

                                //关闭套接字 
                                close( sock_fd );
                            }

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值