多播的地址是特定的,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组播:
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
if(wifi != null)
{
MulticastLock mcLock = 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; } }