Java 如何在Android上使用UDP数据报在两个进程和两台主机之间进行通信
我的分布式应用程序和各种主机需要在同一局域网内找到本地服务器 我使用NsdManager检测本地服务器的成功率有限,这是一种可行的方法,但服务器通常未被检测到。我正在寻找一个更好的解决方案,一个更可靠的解决方案 然后,我尝试多播UDP数据包,在同一进程中检测这些数据包没有问题。(我的单元测试同时使用客户端和服务器实例)。那很好。相同的类不允许我的客户端检测在同一设备上运行的服务器(客户端和服务器是独立的流程应用程序) 然后,我使用广播地址改变了我的方法,同样,在同一个过程中,单元测试很好,但在两个不同的过程中,没有找到服务器 正如我所说,代码在同一个进程中运行良好,但在两个不同的进程中运行时,或者在同一局域网内的两个不同的客户机/服务器主机上运行时,代码不起作用 注:我的局域网是一个高端WiFi根,没有特殊设置 有人知道问题出在哪里吗 请注意,“我的清单”包括客户端和服务器应用程序的以下设置:Java 如何在Android上使用UDP数据报在两个进程和两台主机之间进行通信,java,android,udp,broadcast,multicast,Java,Android,Udp,Broadcast,Multicast,我的分布式应用程序和各种主机需要在同一局域网内找到本地服务器 我使用NsdManager检测本地服务器的成功率有限,这是一种可行的方法,但服务器通常未被检测到。我正在寻找一个更好的解决方案,一个更可靠的解决方案 然后,我尝试多播UDP数据包,在同一进程中检测这些数据包没有问题。(我的单元测试同时使用客户端和服务器实例)。那很好。相同的类不允许我的客户端检测在同一设备上运行的服务器(客户端和服务器是独立的流程应用程序) 然后,我使用广播地址改变了我的方法,同样,在同一个过程中,单元测试很好,但在两
<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套接字,但我需要检测局域网中的任何服务器。@Onik非常正确
制作一个最小的、完整的、可验证的示例可以让我找到问题所在
以下是可能感兴趣的人的代码:
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
*/
}
package.com.example;
导入java.net.DatagramPacket;
导入java.net.DatagramSocket;
导入java.net.InetAddress;
导入java.net.InterfaceAddress;
导入java.net.NetworkInterface;
导入java.util.Enumeration;
导入java.util.List;
导入android.util.Log;
/**
*广播UDP套接字的最小、完整且可验证的示例,发送线程。
*
*大致基于:
*
* https://jackiexie.com/2015/07/15/network-discovery-using-udp-broadcast-java/
*/
公共类BroadcastSender扩展线程{
公共静态最终字符串标记=“BroadcastSender”;
公共静态最终内部测试端口号=1234;
公共静态最终整数测试计数=10;
数据包之间的公共静态最终整数延迟=500;
公共静态最终整数测试时间=测试计数*数据包之间的延迟;
私有DatagramSocket-mSocket;
公共广播发送者(){
超级(标签);
}
公共静态InetAddress getBroadcastAddress(){
试一试{
枚举接口=NetworkInterface.getNetworkInterfaces();
while(interfaces.hasMoreElements()){
NetworkInterface current=interfaces.nextElement();
试一试{
如果(!current.isUp())
继续;
if(current.isLoopback())
继续;
列表地址=current.getInterfaceAddresses();
如果(地址==null){
日志(“getBroadcastAddress无法从“+当前”获取地址);
继续;
}
用于(InterfaceAddress One InterfaceAddress:地址){
InetAddress bcast=oneInterfaceAddress.getBroadcast();
if(bcast!=null)
返回bcast;
}
}
捕获(例外情况除外){
日志(“GetBroadcastAddressException”+ex+“stack”+log.getStackTraceString(ex));
}
}
}
捕获(例外情况除外){
日志(“GetBroadcastAddressException”+ex+“stack”+log.getStackTraceString(ex));
}
返回null;
}
@凌驾
公开募捐{
试一试{
mSocket=新的DatagramSocket();
mSocket.setboodcast(真);
InetAddress interfaceBroadcast=getBroadcastAddress();
InetAddress broadcastAddr=InetAddress.getByName(“255.255.255.255”);
字符串payloadStr=“payload pid=“+android.os.Process.myPid();
byte payloadBytes[]=payloadStr.getBytes();
对于(int packet=0;packet