package ch.ethz.syslab.telesto.common.network;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.channels.SocketChannel;
import ch.ethz.syslab.telesto.common.config.CONFIG;
import ch.ethz.syslab.telesto.common.model.Client;
import ch.ethz.syslab.telesto.common.protocol.Packet;
import ch.ethz.syslab.telesto.common.protocol.Packet.UnknownMethodException;
import ch.ethz.syslab.telesto.common.protocol.handler.PacketProcessingException;
import ch.ethz.syslab.telesto.common.protocol.handler.ProtocolHandler;
import ch.ethz.syslab.telesto.common.util.Log;
public class Connection {
private static Log LOGGER = new Log(Connection.class);
private DoubleBuffer receivingBuffer = new DoubleBuffer(CONFIG.MW_READ_BUFFER_SIZE);;
private DoubleBuffer sendingBuffer = new DoubleBuffer(CONFIG.MW_READ_BUFFER_SIZE);;
protected ProtocolHandler protocolHandler;
protected boolean connected = true;
protected SocketChannel socket;
public Client client;
public Connection(SocketChannel socket) {
this.socket = socket;
}
public Packet readPacket() {
if (!connected) {
LOGGER.warning("Trying to read packet from disconnected client: %s", this);
return null;
}
if (!receivingBuffer.acquire()) {
LOGGER.info("Client is being handled by another thread: %s", this);
return null;
}
receivingBuffer.prepare();
int bytesAvailable = receivingBuffer.bytesAvailable();
if (bytesAvailable < 7) {
LOGGER.fine("Received incomplete packet data (%d bytes)", bytesAvailable);
receivingBuffer.release();
return null;
}
receivingBuffer.limit(2);
int packetSize = receivingBuffer.readView.getShort();
if (bytesAvailable < packetSize + 2) {
LOGGER.fine("Received incomplete packet data (%d bytes)", bytesAvailable);
receivingBuffer.readView.reset();
receivingBuffer.release();
return null;
}
LOGGER.fine("Received packet data (%d bytes)", packetSize);
receivingBuffer.limit(packetSize + 2);
Packet packet;
try {
packet = Packet.create(receivingBuffer.readView);
} catch (UnknownMethodException e) {
LOGGER.warning(e.getMessage());
disconnect();
return null;
} catch (BufferUnderflowException e) {
LOGGER.warning("Not enough data in buffer to read packet. Size must be wrong.");
disconnect();
return null;
}
if (receivingBuffer.bytesRead() - 2 != packetSize) {
LOGGER.warning("Packet length (%d) did not match header (%d).", receivingBuffer.bytesRead() - 2, packetSize);
disconnect();
return null;
}
receivingBuffer.cleanup();
receivingBuffer.release();
return packet;
}
public boolean dataRemaining() {
return receivingBuffer.dataRemaining();
}
public void disconnect() {
connected = false;
try {
socket.close();
} catch (IOException e) {
LOGGER.warning(e, "Failed to close socket for %s", this);
}
}
public Packet handle(Packet packet) throws PacketProcessingException {
return packet.getHandled(protocolHandler);
}
public void send(Packet packet) throws IOException {
synchronized (sendingBuffer) {
packet.emit(sendingBuffer.writeView);
sendingBuffer.readView.limit(sendingBuffer.writeView.position());
socket.write(sendingBuffer.readView);
sendingBuffer.cleanup();
}
}
public int readFromChannel() throws IOException {
synchronized (receivingBuffer) {
return socket.read(receivingBuffer.writeView);
}
}
@Override
public String toString() {
String id;
if (client == null) {
try {
id = socket.getRemoteAddress().toString();
} catch (IOException e) {
id = "disconnected";
}
} else {
id = Integer.toString(client.id);
}
return String.format("Connection(%s)", id);
}
}