(dev) Android, Datagrams (UDP), Broadcast, and Multicast ...
Posted on 2013-03-25, 37 comments, 129 +1's, imported from Google+/Chainfire

NOTICE: This content was originally posted to Google+, then imported here. Some formatting may be lost, links may be dead, and images may be missing.

Yesterday I wasted an hour or so trying to get communication to work between my Android app and some software on the PC. I am no networking guru but I generally know more than enough on the subject to get around, so I didn't expect any trouble. That'll teach me to code on a Sunday morning :)

Part of the protocol is discovery, and I was initially under the impression that the discovery protocol used the broadcast mechanism. Imagine my surprise at the app not picking up the PC's broadcasts.

It took me a while before I noticed that it wasn't a broadcast at all - it was a multicast (and a doh! reverberated around the block). One does not simply receive multicasts in Android.

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

You can use the MulticastSocket in the same way you would use a DatagramSocket . In fact, both use the same socket implementation class, MulticastSocket (being a descendant of DatagramSocket ) just exposes a few more methods to manipulate that implementation class.

As such, when in doubt, just use a MulticastSocket . The biggest practical difference between the two classes is that a MulticastSocket can send and receive multicasts, while a DatagramSocket can only send them. Both classes can send and receive both unicasts and broadcasts.

This is all rather trivial, just writing this up because some of the information I found on this yesterday was incorrect, incomplete, and/or unclear.

EDIT 2013-04-11:

After a lot more playing around, here's some additional info that might be helpful.

You may be tempted to broadcast simply to 255.255.255.255. That often works, but sometimes it does not. You should always specify the correct broadcast address for the interface you want to broadcast to.

Often broadcasting like this is meant for something on the local network, so you would be tempted to use WifiManager to get the Wi-Fi IP address, then use that IP address to get the broadcast address via NetworkInterface::getByInetAddress(). I must warn you though, that this will not work if the user connects to ethernet (yes, that happens), nor will it work if the Android device is providing a hotspot. Depending on your needs, you may want to consult NetworkInterface::getNetworkInterfaces() and examine all the entries. Beware that pretty much everything you will iterate/check can be null here!  

Broadcasting to 255.255.255.255 not working can occur at strange times. To give an example of just one specific device running one specific firmware, namely a Galaxy Note II running 4.1.2:

  • 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.

Similarly, you may be tempted to just multicast to standard multicast addresses as well. This usually works, but sometimes it does not. You can't really translate those address to an interface-specific address, so you need to specify the outgoing NetworkInterface manually by utilizing the MulticastSocket::setNetworkInterface() method. This call is not available in the DatagramSocket class, so even though that class is perfectly capable of sending multicasts, you still need to use the actual MulticastSocket class, or you cannot specify the target interface.

I can imagine if you read the above you might think that broadcasting to 255.255.255.255 might work correctly again if you use a MulticastSocket (as it is a descendant of DatagramSocket ) and specify the correct NetworkInterface - let me save you the trouble: no, that does not work, you still need to use the correct broadcast address to reliably broadcast packets.

These new things are also not strange once you've seen them documented or these issues occur, but you might not run into them during testing, as they often appear to work just fine, and the Android docs don't really seem to warn about this (though maybe I've read over it). Makes me wonder if it would actually be better if DatagramSocket would simply always refuse to broadcast to 255.255.255.255, and MulticastSocket would refuse to multicast unless a NetworkInterface is specified.

EDIT 2013-04-21:

Some more issues. If you're like me, you probably try to reduce memory and object allocations as much as possible. So if you're receiving UDP packets of maximum size 512 (for example), you might do something like this:

byte[] message = new byte[512];

DatagramPacket packet = new DatagramPacket(message, message.length);

while (true) {

socket.receive(packet);

...

}

This to constantly re-use the same buffer to receive packets into and parse them. Quite likely this will work fine on your test device. However, you should be aware that this construct does not work on all Android versions. On some versions this will limit your incoming packet data size to the previous packet size. So if you receive a 256 packet, followed by 384 byte packet, the latter packet will be cut off at 256 bytes. You need to insert a DatagramPacket::setData() (or ::setLenth() or whatever) call before DatagramSocket::receive() like this:

byte[] message = new byte[512];

DatagramPacket packet = new DatagramPacket(message, message.length);

while (true) {

packet.setData(message);

socket.receive(packet);

...

}

Again, sounds logical enough that you need to do this, but on the latest Nexus firmwares it works without doing this, and it isn't mentioned anywhere in the docs, so it can be a tricky bug to find ...

EDIT 2013-12-20:

Please note that on some devices on newer Android versions (like the HTC One on 4.3) you need to be the holder of a WifiManager.MulticastLock to be able to receive broadcasts (not just multicasts). You can send them just fine without the lock, though.

What a mess!

+1129
Sean M commented on 2013-03-25 at 16:00:

Thanks!

Bazz Freeman commented on 2013-03-25 at 16:02:

That's the beauty of Google. When you come to that point, you'll remember that it's somewhere on Google, do a search and end up back here.

When that happens, you can +1 this comment :)

Radha Shankar K commented on 2013-03-25 at 16:03:

Thank you ! I had  something similar in my mind, and I'm sure this will save me quite some time !

Kiran Rao commented on 2013-03-25 at 16:50:

On a related note, for discovery you could look at mDNS and its implementation in the .nsd package (introduced in API 16) or open source libraries like JmDNS.

Joe Philipps commented on 2013-03-25 at 16:54:

IIRC, IPv6 eliminated the explicit broadcast and turned everything that used to be a broadcast (in IPv4) into a multicast.  Ergo it's not surprising multicast seems to have some more "coverage" in Android.

BTW...I hope if you're programming apps with networking that you're also testing them in an IPv6 environment and make sure it works with both IPv4 and IPv6, preferrably preferring IPv6 as it's the protocol of the future Internet.  My guess is that unless you're writing something other than Dalvik code, Android or the system libraries upon which it depends handles most of the IPv6 and IPv6 preferring logic, so the only thing you would have to do explicitly is make sure your network has IPv6 working.

Me in particular, I have a 6in4 tunnel with Hurricane Electric (tunnelbroker.net).

Chainfire commented on 2013-03-25 at 17:11:

+Kiran Rao I didn't spec this protocol - I'm communicating with existing software - as such I have no control over this discovery process. But I'll keep it in mind if I'm ever developing such a thing from scratch.

+Joe Philipps I am doing IPv6 testing on my LAN, but it's also still running IPv4. I should really spend some time testing on v6-only to make sure it all works as expected. Soon, hopefully.

Alex Burton commented on 2013-03-25 at 18:13:

An hour? Lucky you last year I spent three days getting UDP working on Android. A good part of that was learning Java though...

Ibukun commented on 2013-03-25 at 22:34:

Just in time, thanks!

Pedro Prado commented on 2013-03-25 at 23:07:

Taught Multicast in the past. Keep in mind you can be wasting resources... Multicast joining means an IGMP periodic join to keep the joined state, a process you wouldn't need running for unicast. I can't say if the mcast socket would automatically fire the process or an actual join is needed...

Chainfire commented on 2013-03-25 at 23:43:

+Pedro Prado The Android docs warn of this (wasting of resources, not the "why" part), and as such I am of course only keeping the MulticastLock for the short period of time needed :)

Jezz Doobie commented on 2013-03-30 at 01:19:

Hangover induced code'in?

Richard Buchman commented on 2013-03-30 at 04:21:

Nice... :-)

Chainfire commented on 2013-04-11 at 11:15:

Added quite a bit more information :)

Julio Gonzalez commented on 2013-04-19 at 03:16:

Using the correct broadcast address works fine.

how do I get the correct address? :)

Pedro Prado commented on 2013-04-19 at 13:49:

+Julio Gonzalez, if you are referring to the Multicast addresses, this might help: http://en.wikipedia.org/wiki/Multicast_address

If it is really the broadcast address, you need to find the last IP address of your subnet.

E.g.: IP address = 192.168.1.10, subnet mask = 255.255.255.0, then broadcast = 192.168.1.255.

Maybe there is a call that will return you this information automatically...

Chainfire commented on 2013-04-21 at 15:50:

Minor addendum re: DatagramPacket buffers

Julio Gonzalez commented on 2013-04-25 at 00:05:

yeah, thanks, I ended up using an online calculator that I found, but that wasn't my problem, but thanks anyway! :D

Yahya Mohd commented on 2013-06-13 at 12:41:

تطبيق جد كما ذكر الاخوة المتابعين

Chainfire commented on 2013-12-20 at 11:03:

Addendum re: MulticastLock requirement to receive broadcasts in some cases

Tony Hoyle commented on 2013-12-20 at 13:26:

Don't be tempted to find the broadcast address by sticking .255 on the end!  Bitwise or the 32bit IP with the inverse of the netmask instead.. that'll work on all networks, not just those that use /24 subnets.

Multicast is better IMO as (given a half decent router) it crosses subnets on the LAN - my chromecast sits on my test subnet but shows up fine on my working one (which was a pleasant surprise when  I noticed).

Pedro Prado commented on 2013-12-20 at 13:56:

Yeap, it was just an common example. You need to AND the IP address with the netmask and set all remaining bits not covered by the netmask.

I wonder if there is a function call to retrieve that, as the broadcast address is listed on the ifconfig output...

Andrea Florio commented on 2013-12-22 at 08:24:

And what about igmp joins?

Chainfire commented on 2013-12-22 at 14:33:

+Tony Hoyle Keep in mind a number of (especially older) Android devices/firmwares have completely broken multicast - neither send nor receive actually works. I haven't seen that issue popup in quite a while though, so it's probably safe-ish to use now, but still something to remember.

Chainfire commented on 2013-12-22 at 14:44:

+Tony Hoyle +Pedro Prado You can actually get the broadcast address through NetworkInterface::getInterfaceAddresses()::getBroadcast() call.

Pedro Prado commented on 2013-12-22 at 15:15:

IGMP joins are actually IGMP report packets: you "report" that you are a member of the listeners of that particular group. They are also multicast messages so they may not work as stated above...

Andrea Florio commented on 2013-12-22 at 15:17:

That's bad and strange.. Ipv6 uses extensively Multicast... Is unacceptable that's not working

Fermin Calero commented on 2014-01-06 at 00:39:

espero estar cerca

Pedro Casagrande de Campos commented on 2014-03-29 at 10:39:

Thanks man, my application suddenly has stopped to synchronize on some networks, I used the setInterface and now it is working. Not sure what happened, I guess it is something related to ipv6.

Daniel Franklin commented on 2014-05-13 at 11:34:

I too have a strange issue with multicast socket on Android. I have an application to broadcast some data. All of a sudden the packets gets dropped from the application layer. The packets sent from java sockets doesn't even reach my Wifi Driver :( Wifi Data Capture shows no packets sent from device, but java socket send is successful. Same application I implemented using JNI with native sockets. It works like charm!! 

Pedro Prado commented on 2014-05-13 at 13:46:

Reading the edits regarding the rights, it seemed somewhat logical to me from the IGMP point of view:

IGMP is the protocol you use to tell the network that you want to RECEIVE multicasts, but it is not involved i SENDING. 

When you SEND, the network knows what to do with it just because there is a multicast destination address.

But when you want to RECEIVE, the network has only one way to know what you want - by receiving IGMP reports (the packets with the groups you want to listen; you "report" what groups you are listening to).

So if you change "Multicast" to "IGMP", it makes more sense: you can send anytime you want without IGMP, but for receiving, you need to setup IGMP first. HTH...

Meet Vora commented on 2015-09-24 at 17:03:

+Chainfire Nice description in clear language.

Can I know more about how to multicast in hotspot mode?

Meet Vora commented on 2015-09-24 at 17:19:

+Pedro Prado How to use IGMP?

Pedro Prado commented on 2015-09-25 at 04:28:

+Meet Vora IGMP is Internet Group Membership Protocol, the protocol you use to receive multicasts (no protocol is used for sending multicasts).

I don´t know how to implement programatically, I only work with it on network devices. But I would think you just need to use MulticastSocket::joinGroup() specifying the IP address of the multicast stream (group) you wish to receive. This is actually how your device will ask the network for a specific multicast stream.

Meet Vora commented on 2015-09-28 at 17:34:

+Pedro Prado Thanks for that information. But I would like to know that Is there any way how I can receive packet (via multicast) from other connected devices (which are connected to my Smart phone's ad-hoc connection)!!

[for more info: I tried to send\receive packet between two or more devices in a group, but it only works when they are connected in a WIFI ROUTER... instead of this I want to make one of my device's hotspot ON, and others can connect and can transmit\receive packet.. but this is not working... :( ]

Pedro Prado commented on 2015-09-29 at 14:57:

+Meet Vora Well, they all need to join the multicast group. This is required, because otherwise the NIC won´t accept frames destined for the L2 multicast addresses related to the groups.

Other than that, I have no idea if Ad-Hoc connections do support multicast, but I would say they probably do...

If you have a Chromecast, you could try making an ad-hoc connection to it. As far as I know it advertises itself via multicast. See if you can detect your Chromecast via Ad-Hoc on your phone´s youtube app...

nilay jain commented on 2016-02-23 at 07:50:

Is there any way to acquire multicast lock for ethernet

Arroon Bloom commented on 2016-07-05 at 06:06:

thank you very much

This post is over a month old, commenting has been disabled.