如何在Android上使用UDP数据报在两个进程和两个主机之间进行通信

时间:2017-11-17 20:33:44

标签: java android udp broadcast multicast

我的分布式应用程序,各种主机需要在同一局域网内找到本地服务器。

我使用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套接字,但我需要检测局域网中的任何服务器。

2 个答案:

答案 0 :(得分:1)

@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
 */
}

答案 1 :(得分:1)

UDP通信不能依赖于任何类型的进程。问题在于你的代码。