2014년 2월 27일 목요일

Android multicast packet send, receive

Android에서 multicast packet 전송, 수신을 다룰 때 특별한 권한 및 유의 사항이 있음.

[Multicast sending]

- DatagramSocket을 사용 (broadcast 설정, 올바른 port 설정)

channel = DatagramChannel.open();
channel.socket().setReuseAddress(true);
channel.socket().setBroadcast(true);
channel.socket().setSendBufferSize(UDP_BUFFER_SIZE);

- 올바른 주소로 DatagramPacket 전송
  : wifiap (hotspot)일 경우 broadcast 주소(interface info에 있는)로 전송
  : 그 외 multicast 주소로 전송

아래 [multicast sending, receiving mini sample] 참고


[Multicast receiving]

- WifiManager.MulticastLock 설정 및 CHANGE_WIFI_MULTICAST_STATE permission

아래 [multicast 수신 설정] 참고

- MulticastSocket 사용 및 올바른 그룹주소와 interface에 대해 joinGroup()

InetSocketAddress socketAddress = new InetSocketAddress(addr, port);
socket = new MulticastSocket(port);
socket.setReceiveBufferSize(bufsize);

Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();

while (ifs.hasMoreElements()) {
  NetworkInterface xface = ifs.nextElement();
  Enumeration<InetAddress> addrs = xface.getInetAddresses();
  String name = xface.getName();

  if (false == name.startsWith("wlan")) {
  continue;
}

Log.e(LOG_TAG, "Listener Adding " + name + " to our interface set");
socket.joinGroup(socketAddress, xface);



참고 링크들

[multicast 사용 시 주의 점 및 address 지정 내용]
: https://plus.google.com/+Chainfire/posts/9NMemrKYnCd

To receive multicasts you need to do the following:
- use a MulticastSocket instead of a DatagramSocket
- MulticastSocket::joinGroup() the correct group
- hold a WifiManager.MulticastLock
- have the CHANGE_WIFI_MULTICAST_STATE permission

- Cell data and Wi-Fi connected: 255.255.255.255 works
- Cell data and Wi-Fi in hotspot mode: 255.255.255.255 works
- only Wi-Fi in hotspot mode: 255.255.255.255 doesn't work, throws network unreachable exception. Using the correct broadcast address works fine.



[multicast 수신 설정]
: http://codeisland.org/2012/udp-multicast-on-android/

Acquiring a lock
Actually getting the required lock isn’t that much trouble at all. Given that you declared your application to use the following permissions:

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

The code-part is as short as:

WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
if(wifi != null){
    WifiManager.MulticastLock lock = wifi.createMulticastLock("Log_Tag");
    lock.acquire();
}

[WifiManager.MulticastLock]
: http://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock.html

Allows an application to receive Wifi Multicast packets. Normally the Wifi stack filters out packets not explicitly addressed to this device. Acquring a MulticastLock will cause the stack to receive packets addressed to multicast addresses. Processing these extra packets can cause a noticable battery drain and should be disabled when not needed.


[multicast sending, receiving mini sample]
: https://code.google.com/p/boxeeremote/wiki/AndroidUDP

Getting the Broadcast Address

You need to access the wifi manager to get the DHCP info and construct a broadcast address from that:
InetAddress getBroadcastAddress() throws IOException {
    WifiManager wifi = mContext.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    // handle null somehow

    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
      quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}


* 근데 하다 보니 DhcpInfo 의 값이 모두 0인 경우가 있어서
interface 정보에서 broadcast 주소 획득하여 사용.

List<InterfaceAddress> addrs = xface.getInterfaceAddresses();String name = xface.getName();

if (false == name.startsWith("wlan")) { continue;}

for(InterfaceAddress addr : addrs){ if(addr.getAddress() instanceof Inet6Address){ continue;} Log.d(LOG_TAG, name + " has addr " + addr.getBroadcast());}



Sending and Receiving UDP Broadcast Packets

Having constructed the broadcast address, things work as normal. The following code would send the string data over broadcast and then wait for a response:
DatagramSocket socket = new DatagramSocket(PORT);
socket.setBroadcast(true);
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
    getBroadcastAddress(), DISCOVERY_PORT);
socket.send(packet);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);

[broadcast 주소 획득]
: http://stackoverflow.com/questions/15524593/broadcasting-over-wi-fi-direct

Try getting you network connection this way:
InetAddress getBroadcastAddress() throws IOException {
    WifiManager wifi = mContext.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    // handle null somehow

    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
      quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}  
Then try sending a packet this way:
DatagramSocket socket = new DatagramSocket(PORT);
socket.setBroadcast(true);
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
    getBroadcastAddress(), PORT);
socket.send(packet);

// If you want to listen for a response ...
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
Edit: From same page to read try this ...
WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("dk.aboaya.pingpong");
lock.acquire();
serverSocket = new DatagramSocket(19876);
serverSocket.setSoTimeout(15000); //15 sec wait for the client to connect
byte[] data = new byte[UDPBatPositionUpdater.secretWord.length()]; 
DatagramPacket packet = new DatagramPacket(data, data.length);
serverSocket.receive(packet);
lock.release();
String s = new String(packet.getData());
System.out.println(s);
Remember, that you need the following permission for it to work:
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
share|improve this answer

댓글 없음:

댓글 쓰기