blob: 1cd71e8016b69027ef294073d7ee97ad3e39337c [file] [log] [blame]
danielp64c4e052013-02-23 07:21:41 +00001/**
2 *
3 */
4package org.frc971;
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
22public class AccepterThread extends Thread {
23
24 private final static Logger LOG = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
25
26 private ServerSocketChannel sock;
27
28 private List<SocketChannel> connected = new ArrayList<SocketChannel>();
29
30 /* Holds overflow data when socket's send buffer gets full, so that
31 * thread can continue running.
32 */
33 private Map<SocketChannel, ByteBuffer> toSend;
34 /* Keeps track of how many times a write operation on a socket
35 * has failed because it's buffer was full.
36 */
37 private Map<SocketChannel, Integer> failedAttempts; //doesn't like primitive types
38
39 /** Helper function to completely erase a peer from
40 * all three lists and maps that might contain it.
41 */
42 private void erasePeer(SocketChannel peer) {
43 connected.remove(peer);
44 toSend.remove(peer);
45 failedAttempts.remove(peer);
46 }
47
48 /** Constructor
49 *
50 * @param sock is the ServerSocketChannel that you want to monitor
51 */
52 public AccepterThread(ServerSocketChannel sock) {
53 super("Accepter Thread");
54 setPriority(3); //lowish priority so Image Processor overrides it
55 this.sock = sock;
56 start();
57 }
58
59 /** Runs in separate thread. Continually accepts new connections. */
60 public void run() {
61 SocketChannel clientSock;
62 while (true) {
63 try {
64 clientSock = sock.accept();
65 //our writes must not block
66 clientSock.configureBlocking(false);
67 connected.add(clientSock);
68 }
69 catch (IOException e) {
danielp3c598e52013-02-24 06:12:54 +000070 LOG.warning("Cannot serve image processing results to client:" + e.getMessage());
71 Messages.warning("Cannot serve image processing results to client:" + e.getMessage());
danielp64c4e052013-02-23 07:21:41 +000072 }
73 }
74 }
75
76 /** Sends a message to all currently connected clients.
77 *
78 * @param message is the message that you want to send.
79 */
80 public void sendtoAll(ByteBuffer message) {
81 /* Copy our connected list, so we don't have
82 * to hold our lock forever if the writes block.
83 */
84 List<SocketChannel> connectedTemp = new ArrayList<SocketChannel>();
85 for (SocketChannel channel : connected) {
86 connectedTemp.add(channel);
87 }
88
89 int result;
90 for (SocketChannel conn : connectedTemp) {
91 try {
92
93 /** If this socket has data from the
94 * last send operation still waiting to be
95 * sent, send this instead of our original
96 * message. Since we generally want only
97 * current data, our original message will
98 * not be missed. However, it is imperative
99 * that we finish our pending transmission,
100 * because an incomplete transmission could
101 * leave a client thread somewhere blocking
102 * indefinitely.
103 */
104 if (toSend.containsKey(conn)) {
105 message = toSend.get(conn);
106 }
107
108 result = conn.write(message);
109
110 /*if our send buffer is full, store our message away
111 * so we can try again later without halting the thread.
112 */
113 if (message.remaining() > 0) {
114 toSend.put(conn, message);
115 //check and update our count of failed send attempts
116 if (failedAttempts.containsKey(conn)) {
117 int failures = failedAttempts.get(conn);
118 ++failures;
119 if (failures >= 100) {
120 //Socket has become dysfunctional
121 LOG.info("Write would have blocked 100 times. Assuming peer disconect.");
122 erasePeer(conn);
123 }
124 failedAttempts.put(conn, failures);
125 }
126 else {
127 failedAttempts.put(conn, 1);
128 }
129 }
130
131 if (result == -1) {
132 //The write failed. This is probably because the client disconnected.
133 LOG.info("Write returned -1. Client has probably disconnected.");
134 erasePeer(conn);
135 }
136 }
137 catch (IOException e) {
138 //The write failed. This is probably because the client disconnected.
139 LOG.info("Write threw IOException. Client has probably disconnected.");
140 erasePeer(conn);
141 }
142 }
143 }
144
145 /** Overloaded sendtoAll method for byte arrays. */
146 public void sendtoAll(byte[] message) {
147 sendtoAll(ByteBuffer.wrap(message));
148 }
149
150 /** Overloaded sendtoAll method for Strings. */
151 public void sendtoAll(String message) {
152 sendtoAll(message.getBytes());
153 }
154}