网络组播技术

多播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:


局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。

预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。
管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

一、Linux上的实现


1.1 Linux下多播数据接收端的基本步骤:
多播程序框架主要包含套接字初始化、设置多播超时时间、加入多播组、发送数据、接收数据以及从多播组中离开几个方面。其步骤如下:
(1)建立一个socket。
(2)然后设置多播的参数,例如超时时间TTL、本地回环许可LOOP等。
(3)加入多播组。
(4)发送和接收数据。
(5)从多播组离开。

多播组的IP地址为224.0.0.88,端口为8888,当客户端接收到多播的数据后将打印出来。客户端只有在加入多播组后才能接受多播组的数据,因此多播接收端在接收多播组的数据之前需要先加入多播组,当接收完毕后要退出多播组。

/* 
*broadcast_client.c - 多播的客户端 
*/ 
#define MCAST_PORT 8888; 
#define MCAST_ADDR "224.0.0.88" /*一个局部连接多播地址,路由器不进行转发*/ 
#define MCAST_INTERVAL 5 /*发送间隔时间*/ 
#define BUFF_SIZE 256 /*接收缓冲区大小*/ 
int main(int argc, char*argv[]) 
{ 
int s; /*套接字文件描述符*/ 
struct sockaddr_in local_addr; /*本地地址*/ 
int err = -1; 

s = socket(AF_INET, SOCK_DGRAM, 0); /*建立套接字*/ 
if (s == -1) 
{ 
perror("socket()"); 
return -1; 
} 

/*初始化地址*/ 
memset(&local_addr, 0, sizeof(local_addr)); 
local_addr.sin_family = AF_INET; 
local_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
local_addr.sin_port = htons(MCAST_PORT); 

/*绑定socket*/ 
err = bind(s,(struct sockaddr*)&local_addr, sizeof(local_addr)) ; 
if(err < 0) 
{ 
perror("bind()"); 
return -2; 
} 

/*设置回环许可*/ 
int loop = 1; 
err = setsockopt(s,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop)); 
if(err < 0) 
{ 
perror("setsockopt():IP_MULTICAST_LOOP"); 
return -3; 
} 

struct ip_mreq mreq; /*加入广播组*/ 
mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); /*多播地址*/ 
mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*网络接口为默认*/ 
/*将本机加入广播组*/ 
err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof 
(mreq)); 
if (err < 0) 
{ 
perror("setsockopt():IP_ADD_MEMBERSHIP"); 
return -4; 
} 

int times = 0; 
int addr_len = 0; 
char buff[BUFF_SIZE]; 
int n = 0; 
/*循环接收广播组的消息,5次后退出*/ 
for(times = 0;times<5;times++) 
{ 
addr_len = sizeof(local_addr); 
memset(buff, 0, BUFF_SIZE); /*清空接收缓冲区*/ 
/*接收数据*/ 
n = recvfrom(s, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr, 
&addr_len); 
if( n== -1) 
{ 
perror("recvfrom()"); 
} 
/*打印信息*/ 
printf("Recv %dst message from server:%sn", times, buff); 
sleep(MCAST_INTERVAL); 
} 

/*退出广播组*/
err = setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof 
(mreq)); 

close(s); 
return 0; 
}



//选项IP_ADD_MEMBERSHIP用于加入某个多播组,之后就可以向这个多播组发送数据或者从多播组接收数据。此选项的值为mreq结构,成员imn_multiaddr是需要加入的多播组IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。


1.2 多播数据发送端

下面的例子持续向多播IP地址"224.0.0.88"的8888端口发送数据"BROADCAST TEST DATA",每发送一次间隔5s。

/*    
*broadcast_server.c - 多播服务程序    
*/    
#define MCAST_PORT 8888;    
#define MCAST_ADDR "224.0.0.88"/    /*一个局部连接多播地址,路由器不进行转发*/    
#define MCAST_DATA "BROADCAST TEST DATA"            /*多播发送的数据*    
#define MCAST_INTERVAL 5                            /*发送间隔时间*/    
int main(int argc, char*argv)    
{    
    int s;    
    struct sockaddr_in mcast_addr;         
    s = socket(AF_INET, SOCK_DGRAM, 0);         /*建立套接字*/    
    if (s == -1)    
    {    
        perror("socket()");    
        return -1;    
    }    
            
    memset(&mcast_addr, 0, sizeof(mcast_addr));/*初始化IP多播地址为0*/    
    mcast_addr.sin_family = AF_INET;                /*设置协议族类行为AF*/    
    mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);/*设置多播IP地址*/    
    mcast_addr.sin_port = htons(MCAST_PORT);        /*设置多播端口*/    
            
                                                    /*向多播地址发送数据*/    
    while(1) {    
        int n = sendto(s,                           /*套接字描述符*/    
                                    MCAST_DATA,     /*数据*/    
                                    sizeof(MCAST_DATA),    /*长度*/    
                                    0,    
                                    (struct sockaddr*)&mcast_addr,    
                                    sizeof(mcast_addr)) ;    
        if( n < 0)    
        {    
            perror("sendto()");    
            return -2;    
        }          
                
        sleep(MCAST_INTERVAL);                          /*等待一段时间*/
    }    
            
    return 0;    
}


二、android WIFI组播:


2.1. 打开权限

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

<uses-permissionandroid:name="android.permission.INTERNET"/>

2.2 打开组播锁,默认时,移动设备为了省电是关闭的
WifiManagerwifi=(WifiManager)getSystemService(Context.WIFI_SERVICE);
if(wifi!=null)
{
MulticastLockmcLock=wifi.createMulticastLock("mylock");
mcLock
.acquire();
...


public class MulticastDemoActivity extends Activity { 
    
    MulticastLock multicastLock; 
    
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        
        allowMulticast(); 
        
        try { 
            NetUtil.findServerIpAddress(); 
        } catch (IOException e) { 
            throw new RuntimeException(e); 
        } 
        
        Log.d("multicast.demo", "find ip ok."); 
        
        multicastLock.release(); 
    } 
    
    private void allowMulticast(){ 
        WifiManager wifiManager=(WifiManager)getSystemService(Context.WIFI_SERVICE); 
        multicastLock=wifiManager.createMulticastLock("multicast.test"); 
        multicastLock.acquire(); 
    } 
}


使用组播发送报文和接收ip地址信息的工具类代码:

public class NetUtil { 
    
    private static final String TAG="Net.Utils"; 
    private static final int MULTICAST_PORT=5111; 
    private static final String GROUP_IP="224.5.0.7"; 
    private static byte[] sendData; 
    
    static{ 
        sendData=new byte[4]; 
        // 0xEE78F1FB 
        sendData[3] = (byte) 0xEE; 
        sendData[2] = (byte) 0×78; 
        sendData[1] = (byte) 0xF1; 
        sendData[0] = (byte) 0xFB; 
    } 
    
    public static String findServerIpAddress() throws IOException{ 
        String ip=null;

        MulticastSocket multicastSocket=new MulticastSocket(MULTICAST_PORT); 
        multicastSocket.setLoopbackMode(true); 
        InetAddress group = InetAddress.getByName(GROUP_IP); 
        multicastSocket.joinGroup(group); 
        
        DatagramPacket packet=new DatagramPacket(sendData, sendData.length,group,MULTICAST_PORT); 
        
        for(;;){ 
            multicastSocket.send(packet); 
            Log.d(TAG,">>>send packet ok"); 
            
            byte[] receiveData=new byte[256]; 
            packet=new DatagramPacket(receiveData, receiveData.length); 
            multicastSocket.receive(packet); 
            
            String packetIpAddress=packet.getAddress().toString(); 
            packetIpAddress=packetIpAddress.substring(1, packetIpAddress.length()); 
            Log.d(TAG,"packet ip address: "+packetIpAddress); 
            
            StringBuilder packetContent=new StringBuilder(); 
            for(int i=0;i<receiveData.length;i++){ 
                if(receiveData[i]==0){ 
                    break; 
                } 
                packetContent.append((char)receiveData[i]); 
            } 
            ip=packetContent.toString(); 
            Log.d(TAG,"packet content ip is: "+ip); 
            
            if(ip.equals(packetIpAddress)){ 
                Log.d(TAG,"find server ip address: "+ip); 
                break; 
            }else{ 
                Log.d(TAG,"not find server ip address, continue …"); 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e) { 
                } 
            } 
        } 
        
        return ip; 
    } 
}



http://tech.ddvip.com/2013-05/1368542484195543.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值