| /** |
| * |
| */ |
| 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()); |
| } |
| } |