/**
* Copyright 2009 Marc Stogaitis and Mimi Sun
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gmote.common;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.gmote.common.Protocol.UdpPacketTypes;
//import android.util.Log;
//import com.r3mote.server.R3moteServer;
public class MulticastClient {
private static Logger LOGGER = Logger.getLogger(MulticastClient.class.getName());
public static final String FIELD_SEPARATOR = "|";
public static final String FIELD_SEPARATOR_REGEX = "\\|";
/**
* Tries to determine the name and IP of all available servers. Returns null
* if not found.
*
* @param timeout the maximum amount of time to wait until we stop listening for servers.
* @param serverFoundHandler
* A handler that will be notified as servers are found. This is
* useful if you want to display servers in a list at they arrive.
* Pass null if you do not wish to receive notifications.
* @return
*/
public List<ServerInfo> findServers(int timeout, ServerFoundHandler serverFoundHandler) {
List<ServerInfo> servers = new ArrayList<ServerInfo>();
try {
DatagramSocket socket = new DatagramSocket();
InetAddress groupAddr = InetAddress.getByName("230.0.0.1");
byte[] outbuf = createDiscoveryRequest(socket);
sendPacket(socket, groupAddr, outbuf);
// Send out a legacy discovery request in case the server's version is <= 1.2
//outbuf = createLegacyDiscoveryRequest(socket);
//sendPacket(socket, groupAddr, outbuf);
DatagramPacket reply;
long startTime = System.currentTimeMillis();
int elapsedTime = 0;
while (elapsedTime < timeout) {
socket.setSoTimeout(Math.min(timeout - elapsedTime, 3000));
byte[] replyBuffer = new byte[1024];
reply = new DatagramPacket(replyBuffer, replyBuffer.length);
try {
socket.receive(reply);
String dataReceived = new String(reply.getData()).trim();
String[] fields = dataReceived.split(FIELD_SEPARATOR_REGEX);
if (fields.length == 4) {
ServerInfo servInfo = new ServerInfo(fields[0], fields[1], Integer.parseInt(fields[2]), Integer.parseInt(fields[3]));
servers.add(servInfo);
if (serverFoundHandler != null) {
serverFoundHandler.onServerFound(servInfo);
}
}
elapsedTime = (int) (System.currentTimeMillis() - startTime);
} catch (SocketTimeoutException ste) {
// Resend the service notification request.
elapsedTime = (int) (System.currentTimeMillis() - startTime);
if (elapsedTime < timeout) {
sendPacket(socket, groupAddr, outbuf);
}
}
}
return servers;
} catch (SocketException e) {
LOGGER.log(Level.SEVERE,e.getMessage(),e);
return servers;
} catch (IOException e) {
return servers;
}
}
private void sendPacket(DatagramSocket socket, InetAddress groupAddr, byte[] outbuf)
throws IOException {
DatagramPacket packet;
packet = new DatagramPacket(outbuf, outbuf.length, groupAddr,
9901);
outbuf = createLegacyDiscoveryRequest(socket);
socket.send(packet);
}
/**
* Used by server who's versions are <= 1.2
* @param socket
* @return
*/
private byte[] createLegacyDiscoveryRequest(DatagramSocket socket) {
byte[] outbuf = ("gmoteping" + FIELD_SEPARATOR + socket.getLocalPort()).getBytes();
return outbuf;
}
/**
* Creates a packet that has an id number and the port that we are listening
* on. We don't use any object streaming since this channel is also used for
* mouse movements packets which needs to be extremely fast.
*
* @param socket
* @return
*/
private byte[] createDiscoveryRequest(DatagramSocket socket) {
byte[] outbuf = new byte[5];
outbuf[0] = UdpPacketTypes.SERVICE_DISCOVERY.getId();
int port = socket.getLocalPort();
outbuf[1] = (byte)port;
outbuf[2] = (byte)(port >>> 8);
outbuf[3] = (byte)(port >>> 16);
outbuf[4] = (byte)(port >>> 24);
return outbuf;
}
public interface ServerFoundHandler {
/**
* Called when a server is found.
* @param server The server that was found.
*/
public void onServerFound(ServerInfo server);
}
}