I'm creating a token ring with sensors. I have client processes defined in SensorClient class. These client objects receive information about the list of other clients from a server process, the createSensor class.
The problem is that I would like clients to update the information they have when it changes on the server.
The server class:
public class createSensor {
private static createSensor instance = null;
private ArrayList<Sensor> sensor = new ArrayList<>();
public int position, prevPosition, nextPosition, prevPort, nextPort;
private createSensor() {
}
public synchronized ArrayList insertSensor(String type, String identificator, int port, String id, String gatwayAddr, long timestamp) throws IOException {
sensor.add(new Sensor(type, identificator, port, id, gatwayAddr, timestamp));
return new ArrayList<>(sensor); //
}
}
public synchronized boolean hasMeasurements() {
while (InnerBuffer.getInstance().emptyInnerBuffer())
return false;
return true;
}
public synchronized void setPrevNextWhenDelete(int position,ArrayList<Sensor> sensorList) throws IOException {
//code
}
public synchronized ArrayList<Sensor> getSensorList() {
return new ArrayList<>(sensor);
}
public synchronized int size() {
return sensor.size();
}
public synchronized String returnRecentMeasurement (String id){
String recentMeasurement=null;
for (Sensor sensori : sensor) {
if (sensori.getIdentificator().equalsIgnoreCase(id))
recentMeasurement= InnerBuffer.getInstance().returnRecentMeasurements(id);
else
recentMeasurement = null;}
return recentMeasurement;
}
public synchronized void setPrevNextWhenAdd() throws IOException { //some other code where int position, prevPosition, nextPosition, prevPort, nextPort get their values.
}}
The client class:
public class SensorClient {
public static void main(String[] args) throws Exception {
//Starting a new sensor
Sensor sensor = new Sensor(type,identificator,portnumber,ipnumber,gatewayAddr,timestamp);
Gson gson = new Gson();
String message = gson.toJson(sensor);
Client c = Client.create();
WebResource r = c.resource("http://localhost:9999/gateway/");
ClientResponse response = r.path("sensors/add").type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(ClientResponse.class, message);
if (response.getStatus() == 200) {
repeat = false;
Type collectionType = new TypeToken<ArrayList<Sensor>>(){}.getType();
ArrayList<Sensor> sensorList = gson.fromJson(response.getEntity(String.class), collectionType);
System.out.println("Starting the sensor ...");
System.out.println("Push exit when you want to delete the sensor!");
int position = 0;
for(int i = 0; i< sensorList.size();i++){
if(sensorList.get(i).getIdentificator().equalsIgnoreCase(sensor.getIdentificator()) )
position = i;
}
sensors.Sensor.simulation(type, identificator);// special thread for sensors simulations
createSensor.getInstance().setPrevNextWhenAdd(position,sensorList);
serverSocket serverSocket = new serverSocket(portnumber,sensorList,position,sensorList.get(position).getNext());
serverSocket.start();
StopSensor stopSensor = new StopSensor(identificator,portnumber,position,sensorList);
stopSensor.start();
oneSensor s = new oneSensor(portnumber,sensorList);
s.start();
} else {
repeat = true;
count +=1;
System.out.println("Error. Wrong data! ");
}
}
while (repeat );
}
}
}
The serverSocket thread
public class serverSocket extends Thread {
public int port,nextPort;
ArrayList<gateway.Sensor> sensorList;
public static int position;
public serverSocket(int port, ArrayList<gateway.Sensor> sensorList,int position,int nextPort) {
this.port = port;
this.nextPort=nextPort;
this.sensorList= sensorList;
this.position=position;}
public void run() {
ServerSocket welcomeSocket;
Socket connectionSocket;
try {
welcomeSocket = new ServerSocket(port);
while (true) {
connectionSocket = welcomeSocket.accept();
receivedMessages thread = new receivedMessages(connectionSocket,sensorList,position,nextPort);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
System.err.println("Error!!!!!!!!!");
}
}
}
The receivedMessages thread
public class receivedMessages extends Thread {
private BufferedReader inFromClient;
private Socket connectionSocket;
ArrayList<gateway.Sensor> sensorList;
int position,nextPort;
public receivedMessages(Socket socket, ArrayList<gateway.Sensor> sensorList,int position,int nextPort){
connectionSocket = socket;
this.sensorList=sensorList;
this.position=position;
this.nextPort=nextPort;
try {
inFromClient = new BufferedReader( new InputStreamReader(connectionSocket.getInputStream()));
} catch (IOException e) { e.printStackTrace(); }
}
#Override
public void run() {
// while(true) {
try {
String message = (inFromClient.readLine().toString());
System.out.println(message);
if (message.startsWith("Next") || message.startsWith("Previous")) {
System.out.println(message);
} else if (message.startsWith("The")) {
System.out.println(message); createSensor.getInstance().setPrevNextWhenDelete(position, sensorList);
} else {// i receive the message that the list has changed
System.out.println(message);
sensorList = createSensor.getInstance().getSensorList();
System.out.println("Updated " + sensorList);}
Listening for changes
There are 2 possibilities for your client processes to be informed when data is modified:
your client objects could regularly check whether list has been modified:
by asking for the current version of the list
or by asking for a timestamp of the list's modification (your server class, createSensor, would then have to keep a record of that)
your client objects could open a port to listen to notifications, and your server class could notify them of a change, pushing the new list to them when the list is modified.
Sharing updates between the listener thread and the worker thread
In both cases, on your clients side, you will have one thread doing the work, and another thread listening for changes. That second thread will reconstruct a new list each time. In order to share it with the worker thread, here is what you can do :
your listener thread will keep a reference to the sensorList that it was previously passed by the worker thread when created
when reconstructing a new list, your listener thread will store that new list in another reference, newList
your listener list can then modify the "old" list so that it matches the new list, by using oldList.clear() then oldList.addAll(newList).
This would work, since even though the worker thread might have its own reference to the list, both references point to the same object, so it would see the changes.
Code for starting listener thread
For example, if your worker thread is the main thread, and it is creating the listener thread, your code could be like this, inside the worker thread code :
class Listener implements Runnable {
List<Sensor> sensorList;
Listener(List<Sensor> list ){
sensorList = list;
}
public void run(){
// code to listen to changes on the server
// When server sends new information, thread can update the list directly:
// sensorList.clear();
// sensorList.addAll(newList);
}
}
Listener l = new Listener(sensorList);
Thread listenerThread = new Thread(l);
listenerThread.start();
You can read about threads and how they share memory in the Java Tutorials on Concurrency.
Be careful with synchronization though : when you update the old list, you should make sure other thread is not iterating over it. You should probably use Collections.synchronizedList(sensorList) to make sure there are no thread interferences.
Related
I am working on a project and for one part of my project I have as ArrayList of Strings I keep on record to hold onto incoming messages from other systems that are interconnected. This is a peer to peer design so I wanna have a BufferedReader ready to read any messages sent from any sockets to the system, so I designed a thread that, when created, creates a new thread for each socket that will listen to a specific input stream.
Right now I have attempted this using the following two private classes:
InputListener(inner class ListenerThread)
private class InputListener implements Runnable{
private ArrayList<String> queue;
private ArrayList<Stream> sockets;
private ArrayList<Thread> threads;
public InputListener(ArrayList<String> q, ArrayList<Stream> s)
{
this.queue = q;
this.sockets = s;
this.threads = new ArrayList<Thread>();
for(int i = 0; i < this.sockets.size(); i++)
{
Thread t = new Thread(new ListeningThread(this.sockets.get(i).is, this.queue));
t.start();
threads.add(t);
}
}
private class ListeningThread implements Runnable{
private BufferedReader read;
private ArrayList<String> queue;
private boolean status;
public ListeningThread(InputStream is, ArrayList<String> q)
{
this.read = new BufferedReader(new InputStreamReader(is));
this.queue = q;
status = true;
}
#Override
public void run() {
while(true)
{
String str = "";
try {
str += read.readLine();
while(!str.equals("END"))
str += read.readLine();
this.queue.add(str);
} catch (IOException e) {
status = false;
break;
}
}
}
}
#Override
public void run() {
while(status)
;
}
}
Stream
private class Stream{
public InputStream is;
public OutputStream os;
public Stream(final Socket s)
{
try {
this.is = s.getInputStream();
this.os = s.getOutputStream();
} catch (IOException e) {
return;
}
}
public InputStreamReader getReader()
{
return new InputStreamReader(this.is);
}
}
When I create the InputListener I pass a reference to a queue from another class, I am excluding this class to prevent over complicating this problem, so assume that this ArrayList is initialized and it's pointer(I cant remember what java calls it) is passed. My problem is that when I use a loop like the following, I just get trapped in an infinite loop
while(queue.size equals 0)
Do nothing
Remove and do something with String at index 0 in queue
Can anyone help me with this problem? any help will be greatly appriciated!
You should use one of the specialized container classes in java.util.concurrent instead of a standard unsynchronized ArrayList.
For example, LinkedBlockingQueue.
// in the setup
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// in producer thread
queue.put(work);
// in consumer thread
work = queue.take(); // blocking - waits as long as needed
I also suggest reading the Java Tutorial on Concurrency. It is not a trivial subject.
I have a method where I listen for UDP packets in a while loop. I want to parse the packets using another method in a different class as they arrive and do many different parsing and analyzing of each packet in another part of the application. I am thinking it would be better to have the PacketParser methods process the Queue outside of the loop. Would it be possible to just add the packets to a Queue as they come in and then have another part of the application listen for items as they come into the Queue and perform other actions as the original while loop keeps listening for packets and adds them to the queue? I would like to have another function monitor the queue and process the packets, is there something in Java to monitor a Queue or Stack? Is there a better way to do this?
public void read(String multicastIpAddress, int multicastPortNumber) {
PacketParser parser = new PacketParser(logger);
InetAddress multicastAddress = null;
MulticastSocket multicastSocket = null;
final int PortNumber = multicastPortNumber;
try {
multicastAddress = InetAddress.getByName(multicastIpAddress);
multicastSocket = new MulticastSocket(PortNumber);
String hostname = InetAddress.getLocalHost().getHostName();
byte[] buffer = new byte[8192];
multicastSocket.joinGroup(multicastAddress);
System.out.println("Listening from " + hostname + " at " + multicastAddress.getHostName());
int numberOfPackets = 0;
while (true) {
numberOfPackets++;
DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);
multicastSocket.receive(datagramPacket);
// add to queue for another function to process the packets
}
} catch (SocketException socketException) {
System.out.println("Socket exception " + socketException);
} catch (IOException exception) {
System.out.println("Exception " + exception);
} finally {
if (multicastSocket != null) {
try {
multicastSocket.leaveGroup(multicastAddress);
multicastSocket.close();
} catch (IOException exception) {
System.out.println(exception.toString());
}
}
}
}
Ok, so I did some reading about the producer-consumer pattern and figured it out so here is what I did.
Basically the producer-consumer pattern involves three things: a producer, a consumer and a shared queue. In this context the PacketReader is the producer that takes in network packets and places them into the shared queue. The PacketParser is the consumer who processes the packets in the shared queue. So I created an instance of a LinkedBlockingQueue and passed that shared queue into an instance of the consumer (PacketReader) and an instance of the producer (PacketParser). Then the consumer and producer instances are each passed into an instance of the Thread class. Finally, call the start() method on each thread instance.
public class Main {
public static void main(String[] args) {
BlockingQueue<Packet> queue = new LinkedBlockingQueue<>();
ILogger logger = Injector.getLogger();
Thread reader = new Thread(new PacketReader(logger, queue, "239.1.1.1", 49410));
Thread parser = new Thread(new PacketParser(logger, queue));
reader.start();
parser.start();
}
}
The reason to use the LinkedBlockingQueue is because the put() method will block the queue if full and take() will block if queue if empty. The producer and consumer classes need to implement the Runnable interface and contain a method named run() that takes no parameters.
Consumer class
public class PacketParser implements Runnable {
private ILogger logger;
private BlockingQueue<Packet> queue;
private boolean running = true;
public PacketParser(ILogger logger, BlockingQueue<Packet> queue) {
this.logger = logger;
this.queue = queue;
}
public void stop() {
running = false;
}
public void run() {
while (running) {
Packet packet;
try {
packet = queue.take();
parse(packet);
} catch (InterruptedException exception) {
logger.Log(exception.getStackTrace().toString());
}
}
}
Producer class
public class PacketReader implements Runnable {
private ILogger logger;
private final Queue<Packet> queue;
private String multicastIpAddress;
private int multicastPortNumber;
private boolean running = true;
public PacketReader(ILogger logger, Queue<Packet> queue, String multicastIpAddress, int multicastPortNumber) {
this.logger = logger;
this.queue = queue;
this.multicastIpAddress = multicastIpAddress;
this.multicastPortNumber = multicastPortNumber;
}
public void stop() {
running = false;
}
public void run() {
InetAddress multicastAddress = null;
MulticastSocket multicastSocket = null;
try {
multicastAddress = InetAddress.getByName(multicastIpAddress);
multicastSocket = new MulticastSocket(multicastPortNumber);
String hostname = InetAddress.getLocalHost().getHostName();
byte[] buffer = new byte[8192];
multicastSocket.joinGroup(multicastAddress);
System.out.println("Listening from " + hostname + " at " + multicastAddress.getHostName());
int numberOfPackets = 0;
while (running) {
numberOfPackets++;
DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);
multicastSocket.receive(datagramPacket);
Packet packet = new Packet(numberOfPackets, datagramPacket);
queue.add(packet);
}
} catch (SocketException socketException) {
System.out.println("Socket exception " + socketException);
} catch (IOException exception) {
System.out.println("Exception " + exception);
} finally {
if (multicastSocket != null) {
try {
multicastSocket.leaveGroup(multicastAddress);
multicastSocket.close();
} catch (IOException exception) {
System.out.println(exception.toString());
}
}
}
}
}
So I have a multithreaded server, and data sends back and forth correctly, but my write operations stalls on the slower connections. I've noticed that it goes by connection time. The first client to connect always receives data first from the server. The next one has to wait until the first one is done receiving and so on so forth. What I'm looking for is a server that sends data to many clients without waiting for a client to finish receiving. I've read up about NIO (non-blocking), but I'd really prefer keeping my current method, which is to use a separate thread for each client.
Here's the code.
Server:
public class Server implements Runnable {
private Thread thread;
private ServerSocket serverSocket;
private ArrayList<ClientThread> clients;
public Server(int port) throws IOException {
thread = new Thread(this);
clients = new ArrayList<ClientThread>();
serverSocket = new ServerSocket(port);
thread.start();
}
#Override
public void run() {
while (true) {
try {
//Listens to clients connecting.
ClientThread client = new ClientThread(serverSocket.accept());
clients.add(client);
ServerWindow.addText("-- Someone connected!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void broadcast(String data) {
broadcast(data, null);
}
public void broadcast(String data, ClientThread exclude) {
int amount = clients.size();
for (int i = 0; i < amount; i++) {
if (!clients.get(i).equals(exclude)) { //Don't send it to that client.
try {
clients.get(i).broadcast(data);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Client thread object:
public class ClientThread implements Runnable {
private Thread thread;
private Socket socket;
private Scanner input;
private PrintWriter output;
public ClientThread(Socket s) throws IOException {
thread = new Thread(this);
socket = s;
socket.setTcpNoDelay(true);
//socket.setSoTimeout(10); //Send little chunk for 10 milliseconds.
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream());
thread.start();
}
public void run() {
while (true) {
if (input.hasNext()) {
reciever(input.nextLine());
}
}
}
private void reciever(String data) {
ServerWindow.addText(data);
ServerWindow.server.broadcast(data, this);
}
public void broadcast(String data) throws IOException {
output.println(data);
output.flush();
}
}
It seems you are calling the broadcast method from the same thread.
This is a common pitfall for users new to multithreading in Java.
The fact that the broadcast method is in a subclass of Thread does not mean it will be executed on that Thread
In fact it will be executed on the thread that called it. The only method that will be executed on your created ClientThread is run() and anything that run() calls while it is executing. If you want said thread to not only read data from your connection but also write to it, you have to modify the run method to listen to external commands to start writing.
I wanted to create a simple game with a server and more than one clients. Server will have several Hashmaps and Arraylists. Server will broadcast these to clients, then one by one a client may modify these and send back to server and then server will broadcast updated values to all clients.
To get started, I have created Server - Client chat app. When a client sends String message to server, Server will add that String message to it's Arraylist and will broadcast that arraylist to all clients. I have used threads so that multiple clients can send messages concurrently, but I haven't applied thread-safety yet.
Lets come to the problem. for the first time when a client sends String to server, server prints it well, add to it's arraylist, then broadcasts it to all clients and all clients can see that too. But next time when client sends String message, server accepts it, adds to arraylist and broadcasts it, but this time all clients gets old arraylist ( list with only one String which was added first ). I have printed arraylist before broadcasting and it shows modified values, but at client side it shows list with one entry only.
Part of Server code
public class ServerGUI extends javax.swing.JFrame {
public static final int SERVER_PORT = 4000;
private ServerSocket ss;
ArrayList<String> al;
ArrayList<ClientHandler> clients;
public ServerGUI() {
initComponents();
setVisible(true);
al = new ArrayList<>();
clients = new ArrayList<>();
initNet();
}
private void initNet() {
Socket ds = null;
try {
ss = new ServerSocket(SERVER_PORT, 1);
while (true) {
ds = ss.accept();
clients.add(new ClientHandler(ds));
}
} catch (Exception e) {
System.out.println("shutting down server......");
}
}
class ClientHandler extends Thread {
private Socket ds;
private ObjectOutputStream out;
private ObjectInputStream in;
public ClientHandler(Socket ds) throws Exception {
this.ds = ds;
out = new ObjectOutputStream(ds.getOutputStream());
in = new ObjectInputStream(ds.getInputStream());
start();
}
public ObjectOutputStream getOut() {
return out;
}
public void run() {
try {
while (true) {
acceptData(in);
broadcastData();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("Finally called. socket closed");
if (ds != null) {
try {
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
private void acceptData(ObjectInputStream in) throws Exception {
System.out.println("acceptData called by " + Thread.currentThread().getName());
String s = (String) in.readObject();
al.add(s);
jta.setText(al.toString());
}
private void broadcastData() throws Exception {
System.out.println("broadcast called by " + Thread.currentThread().getName());
System.out.println("al is : \n" + al);
for (ClientHandler clnt : clients) {
clnt.getOut().writeObject(al);
clnt.getOut().flush();
}
}
Part of Client code
public class ClientGUI extends javax.swing.JFrame {
public static final int SERVER_PORT = 4000;
public static final String SERVER_IP = "127.0.0.1";
private Socket s1;
private ObjectOutputStream out;
private ObjectInputStream in;
private ArrayList<String> al;
public ClientGUI() {
initComponents();
setVisible(true);
initNet();
}
private void initNet() {
try {
s1 = new Socket(SERVER_IP, SERVER_PORT);
out = new ObjectOutputStream(s1.getOutputStream());
in = new ObjectInputStream(s1.getInputStream());
System.out.println("connected to server");
new ReadData();
} catch (Exception e) {
e.printStackTrace();
}
}
class ReadData extends Thread {
public ReadData() {
start();
}
public void run() {
System.out.println("client thread started");
try {
while (true) {
al = (ArrayList<String>) in.readObject();
System.out.println("client read completed, al is "+al);
jta.setText(al.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void textFieldActionPerformed(java.awt.event.ActionEvent evt) {
try {
out.writeObject(jtf.getText());
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
This is normal behavior. If you send the same object (your ArrayList) several times to a given ObjectOutputStream, the stream will send the full object the first time, and will only send a reference to this object the next times. This is what allows sending a graph of objects without consuming too much bandwidth, and without going into infinite loops because a references b which also references a.
To make sure the ArrayList is sent a second time, you need to call reset() on the ObjectOutputStream.
I am writing a code to send a UDP Multicast over Wifi from my mobile device. There is a server code running on other devices in the network. The servers will listen to the multicast and respond with their IP Address and Type of the system (Type: Computer, Mobile Device, Raspberry Pi, Flyports etc..)
On the mobile device which has sent the UDP Multicast, I need to get the list of the devices responding to the UDP Multicast.
For this I have created a class which will work as the structure of the device details.
DeviceDetails.class
public class DeviceDetails
{
String DeviceType;
String IPAddr;
public DeviceDetails(String type, String IP)
{
this.DeviceType=type;
this.IPAddr=IP;
}
}
I am sending the UDP Multicast packet at the group address of 225.4.5.6 and Port Number 5432.
I have made a class which will call a thread which will send the UDP Packets. And on the other hand I have made a receiver thread which implements Callable Interface to return the list of the devices responding.
Here is the code:
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;
}
}
The following code is of the activity which calls the 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());
}
}
}
}
Now the standard way of receiving the packet says to call the receive method under an infinite loop. But I want to receive the incoming connections only for first 30seconds and then stop looking for connections.
This is similar to that of a bluetooth searching. It stops after 1 minute of search.
Now the problem lies is, I could use a counter but the problem is thread.stop is now depricated. And not just this, if I put the receive method under infinite loop it will never return the value.
What should I do.? I want to search for say 30 seconds and then stop the search and want to return the list of the devices responding.
Instead of calling stop(), you should call interrupt(). This causes a InterruptedException to be thrown at interruptable spots at your code, e.g. when calling Thread.sleep() or when blocked by an I/O operation. Unfortunately, DatagramSocket does not implement InterruptibleChannel, so the call to receive cannot be interrupted.
So you either use DatagramChannel instead of the DatagramSocket, such that receive() will throw a ClosedByInterruptException if Thread.interrupt() is called. Or you need to set a timeout by calling DatagramSocket.setSoTimeout() causing receive() to throw a SocketTimeoutException after the specified interval - in that case, you won't need to interrupt the thread.
Simple approach
The easiest way would be to simply set a socket timeout:
public MulticastReceiver() {
try {
socket = new DatagramSocket(5500);
socket.setSoTimeout(30 * 1000);
} catch (Exception ioe) {
throw new RuntimeException(ioe);
}
}
This will cause socket.receive(inPacket); to throw a SocketTimeoutException after 30 seconds. As you already catch Exception, that's all you need to do.
Making MulticastReceiver interruptible
This is a more radical refactoring.
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);;
}
}
The DeviceManagerWindow looks a bit different; I'm not sure what you intend to do there, as you juggle around with lists and arrays, but you only have one future... So I assume you want to listen for 30 secs and fetch as many devices as possible.
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();
That's about it. No matter which solution you choose, don't forget to close the socket/channel.
I have solved it.. you can run your code in following fashion:
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());
}
here is the time bound search.... and you can quit your thread using 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.
....
}
}
Dont add any loop on the listening side. simply use 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)will set the listening time for the socket only for 3 seconds. If the packet dont arrive it will go further.DeviceManagerWindow.addDevice(device);this line will call the addDevice method in the calling class. where you can prepare your list
}
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;
}
}
Hope that works.. Well it will work.
All the best. Let me know if any problem.