blob: ad599de8cbc8c39249583f97a0b3bb553aff987d [file] [log] [blame]
/**
*
*/
package org.spartanrobotics;
/**
* @author daniel
* Accepts clients for data server
*/
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/** Thread accepts new connections for the server and sends data to them
* without blocking.
*/
public class AccepterThread implements Runnable {
private final static Logger LOG = Logger.getLogger(
AccepterThread.class.getName());
private ServerSocketChannel sock;
private List<Client> connected = new ArrayList<Client>();
private Thread t;
/** Constructor
*
* @param sock is the ServerSocketChannel that you want to monitor
*/
public AccepterThread(ServerSocketChannel sock) {
t = new Thread(this, "Accepter Thread");
t.setPriority(Thread.NORM_PRIORITY - 1);
//lowish priority so Image Processor overrides it
this.sock = sock;
t.start();
}
/** Runs in separate thread. Continually accepts new connections. */
public void run() {
SocketChannel clientSock;
while (true) {
try {
clientSock = sock.accept();
//our writes must not block
clientSock.configureBlocking(false);
Client client = new Client();
client.channel = clientSock;
connected.add(client);
}
catch (IOException e) {
LOG.warning("Cannot serve image processing results to client:"
+ e.getMessage());
Messages.warning("Cannot serve image processing results to client:"
+ e.getMessage());
}
}
}
/** Sends a message to all currently connected clients.
*
* @param message is the message that you want to send.
*/
public void sendtoAll(ByteBuffer message) {
/* Copy our connected list, so we don't have
* to hold our lock forever if the writes block.
*/
List<Client> connectedTemp = new ArrayList<Client>();
for (Client client : connected) {
connectedTemp.add(client);
}
int result;
for (Client client : connectedTemp) {
try {
/** If this socket has data from the
* last send operation still waiting to be
* sent, send this instead of our original
* message. Since we generally want only
* current data, our original message will
* not be missed. However, it is imperative
* that we finish our pending transmission,
* because an incomplete transmission could
* leave a client thread somewhere blocking
* indefinitely.
*/
if (client.toSend != null) {
message = client.toSend;
}
result = client.channel.write(message);
/*if our send buffer is full, store our message away
* so we can try again later without halting the thread.
*/
if (message.remaining() > 0) {
client.toSend = message;
//check and update our count of failed send attempts
++client.failedAttempts;
if (client.failedAttempts >= 100) {
//Socket has become dysfunctional
LOG.info("Write would have blocked 100 times. Assuming peer disconect.");
connected.remove(client);
}
}
if (result == -1) {
//The write failed. This is probably because the client disconnected.
LOG.info("Write returned -1. Client has probably disconnected.");
connected.remove(client);
}
}
catch (IOException e) {
//The write failed. This is probably because the client disconnected.
LOG.info("Write threw IOException. Client has probably disconnected.");
connected.remove(client);
}
}
}
/** Overloaded sendtoAll method for byte arrays. */
public void sendtoAll(byte[] message) {
sendtoAll(ByteBuffer.wrap(message));
}
/** Overloaded sendtoAll method for Strings. */
public void sendtoAll(String message) {
sendtoAll(message.getBytes());
}
}