我的分布式应用程序,各种主机需要在同一局域网内找到本地服务器。
我使用NsdManager检测本地服务器的成功有限,这是可行的方法,但通常,服务器仍然未被发现。我正在寻找更好的解决方案,更可靠。
然后我尝试组播UDP数据包,我在同一进程中检测没有问题。 (我的单元测试使用客户端和服务器实例)。这很好。相同的类不允许我的客户端检测在同一设备上运行的服务器(客户端和服务器是独立的流程应用程序)。
然后我使用广播地址改变了我的方法,同样,在同一个过程中,单元测试很好,但在两个不同的过程中,没有找到服务器。
正如我所说,代码在同一个进程中运行良好,但在两个不同的进程中运行时,或在同一局域网内的两个不同的客户机/服务器主机上运行时,它们不起作用。
注意:我的局域网是高端WiFi rooter,没有特殊设置。
任何人都有任何疑问可能是什么问题?
请注意,我的清单包括客户端和服务器应用程序的这些设置:
<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" />
请注意,为了让我的客户端检测在同一主机上运行的服务器,我已经在使用TCP套接字,但我需要检测局域网中的任何服务器。
答案 0 :(得分:1)
制作一个最小的,完整的,可验证的例子让我找到了问题。
以下是可能感兴趣的人的代码:
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
*/
}
答案 1 :(得分:1)
UDP通信不能依赖于任何类型的进程。问题在于你的代码。