Reputation: 590
My distributed application, with various hosts need to find local servers within the same LAN.
I had limited success detecting local servers using NsdManager, which is the way to go, but often, servers remain undetected. I am looking for a better solution, something more reliable.
Then I attempted to multicast UDP packets, which I have no problem detecting from within the same process. (My unit test uses both client and servers instances). That works fine. The same classes will not allow my clients to detect my servers running on the same device (client and servers are separate process applications).
Then I varied my approach using broadcast addresses, again, within the same process, unit tests are fine, but in two distinct process, no server found.
As I said, the code works fine within the same process, but they do not work when running in two distinct processes, or on two distinct client / server hosts within the same LAN.
Note: my LAN is a high end WiFi rooter, no special settings.
Anyone has any clue what might be the problem?
Note that my manifest includes these settings for both client and server applications:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
Note that for my client to detect the server running on the same Host, I am already using TCP sockets, but I need to detect whatever server is in my LAN.
Upvotes: 1
Views: 567
Reputation: 19959
UDP communication cannot depend on any kind of processes. The problem is with your code.
Upvotes: 1
Reputation: 590
@Onik was quite right,
Making a Minimal, Complete, and Verifiable example allowed me to find the problem.
Here is the code for whoever may be interested:
package com.example;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.List;
import android.util.Log;
/**
* Minimal, Complete, and Verifiable example for broadcasting UDP sockets, send thread.
*
* Loosely based on:
*
* https://jackiexie.com/2015/07/15/network-discovery-using-udp-broadcast-java/
*/
public class BroadcastSender extends Thread {
public static final String TAG = "BroadcastSender";
public static final int TEST_PORT_NUMBER = 1234;
public static final int TEST_COUNT = 10;
public static final int DELAY_BETWEEN_PACKETS_MS = 500;
public static final int TEST_TIME_MS = TEST_COUNT * DELAY_BETWEEN_PACKETS_MS;
private DatagramSocket mSocket;
public BroadcastSender() {
super( TAG );
}
public static InetAddress getBroadcastAddress() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while( interfaces.hasMoreElements() ) {
NetworkInterface current = interfaces.nextElement();
try {
if( !current.isUp() )
continue;
if( current.isLoopback() )
continue;
List<InterfaceAddress> addresses = current.getInterfaceAddresses();
if( addresses == null ) {
log("getBroadcastAddress can not get addresses from "+current);
continue;
}
for( InterfaceAddress oneInterfaceAddress : addresses ) {
InetAddress bcast = oneInterfaceAddress.getBroadcast();
if( bcast != null )
return bcast;
}
}
catch( Exception ex) {
log( "getBroadcastAddress exception "+ex+" stack "+Log.getStackTraceString(ex) );
}
}
}
catch( Exception ex) {
log( "getBroadcastAddress exception "+ex+" stack "+Log.getStackTraceString(ex) );
}
return null;
}
@Override
public void run() {
try {
mSocket = new DatagramSocket();
mSocket.setBroadcast(true);
InetAddress interfaceBroadcast = getBroadcastAddress();
InetAddress broadcastAddr = InetAddress.getByName("255.255.255.255");
String payloadStr = "payload pid="+android.os.Process.myPid();
byte payloadBytes[] = payloadStr.getBytes();
for( int packet = 0; packet < TEST_COUNT; packet++) {
// First send to 255.255.255.255
DatagramPacket dp = new DatagramPacket(payloadBytes, payloadBytes.length, broadcastAddr, TEST_PORT_NUMBER);
mSocket.send(dp);
// Then send to whatever address we have for interfaceBroadcast
if( interfaceBroadcast != null ) {
dp = new DatagramPacket(payloadBytes, payloadBytes.length, interfaceBroadcast, TEST_PORT_NUMBER);
mSocket.send(dp);
}
log("sent #"+packet+" with "+payloadBytes.length+" bytes \""+payloadStr+"\"");
synchronized (this) {
try {
wait(DELAY_BETWEEN_PACKETS_MS);
}
catch( InterruptedException ex) {
log("run exeption "+ex.toString());
}
}
}
mSocket.close();
mSocket = null;
}
catch( Exception ex) {
log("run exception "+ex.toString()+" stack "+Log.getStackTraceString(ex));
}
}
private static void log( String message) {
Log.d(TAG, message);
}
/**
* Minimal, Complete, and Verifiable example for broadcasting UDP sockets, receive thread.
* Loosely based on:
*
* https://jackiexie.com/2015/07/15/network-discovery-using-udp-broadcast-java/
*/
public static class BroadcastReceiver extends Thread {
public static final String TAG = "BroadcastReceiver";
public int totalReceived = 0;
public BroadcastReceiver() {
super( TAG );
}
@Override
public void run() {
final String methodName = "run";
try {
byte messageBytes[] = new byte[1500];
DatagramPacket dp = new DatagramPacket(messageBytes, messageBytes.length);
DatagramSocket mSocket = new DatagramSocket(TEST_PORT_NUMBER);
mSocket.setSoTimeout(DELAY_BETWEEN_PACKETS_MS);
mSocket.setBroadcast(true);
while( totalReceived < TEST_COUNT ) {
try {
// Get one UDP packet
mSocket.receive(dp);
if( dp.getLength() > 0 ) {
totalReceived++;
String payloadStr = new String(dp.getData());
log( "received #"+totalReceived+" : \""+payloadStr+"\"");
}
}
catch( Exception ex) {
log("receive exception "+ex.toString());
}
}
mSocket.close();
mSocket = null;
log(methodName+" socket closed.");
}
catch( Exception ex) {
log(methodName+" exception "+ex.toString());
}
}
private static void log( String message) {
Log.d(TAG, message);
}
}
public static boolean unit_test() {
try {
BroadcastSender sender = new BroadcastSender();
sender.start();
BroadcastReceiver receiver = new BroadcastReceiver();
receiver.start();
log( "joining "+sender.getName());
sender.join(TEST_TIME_MS);
log( "joining "+receiver.getName());
receiver.join(TEST_TIME_MS);
boolean success = receiver.totalReceived >= TEST_COUNT;
log( "unit test results="+success);
return success;
}
catch( Exception ex ) {
log( "unit test exception "+ex);
return false;
}
}
/*
* Log output:
*
D 2017-11-17 19:10:56.809 6228 BroadcastSender sent #0 with 16 bytes "payload pid=6228"
D 2017-11-17 19:10:57.313 6228 BroadcastSender sent #1 with 16 bytes "payload pid=6228"
D 2017-11-17 19:10:57.314 6228 BroadcastReceiver received #1 : "payload pid=6228"
D 2017-11-17 19:10:57.315 6228 BroadcastReceiver received #2 : "payload pid=6228"
D 2017-11-17 19:10:57.816 6228 BroadcastSender sent #2 with 16 bytes "payload pid=6228"
D 2017-11-17 19:10:57.817 6228 BroadcastReceiver received #3 : "payload pid=6228"
D 2017-11-17 19:10:57.818 6228 BroadcastReceiver received #4 : "payload pid=6228"
D 2017-11-17 19:10:58.319 6228 BroadcastReceiver received #5 : "payload pid=6228"
D 2017-11-17 19:10:58.319 6228 BroadcastSender sent #3 with 16 bytes "payload pid=6228"
D 2017-11-17 19:10:58.321 6228 BroadcastReceiver received #6 : "payload pid=6228"
D 2017-11-17 19:10:58.822 6228 BroadcastSender sent #4 with 16 bytes "payload pid=6228"
D 2017-11-17 19:10:58.822 6228 BroadcastReceiver received #7 : "payload pid=6228"
D 2017-11-17 19:10:58.823 6228 BroadcastReceiver received #8 : "payload pid=6228"
D 2017-11-17 19:10:59.325 6228 BroadcastReceiver receive exception java.net.SocketTimeoutException: Receive timed out
D 2017-11-17 19:10:59.326 6228 BroadcastReceiver received #9 : "payload pid=6228"
D 2017-11-17 19:10:59.327 6228 BroadcastReceiver received #10 : "payload pid=6228"
D 2017-11-17 19:10:59.325 6228 BroadcastSender sent #5 with 16 bytes "payload pid=6228"
D 2017-11-17 19:10:59.328 6228 BroadcastReceiver run socket closed.
D 2017-11-17 19:10:59.831 6228 BroadcastSender sent #6 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:00.333 6228 BroadcastSender sent #7 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:00.834 6228 BroadcastSender sent #8 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:01.336 6228 BroadcastSender sent #9 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:01.839 6228 BroadcastSender sent #10 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:02.341 6228 BroadcastSender sent #11 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:02.844 6228 BroadcastSender sent #12 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:03.346 6228 BroadcastSender sent #13 with 16 bytes "payload pid=6228"
D 2017-11-17 19:11:01.810 6228 BroadcastSender unit test results=true
*/
}
Upvotes: 1