在定义的时间段内通过Wifi发现设备

时间:2013-04-10 07:23:51

标签: java multithreading

我正在编写代码,以便从我的移动设备发送UDP Multicast Wifi。在网络中的其他设备上运行服务器代码。服务器将监听多播,以其IP地址和系统类型进行响应(类型:计算机,移动设备,Raspberry Pi,Flyport等)。

在已发送UDP Multicast的移动设备上,我需要设置设备列表以响应UDP Multicast

为此,我创建了一个类,它将作为device details的结构。

DeviceDetails.class

public class DeviceDetails
{
    String DeviceType;
    String IPAddr;
    public DeviceDetails(String type, String IP)
    {
        this.DeviceType=type;
        this.IPAddr=IP;
    }
}

我正在group address of 225.4.5.6Port Number 5432发送UDP多播数据包。

我创建了一个会调用thread的课程,该课程会发送UDP Packets。另一方面,我创建了一个receiver thread来实现 Callable 接口,以返回响应的设备列表。

以下是代码:

MulticastReceiver.java

public class MulticastReceiver implements Callable<DeviceDetails>
{
    DatagramSocket socket = null;
    DatagramPacket inPacket = null;
    boolean check = true;
    public MulticastReceiver()
    {
        try
        {
            socket = new DatagramSocket(5500);
        }
        catch(Exception ioe)
        {
            System.out.println(ioe);
        }
    }
    @Override
    public DeviceDetails call() throws Exception
    {
        // TODO Auto-generated method stub
        try
        {
            byte[] inBuf = new byte[WifiConstants.DGRAM_LEN];
            //System.out.println("Listening");
            inPacket = new DatagramPacket(inBuf, inBuf.length);
            if(check)
            {
                socket.receive(inPacket);

            }

            String msg = new String(inBuf, 0, inPacket.getLength());

            Log.v("Received: ","From :" + inPacket.getAddress() + " Msg : " + msg);
            DeviceDetails device = getDeviceFromString(msg);
            Thread.sleep(100);
            return device;
        }
        catch(Exception e)
        {
            Log.v("Receiving Error: ",e.toString());
            return null;
        }
    }
    public DeviceDetails getDeviceFromString(String str)
    {
        String type;
        String IP;
            type=str.substring(0,str.indexOf('`'));
            str = str.substring(str.indexOf('`')+1);
            IP=str;
        DeviceDetails device = new DeviceDetails(type,IP);
        return device;
    }
}

以下代码是调用Receiver Thread

的活动
public class DeviceManagerWindow extends Activity
{
    public void searchDevice(View view)
    {
        sendMulticast = new Thread(new MultiCastThread());
        sendMulticast.start();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        List<Future<DeviceDetails>> deviceList = new ArrayList<Future<DeviceDetails>>();

            Callable<DeviceDetails> device = new MulticastReceiver();
            Future<DeviceDetails> submit = executorService.submit(device);
            deviceList.add(submit);

        DeviceDetails[] devices = new DeviceDetails[deviceList.size()];
        int i=0;
        for(Future<DeviceDetails> future :deviceList)
        {
            try
            {
                devices[i] = future.get();
            }
            catch(Exception e)
            {
                Log.v("future Exception: ",e.toString());
            }
        }
    }
}

现在接收数据包的标准方法是在无限循环下调用receive method。但是我希望仅在前30秒接收传入连接,然后停止寻找连接。

This is similar to that of a bluetooth searching. It stops after 1 minute of search.

问题在于,我可以使用计数器,但问题是 thread.stop 现在已被删除。不仅如此,如果我将 receive method 置于无限循环下,它将永远不会返回该值。

我该怎么办?我想搜索30秒,然后停止搜索,并希望返回响应的设备列表。

2 个答案:

答案 0 :(得分:2)

您应该致电stop(),而不是致电interrupt()。这会导致InterruptedException在您的代码的可中断位置被抛出,例如在调用Thread.sleep()时或在被I / O操作阻止时。很遗憾,DatagramSocket未实现InterruptibleChannel,因此无法中断对receive的调用。
因此,您要么使用DatagramChannel而不是DatagramSocket,那么receive()会在调用ClosedByInterruptException时抛出Thread.interrupt()。或者您需要通过调用DatagramSocket.setSoTimeout()来设置超时,从而导致receive()在指定的时间间隔后抛出SocketTimeoutException - 在这种情况下,您将不需要中断线程。

简单方法

最简单的方法是简单地设置套接字超时:

public MulticastReceiver() {
    try {
        socket = new DatagramSocket(5500);
        socket.setSoTimeout(30 * 1000);
    } catch (Exception ioe) {
        throw new RuntimeException(ioe);
    }
}

这将导致socket.receive(inPacket);在30秒后抛出SocketTimeoutException。正如您已经抓住Exception,这就是您需要做的一切。

使MulticastReceiver可中断

这是一个更激进的重构。

public class MulticastReceiver implements Callable<DeviceDetails> {
    private DatagramChannel channel;
    public MulticastReceiver() {
        try {
            channel = DatagramChannel.open();
            channel.socket().bind(new InetSocketAddress(5500));
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
    }
    public DeviceDetails call() throws Exception {
        ByteBuffer inBuf = ByteBuffer.allocate(WifiConstants.DGRAM_LEN);
        SocketAddress socketAddress = channel.receive(inBuf);

        String msg = new String(inBuf.array(), 0, inBuf.capacity());

        Log.v("Received: ","From :" + socketAddress + " Msg : " + msg);
        return getDeviceFromString(msg);;
    }
}

DeviceManagerWindow看起来有点不同;我不确定你打算在那里做什么,因为你在处理列表和数组,但是你只有一个未来......所以我假设你想要听30秒并获取尽可能多的设备。

ExecutorService executorService = Executors.newFixedThreadPool(1);
MulticastReceiver receiver = new MulticastReceiver();

List<DeviceDetails> devices = new ArrayList<DeviceDetails>();
long runUntil = System.currentTimeMillis() + 30 * 1000;
while (System.currentTimeMillis() < runUntil) {
    Future<Object> future = executorService.submit(receiver);
    try {
        // wait no longer than the original 30s for a result
        long timeout = runUntil - System.currentTimeMillis();
        devices.add(future.get(timeout, TimeUnit.MILLISECONDS));
    } catch (Exception e) {
        Log.v("future Exception: ",e.toString());
    }
}
// shutdown the executor service, interrupting the executed tasks
executorService.shutdownNow();

就是这样。无论您选择哪种解决方案,都不要忘记关闭插座/通道。

答案 1 :(得分:-1)

我已经解决了..您可以按照以下方式运行代码:

<强> DeviceManagerWindow.java

public class DeviceManagerWindow extends Activity
{
    public static Context con;
    public static int rowCounter=0;
    Thread sendMulticast;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_device_manager_window);
        WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
        if(wifi != null)
        {
            WifiManager.MulticastLock lock = wifi.createMulticastLock("WifiDevices");
            lock.acquire();
        }
        TableLayout tb = (TableLayout) findViewById(R.id.DeviceList);
        tb.removeAllViews();
        con = getApplicationContext();
    }
    public void searchDevice(View view) throws IOException, InterruptedException
    {
        try
        {
            sendMulticast = new Thread(new MultiCastThread());
            sendMulticast.start();
            sendMulticast.join();
        }
        catch(Exception e)
        {
            Log.v("Exception in Sending:",e.toString());
        }

这里是时间限制搜索....你可以使用 thread.join

退出你的主题
        //Device Will only search for 1 minute
        for(long stop=System.nanoTime()+TimeUnit.SECONDS.toNanos(1); stop>System.nanoTime();)
        {
            Thread recv = new Thread(new MulticastReceiver());
            recv.start();
            recv.join();
        }
    }
    public static synchronized void addDevice(DeviceDetails device) throws InterruptedException
    {
        ....
        Prepare your desired list here.
        ....
    }
}

不要在聆听方添加任何循环。只需使用socket.receive

<强> MulticastReceiver.java

public class MulticastReceiver implements Runnable
{
    DatagramSocket socket = null;
    DatagramPacket inPacket = null;
    public MulticastReceiver()
    {
        try
        {
            socket = new DatagramSocket(WifiConstants.PORT_NO_RECV);
        }
        catch(Exception ioe)
        {
            System.out.println(ioe);
        }
    }
    @Override
    public void run()
    {
        byte[] inBuf = new byte[WifiConstants.DGRAM_LEN];

        //System.out.println("Listening");
        inPacket = new DatagramPacket(inBuf, inBuf.length);
        try
        {
            socket.setSoTimeout(3000)
            socket.receive(inPacket);
            String msg = new String(inBuf, 0, inPacket.getLength());
            Log.v("Received: ","From :" + inPacket.getAddress() + " Msg : " + msg);
            DeviceDetails device = getDeviceFromString(msg);
            DeviceManagerWindow.addDevice(device);

socket.setSoTimeout(3000) 只会将套接字的侦听时间设置为3秒。如果数据包没有到达,它将更进一步。 DeviceManagerWindow.addDevice(device); 此行将调用调用类中的addDevice方法。您可以在哪里准备清单

        }
        catch(Exception e)
        {
            Log.v("Receiving Error: ",e.toString());
        }
        finally
        {
            socket.close();
        }
    }
    public DeviceDetails getDeviceFromString(String str)
    {
        String type;
        String IP;
            type=str.substring(0,str.indexOf('`'));
            str = str.substring(str.indexOf('`')+1);
            IP=str;
        DeviceDetails device = new DeviceDetails(type,IP);
        return device;
    }
}

希望有效..好吧它会起作用。 祝一切顺利。如果有任何问题,请告诉我。