blob: ad599de8cbc8c39249583f97a0b3bb553aff987d [file] [log] [blame]
danielpb913fa72013-03-03 06:23:20 +00001/**
2 *
3 */
4package org.spartanrobotics;
5
6/**
7 * @author daniel
8 * Accepts clients for data server
9 */
10
11import java.io.IOException;
12
13import java.nio.ByteBuffer;
14import java.nio.channels.ServerSocketChannel;
15import java.nio.channels.SocketChannel;
16
17import java.util.ArrayList;
18import java.util.List;
19import java.util.Map;
20import java.util.logging.Logger;
21
22/** Thread accepts new connections for the server and sends data to them
23 * without blocking.
24 */
25public class AccepterThread implements Runnable {
26
27 private final static Logger LOG = Logger.getLogger(
28 AccepterThread.class.getName());
29
30 private ServerSocketChannel sock;
31
32 private List<Client> connected = new ArrayList<Client>();
33
34 private Thread t;
35
36 /** Constructor
37 *
38 * @param sock is the ServerSocketChannel that you want to monitor
39 */
40 public AccepterThread(ServerSocketChannel sock) {
41 t = new Thread(this, "Accepter Thread");
42 t.setPriority(Thread.NORM_PRIORITY - 1);
43 //lowish priority so Image Processor overrides it
44 this.sock = sock;
45 t.start();
46 }
47
48 /** Runs in separate thread. Continually accepts new connections. */
49 public void run() {
50 SocketChannel clientSock;
51 while (true) {
52 try {
53 clientSock = sock.accept();
54 //our writes must not block
55 clientSock.configureBlocking(false);
56 Client client = new Client();
57 client.channel = clientSock;
58 connected.add(client);
59 }
60 catch (IOException e) {
61 LOG.warning("Cannot serve image processing results to client:"
62 + e.getMessage());
63 Messages.warning("Cannot serve image processing results to client:"
64 + e.getMessage());
65 }
66 }
67 }
68
69 /** Sends a message to all currently connected clients.
70 *
71 * @param message is the message that you want to send.
72 */
73 public void sendtoAll(ByteBuffer message) {
74 /* Copy our connected list, so we don't have
75 * to hold our lock forever if the writes block.
76 */
77 List<Client> connectedTemp = new ArrayList<Client>();
78 for (Client client : connected) {
79 connectedTemp.add(client);
80 }
81
82 int result;
83 for (Client client : connectedTemp) {
84 try {
85
86 /** If this socket has data from the
87 * last send operation still waiting to be
88 * sent, send this instead of our original
89 * message. Since we generally want only
90 * current data, our original message will
91 * not be missed. However, it is imperative
92 * that we finish our pending transmission,
93 * because an incomplete transmission could
94 * leave a client thread somewhere blocking
95 * indefinitely.
96 */
97 if (client.toSend != null) {
98 message = client.toSend;
99 }
100
101 result = client.channel.write(message);
102
103 /*if our send buffer is full, store our message away
104 * so we can try again later without halting the thread.
105 */
106 if (message.remaining() > 0) {
107 client.toSend = message;
108 //check and update our count of failed send attempts
109 ++client.failedAttempts;
110 if (client.failedAttempts >= 100) {
111 //Socket has become dysfunctional
112 LOG.info("Write would have blocked 100 times. Assuming peer disconect.");
113 connected.remove(client);
114 }
115 }
116
117 if (result == -1) {
118 //The write failed. This is probably because the client disconnected.
119 LOG.info("Write returned -1. Client has probably disconnected.");
120 connected.remove(client);
121 }
122 }
123 catch (IOException e) {
124 //The write failed. This is probably because the client disconnected.
125 LOG.info("Write threw IOException. Client has probably disconnected.");
126 connected.remove(client);
127 }
128 }
129 }
130
131 /** Overloaded sendtoAll method for byte arrays. */
132 public void sendtoAll(byte[] message) {
133 sendtoAll(ByteBuffer.wrap(message));
134 }
135
136 /** Overloaded sendtoAll method for Strings. */
137 public void sendtoAll(String message) {
138 sendtoAll(message.getBytes());
139 }
140}