Reputation: 791
I am trying to develop an app that sends some broadcast messages and receives some answers from the other android devices. I am having some trouble receiving the UDP messages from the other devices. I should mention that this code worked on Gingerbread but on JellyBean it's not working anymore and I do not know what might be the problem.
Here is where I send the broadcast message (I know the other devices listen on port 5000) :
private void sendUDPMessage(String msg) {
try {
DatagramSocket clientSocket = new DatagramSocket();
clientSocket.setBroadcast(true);
InetAddress address = InetAddress.getByName(Utils.getBroadcastAddress());
byte[] sendData;
sendData = msg.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData,
sendData.length, address, 5000);
clientSocket.send(sendPacket);
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
And here is where I receive it :
private void start_UDP()
{
try {
serverSocketUDP = new DatagramSocket(5000);
}
catch (Exception e) {
Log.i(LOGTAG, "Exception opening DatagramSocket UDP");
}
final byte[] receiveData = new byte[1024];
while(runningUDP) {
Log.d(LOGTAG, "Waiting for Broadcast request in ServerUDP.");
final DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocketUDP.receive(receivePacket);
byte[] sendData = new byte[1024];
InetAddress address = receivePacket.getAddress();
int port = receivePacket.getPort();
if(!receivePacket.getAddress().getHostAddress().equals(Utils.getLocalIpAddress()))
{
String req = new String(receivePacket.getData(), 0, receivePacket.getLength());
Log.d(LOGTAG, "Received UDP message : "+req+" from: "+receivePacket.getAddress().getHostAddress());
}
}// while ends
}//method ends
I should mention that these 2 functions are separate in 2 different threads so I can send and receive simultaneously.
I also acquire the following locks:
powerManager =(PowerManager)context.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK ,LOGTAG); // PARTIAL_WAKE_LOCK Only keeps CPU on
wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
wifiLock = wifiManager.createWifiLock(3, LOGTAG);
multicastLock = wifiManager.createMulticastLock(LOGTAG);
wakeLock.acquire();
multicastLock.acquire();
wifiLock.acquire();
And the permissions on the Manifest file :
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
I have tested if the messages are sent using wireshark and tcpdump and they are sent. Moreover, what is even more strange, I receive the broadcast messages that I send (but I discard them because I dont need to process the messages sent from myself) but I dont receive the broadcast messages sent from the other devices (which should have the same format, only the source address would be different and the message contained, either way should not affect the broadcast message).
Please let me know if you have any ideas because I really ran out of anything else I could try. Any help would be appreciated. Thanks!
EDIT: I have made some tests and even if when I run on each of the phones ifconfig wlan0 and it says something like
ifconfig wlan0
wlan0: ip 169.254.17.28 mask 255.255.0.0 flags [up broadcast multicast]
which means that the interface is active and the IP is set and can receive broadcast messages and multicast msgs but when I use
InetAddress in=InetAddress.getByName("169.254.17.28");
if (in.isReachable(1000))
Log.i(LOGTAG, "host is reachable");
else
Log.i(LOGTAG, "host is not reachable");
It shows in the logs host is not reachable.
This is where I turn on the Wi-fi
private void startWifiAdhoc() {
WifiManager wifiManager = (WifiManager)SharingFileService.context.getSystemService(Context.WIFI_SERVICE);
String command="";
if (condWifiAdhoc == false) {
condWifiAdhoc=true;
wifiInterface = Utils.getWifiInterface();
wifiManager.setWifiEnabled(true);
localIP = Utils.getLinkLocalAddress();
}
else
{
wifiManager.setWifiEnabled(true);
localIP = Utils.getLinkLocalAddress();
}
// Set wifi ad-hoc
command = context.getFilesDir().getPath()
+ "/iwconfig " + wifiInterface + " mode ad-hoc essid "
+ "mcp" + " channel " + "1" + " commit\n";
Log.i(LOGTAG, command);
Utils.rootExec(command);
Log.i(LOGTAG, "Ip address used :" + localIP);
command = context.getFilesDir().getPath()
+ "/ifconfig " + wifiInterface + " " + localIP
+ " netmask 255.255.0.0 up\n";
Log.i(LOGTAG, command);
Utils.rootExec(command);
}
Upvotes: 21
Views: 40220
Reputation: 632
I have been searching a long time a consistent, long-term solution, but everything I tested was not working everywhere.
I decided to do some benchmark. Context :
fun benchmarkSendBroadcast(context: Context, message: String,
address: InetAddress, policy: Boolean = true) {
// We want to know if policy is useful when we send broadcast
if (policy) {
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
}
val socket = DatagramSocket()
// No impact if not set, but broadcast are not working
// if set to false
socket.broadcast = true
val buffer = message.toByteArray()
val packet = DatagramPacket(buffer, buffer.size, address, 8888)
socket.send(packet)
}
1) Get IP by activeNetwork :
fun getLocalIP(context: Context): Inet4Address? {
val manager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val props = manager.getLinkProperties(manager.activeNetwork)
return props?.linkAddresses?.find {
it.address is Inet4Address && it.flags == OsConstants.IFA_F_PERMANENT
}?.address as Inet4Address?
}
fun getBroadcastAddress1(inet4Address: Inet4Address): InetAddress? {
val temp: NetworkInterface
var inetBroadcast: InetAddress? = null
try {
temp = NetworkInterface.getByInetAddress(inet4Address)
val addresses = temp.interfaceAddresses
for (inetAddress in addresses) inetBroadcast = inetAddress.broadcast
return inetBroadcast
} catch (e: SocketException) {
e.printStackTrace()
}
return null
}
2) Get IP by DHCP :
fun getBroadcastAddress2(context: Context): InetAddress {
val dhcp =context.getSystemService(WifiManager::class.java).dhcpInfo
val broadcast = (dhcp.ipAddress and dhcp.netmask) or dhcp.netmask.inv()
val quads = ByteArray(4)
for (k in 0..3) quads[k] = ((broadcast shr k * 8) and 0xFF).toByte()
return InetAddress.getByAddress(quads)
}
3) How I call it
fun benchmarkBroadcast() {
// 1) Using currentNetwork
benchmarkSendBroadcast("hello broadcast 1",
getBroadcastAddress1(getLocalIP(context)!!)!!, context)
// 2) Using DHCP
benchmarkSendBroadcast("hello broadcast 2",
getBroadcastAddress2(context), context)
// 3) Using "192.168.1.255" with policy enabled :
benchmarkSendBroadcast("hello broadcast 3",
Inet4Address.getByName("192.168.1.255"), context, true)
// 4) Using "255.255.255.255" with policy enabled :
benchmarkSendBroadcast("hello broadcast 4",
Inet4Address.getByName("255.255.255.255"), context, true)
// 5) Using "192.168.1.255" with policy disabled :
benchmarkSendBroadcast("hello broadcast 5",
Inet4Address.getByName("192.168.1.255"), context, false)
// 6) Using "255.255.255.255" with policy disabled :
benchmarkSendBroadcast("hello broadcast 6",
Inet4Address.getByName("255.255.255.255"), context, false)
}
Must be called from a coroutine IO
or a dedicated Thread
fun benchmarkReceiveBroadcast(inetAddress: InetAddress, threadPolicy: Boolean) {
if (threadPolicy) {
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
}
// Use the same port
val socket = DatagramSocket(8888, inetAddress)
socket.broadcast = true // works even at false
while (true) {
val buffer = ByteArray(1024)
val packet = DatagramPacket(buffer, buffer.size)
socket.receive(packet)
val message = String(packet.data, 0, packet.length)
Log.d("debug", "Broadcast received: $message")
}
}
1) Lock broadcast
I wanted to know if lock and unlock broadcast are necessary
fun lockBroadcast(context: Context) {
try {
val wifi = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
multicastLock = wifi.createMulticastLock(Host::class.java.name).apply {
setReferenceCounted(true)
acquire()
}
} catch (ex: IOException) {
Log.e("debug", ex.message, ex)
}
}
fun unlockBroadcast() {
multicastLock?.release()
}
2) How I call it
Note : Each receiveBroadcast was called and tested one by one with all the other commented. The lock broadcast was also commented for the 6 first tests
suspend fun testReceive(context: Context) {
// Below tests with no locked broadcast
unlockBroadcast()
benchmarkReceiveBroadcast(InetAddress.getByName("0.0.0.0"), false)
benchmarkReceiveBroadcast(getBroadcastAddress1(getLocalIP(context)!!)!!, false)
benchmarkReceiveBroadcast(getBroadcastAddress2(context), false)
benchmarkReceiveBroadcast(InetAddress.getByName("0.0.0.0"), true)
benchmarkReceiveBroadcast(getBroadcastAddress1(getLocalIP(context)!!)!!, true)
benchmarkReceiveBroadcast(getBroadcastAddress2(context), true)
// Below tests with locked multicast
lockBroadcast(context)
benchmarkReceiveBroadcast(InetAddress.getByName("0.0.0.0"), false)
benchmarkReceiveBroadcast(getBroadcastAddress1(getLocalIP(context)!!)!!, false)
benchmarkReceiveBroadcast(getBroadcastAddress2(context), false)
benchmarkReceiveBroadcast(InetAddress.getByName("0.0.0.0"), true)
benchmarkReceiveBroadcast(getBroadcastAddress1(getLocalIP(context)!!)!!, true)
benchmarkReceiveBroadcast(getBroadcastAddress2(context), true)
}
You can see the test result here
0) Flacky broadcasts
If wee look up at the tests, we can see that there's planty way to send broadcast. Sometimes phones deal with it, and others do not.
1) The multicast lock
It was a surprise to see that the multicast lock advice has never changed any of the results in my tests.
2) The policy
The policy doesn't have any impact on the broadcast sending and receiving from what I could read in the results.
3) Getting broadcast from DHCP is inconsistant
If we compare the results of the two getBroadcastAddress function, the one with DCHP is inconsistant :
// Huawei : 192.168.1.255
// Nothing : 192.168.1.255
// Smasung A20E : 192.168.1.255
// Samsung S20FE : 192.168.1.255
getBroadcastAddress1(getLocalIP(context)!!)!!
// Huawei : 192.168.1.255
// Nothing : 255.255.255.255
// Samsung A20E : 255.255.255.255
// Samsung S20FE : 255.255.255.255
getBroadcastAddress2(context)
4) The most failing one
Using the adress 255.255.255.255 as a broadcast address can work, but the receiver must listen 255.255.255.255. In the tests, this is the most unsuccessful and inconsistant one.
5) The most successful one
receiveBroadcast(InetAddress.getByName("0.0.0.0"))
was able to receive any possible way to send broadcasts, on any phones.
With and without policy, and with and without multicastLock.
On the sender side, you should use InetAddress.getByName("192.168.1.255")
or getBroadcastAddress1()
.
To be precise, getBroadcastAddress1()
would care about if the network mask is not 255.255.255.0, so this is a better choice.
Sender :
fun sendBroadcast(context: Context, message: String) {
val socket = DatagramSocket()
socket.broadcast = true
val address = getBroadcastAddress1(getLocalIP(context)!!)!!
val buffer = message.toByteArray()
val packet = DatagramPacket(buffer, buffer.size, address, 8888)
socket.send(packet)
}
fun getLocalIP(context: Context): Inet4Address? {
val manager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val props = manager.getLinkProperties(manager.activeNetwork)
return props?.linkAddresses?.find {
it.address is Inet4Address && it.flags == OsConstants.IFA_F_PERMANENT
}?.address as Inet4Address?
}
fun getBroadcastAddress1(inet4Address: Inet4Address): InetAddress? {
val temp: NetworkInterface
var inetBroadcast: InetAddress? = null
try {
temp = NetworkInterface.getByInetAddress(inet4Address)
val addresses = temp.interfaceAddresses
for (inetAddress in addresses) inetBroadcast = inetAddress.broadcast
return inetBroadcast
} catch (e: SocketException) {
e.printStackTrace()
}
return null
}
Receiver :
fun receiveBroadcast() {
val socket = DatagramSocket(8888, InetAddress.getByName("0.0.0.0"))
while (true) {
val buffer = ByteArray(1024)
val packet = DatagramPacket(buffer, buffer.size)
socket.receive(packet)
val message = String(packet.data, 0, packet.length)
Log.d("debug", "Broadcast received: $message")
}
}
Note : Don't forget to close all the sockets after usage.
You can build my project when I was doing the tests described above
Upvotes: 0
Reputation: 869
I got this working by using a method described here to calculate the broadcast address: https://code.google.com/p/boxeeremote/wiki/AndroidUDP
Here's my receiver:
try {
//Keep a socket open to listen to all the UDP trafic that is destined for this port
socket = new DatagramSocket(Constants.PORT, InetAddress.getByName("0.0.0.0"));
socket.setBroadcast(true);
while (true) {
Log.i(TAG,"Ready to receive broadcast packets!");
//Receive a packet
byte[] recvBuf = new byte[15000];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet);
//Packet received
Log.i(TAG, "Packet received from: " + packet.getAddress().getHostAddress());
String data = new String(packet.getData()).trim();
Log.i(TAG, "Packet received; data: " + data);
// Send the packet data back to the UI thread
Intent localIntent = new Intent(Constants.BROADCAST_ACTION)
// Puts the data into the Intent
.putExtra(Constants.EXTENDED_DATA_STATUS, data);
// Broadcasts the Intent to receivers in this app.
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
}
} catch (IOException ex) {
Log.i(TAG, "Oops" + ex.getMessage());
}
And here's my sender:
public void sendBroadcast(String messageStr) {
// Hack Prevent crash (sending should be done using an async task)
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
//Open a random port to send the package
DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(true);
byte[] sendData = messageStr.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, getBroadcastAddress(), Constants.PORT);
socket.send(sendPacket);
System.out.println(getClass().getName() + "Broadcast packet sent to: " + getBroadcastAddress().getHostAddress());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
}
}
InetAddress getBroadcastAddress() throws IOException {
WifiManager wifi = (WifiManager) 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);
}
Upvotes: 28
Reputation: 1266
I came across your post when trying to solve a similar issue. Did you get your stuff working?
In my case, I had been trying to get a Nexus 7 (first gen with Jelly Bean 4.3) and Nexus One (Gingerbread 2.3.6) talking to each other via UDP. Initially my app, running on both devices, would successfully link up but only with one-way communication from the phone to the tablet. I had only one permission in place in the manifest: INTERNET. Communication from the tablet to the phone started working once I had added the ACCESS_NETWORK_STATE permission to the manifest.
So, for some reason, the Nexus 7 is happy with just the INTERNET permission for both sending and receiving UDP packets (well, my particular implementation of it, at least). The Nexus One will send with only the INTERNET permission but will not receive unless the ACCESS_NETWORK_STATE permission is given as well.
Your code looks similar to mine (I don't recognize your "UTILS." calls, however). In my case, though, for the purposes of testing, I've hard-coded the broadcast address (192.168.n.255). I'm on an access point while you're on an adhoc network. Perhaps that has some effect as well.
Upvotes: 3