Send Broadcast UDP but not receive it on other Android devices
Asked Answered
M

3

21

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);

}
Malonylurea answered 25/6, 2013 at 22:35 Comment(3)
Note that some routers disable multicast DNS by default.Flews
oh... I must mention that I'm in a wifi adhocMalonylurea
You may want to look at this post: https://mcmap.net/q/660225/-udp-broadcast-packets-not-received-in-sleep-mode It says that some wifi drivers can disable broadcast receivers, but in that case it happens after resuming from sleep mode.Photocopier
A
28

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);
  }
Accountancy answered 27/8, 2014 at 6:26 Comment(2)
Does this code work only if the devices are on the same local network?Rhetorician
verified on Android 5.1.1 and Android 7.1.2Navarino
R
3

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.

Riyal answered 8/8, 2013 at 16:51 Comment(0)
M
0

2024 statement

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 :

  • All phones was physical. Emulator phone are running under a sub-network which make them unreachable from your home network.
  • My phones are :
    • Nothing phone (1) - Android 14 API 34
    • Huawei P20 - Android 10 API 29
    • Samsung A20E - Android 11 API 30
    • Samsung S20FE - Android Android 13 API 33
  • The port must be the same both on the broadcast side and the receiver side

How did I test

Way to write my benchmark broadcast :

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)
}

Ways to call my broadcast function :

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)
}

Way to write my receiver :

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")
    }
}

Ways to call my receiver function :

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

Conclusion

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.

Definitive version

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

Results of the tests

Margetts answered 23/6 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.