package edu.washington.cs.oneswarm.f2f.dht;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.Queue;
public class CHTClientUDP implements CHTClientInterface {
private static final int RECEIVE_TIME_OUT = 4000;
private final static int MAX_QUEUE_LENGTH = 200;
private final int serverPort;
private final String server;
private final Queue<CHTGetJob> jobs = new LinkedList<CHTGetJob>();
private Thread jobExecutorThread;
public CHTClientUDP(String server, int serverPort) throws UnknownHostException {
this.server = server;
this.serverPort = serverPort;
}
@Override
public void put(byte[] key, byte[] value) throws IOException {
if (key == null || key.length != 20) {
throw new RuntimeException("Key length must be 20");
}
InetAddress serverAddr = InetAddress.getByName(server);
DatagramSocket s = new DatagramSocket();
byte[] payload = new byte[1 + 20 + value.length];
payload[0] = 0;
System.arraycopy(key, 0, payload, 1, key.length);
System.arraycopy(value, 0, payload, 21, value.length);
DatagramPacket p = new DatagramPacket(payload, payload.length, serverAddr, serverPort);
s.send(p);
s.close();
}
@Override
public void get(final byte[] key, final CHTCallback callback) {
if (key == null || key.length != 20) {
throw new RuntimeException("Key length must be 20");
}
synchronized (jobs) {
if (jobs.size() > MAX_QUEUE_LENGTH) {
callback.errorReceived(new Exception("max queue length reached"));
} else {
// System.out.println("adding job to queue, size=" +
// jobs.size());
jobs.add(new CHTGetJob(key, callback));
if (jobExecutorThread == null) {
jobExecutorThread = new Thread(new Runnable() {
@Override
public void run() {
// System.out.println("starting new thread");
DatagramSocket s = null;
try {
s = new DatagramSocket();
s.setSoTimeout(RECEIVE_TIME_OUT);
CHTGetJob job;
do {
/*
* grab the first element
*/
synchronized (jobs) {
job = jobs.poll();
// System.out.println(
// "grabbing job from queue, size=" +
// jobs.size());
if (job == null) {
jobExecutorThread = null;
}
}
if (job != null) {
try {
job.execute(s);
} catch (Throwable t) {
job.cb.errorReceived(t);
}
}
} while (job != null);
// System.out.println("Thread completed");
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (s != null) {
try {
s.close();
} catch (Throwable t) {
}
}
}
}
});
jobExecutorThread.setDaemon(true);
jobExecutorThread.setName("CHT client");
jobExecutorThread.start();
}
}
}
}
class CHTGetJob {
private final byte[] key;
private final CHTCallback cb;
public CHTGetJob(final byte[] key, final CHTCallback callback) {
this.key = key;
this.cb = callback;
}
public void execute(DatagramSocket s) throws IOException {
long startTime = System.currentTimeMillis();
InetAddress serverAddr = InetAddress.getByName(server);
byte[] payload = new byte[1 + 20];
payload[0] = 1;
System.arraycopy(key, 0, payload, 1, key.length);
DatagramPacket p = new DatagramPacket(payload, payload.length, serverAddr, serverPort);
s.send(p);
byte[] incomingBuffer = new byte[1500];
DatagramPacket incomingPacket = new DatagramPacket(incomingBuffer,
incomingBuffer.length);
s.receive(incomingPacket);
if (incomingPacket.getLength() > 0) {
byte[] value = new byte[incomingPacket.getLength()];
System.arraycopy(incomingPacket.getData(), 0, value, 0, value.length);
cb.valueReceived(key, value);
} else {
cb.errorReceived(new Exception("Key not in CHT"));
}
/*
* rate limit the jobs, they need to take at least 200ms to avoid
* getting dropped at the server
*/
try {
Thread.sleep(250);
} catch (InterruptedException e) {
}
}
}
}