picrate 0.9.0-java → 1.0.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mvn/extensions.xml +1 -1
- data/README.md +2 -2
- data/Rakefile +9 -9
- data/docs/_config.yml +1 -1
- data/docs/_posts/2018-06-26-auto_install_picrate.md +4 -4
- data/docs/index.html +0 -4
- data/lib/picrate.rb +1 -0
- data/lib/picrate/app.rb +0 -1
- data/lib/picrate/runner.rb +15 -8
- data/lib/picrate/version.rb +2 -1
- data/library/color_group/color_group.rb +1 -1
- data/library/video_event/video_event.rb +2 -1
- data/pom.rb +3 -3
- data/pom.xml +3 -3
- data/src/main/java/monkstone/MathToolModule.java +253 -203
- data/src/main/java/monkstone/videoevent/CaptureEvent.java +27 -0
- data/src/main/java/monkstone/videoevent/{VideoInterface.java → MovieEvent.java} +14 -24
- data/src/main/java/processing/net/Client.java +673 -685
- data/src/main/java/processing/net/Server.java +329 -326
- data/test/color_group_test.rb +1 -2
- data/test/deglut_spec_test.rb +0 -2
- data/test/helper_methods_test.rb +1 -2
- data/test/math_tool_test.rb +0 -2
- data/test/respond_to_test.rb +1 -2
- data/test/test_helper.rb +2 -0
- data/test/vecmath_spec_test.rb +0 -2
- data/vendors/Rakefile +4 -5
- metadata +6 -5
@@ -1,6 +1,6 @@
|
|
1
1
|
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
2
2
|
|
3
|
-
|
3
|
+
/*
|
4
4
|
Server - basic network server implementation
|
5
5
|
Part of the Processing project - http://processing.org
|
6
6
|
|
@@ -21,365 +21,368 @@
|
|
21
21
|
Public License along with this library; if not, write to the
|
22
22
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
23
23
|
Boston, MA 02111-1307 USA
|
24
|
-
|
24
|
+
*/
|
25
|
+
|
25
26
|
package processing.net;
|
26
27
|
|
27
|
-
import
|
28
|
-
|
29
|
-
import java.
|
30
|
-
import java.
|
31
|
-
import java.net
|
32
|
-
|
33
|
-
import java.net.SocketException;
|
34
|
-
import java.net.UnknownHostException;
|
35
|
-
import processing.core.PApplet;
|
28
|
+
import processing.core.*;
|
29
|
+
|
30
|
+
import java.io.*;
|
31
|
+
import java.lang.reflect.*;
|
32
|
+
import java.net.*;
|
33
|
+
|
36
34
|
|
37
35
|
/**
|
38
36
|
* ( begin auto-generated from Server.xml )
|
39
|
-
*
|
40
|
-
* A server sends and receives data to and from its associated clients
|
41
|
-
* programs connected to it). When a server is started, it begins
|
42
|
-
* connections on the port specified by the <b>port</b>
|
43
|
-
* parameter. Computers have many ports for transferring data and some are
|
44
|
-
* commonly used so be sure to not select one of these. For example, web
|
45
|
-
* usually use port 80 and POP mail uses port 110.
|
46
|
-
*
|
37
|
+
*
|
38
|
+
* A server sends and receives data to and from its associated clients
|
39
|
+
* (other programs connected to it). When a server is started, it begins
|
40
|
+
* listening for connections on the port specified by the <b>port</b>
|
41
|
+
* parameter. Computers have many ports for transferring data and some are
|
42
|
+
* commonly used so be sure to not select one of these. For example, web
|
43
|
+
* servers usually use port 80 and POP mail uses port 110.
|
44
|
+
*
|
47
45
|
* ( end auto-generated )
|
48
|
-
*
|
49
46
|
* @webref net
|
50
47
|
* @usage application
|
51
|
-
* @brief The server class is used to create server objects which send and
|
52
|
-
*
|
53
|
-
* it).
|
54
|
-
* @instanceName server any variable of type Server
|
48
|
+
* @brief The server class is used to create server objects which send and receives data to and from its associated clients (other programs connected to it).
|
49
|
+
* @instanceName server any variable of type Server
|
55
50
|
*/
|
56
51
|
public class Server implements Runnable {
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
52
|
+
PApplet parent;
|
53
|
+
Method serverEventMethod;
|
54
|
+
|
55
|
+
volatile Thread thread;
|
56
|
+
ServerSocket server;
|
57
|
+
int port;
|
58
|
+
|
59
|
+
protected final Object clientsLock = new Object[0];
|
60
|
+
/** Number of clients currently connected. */
|
61
|
+
public int clientCount;
|
62
|
+
/** Array of client objects, useful length is determined by clientCount. */
|
63
|
+
public Client[] clients;
|
64
|
+
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @param parent typically use "this"
|
68
|
+
* @param port port used to transfer data
|
69
|
+
*/
|
70
|
+
public Server(PApplet parent, int port) {
|
71
|
+
this(parent, port, null);
|
72
|
+
}
|
73
|
+
|
74
|
+
|
75
|
+
/**
|
76
|
+
* @param parent typically use "this"
|
77
|
+
* @param port port used to transfer data
|
78
|
+
* @param host when multiple NICs are in use, the ip (or name) to bind from
|
79
|
+
*/
|
80
|
+
public Server(PApplet parent, int port, String host) {
|
81
|
+
this.parent = parent;
|
82
|
+
this.port = port;
|
83
|
+
|
84
|
+
try {
|
85
|
+
if (host == null) {
|
86
|
+
server = new ServerSocket(this.port);
|
87
|
+
} else {
|
88
|
+
server = new ServerSocket(this.port, 10, InetAddress.getByName(host));
|
89
|
+
}
|
90
|
+
//clients = new Vector();
|
91
|
+
clients = new Client[10];
|
92
|
+
|
93
|
+
thread = new Thread(this);
|
94
|
+
thread.start();
|
95
|
+
|
96
|
+
parent.registerMethod("dispose", this);
|
97
|
+
|
98
|
+
// reflection to check whether host applet has a call for
|
99
|
+
// public void serverEvent(Server s, Client c);
|
100
|
+
// which is called when a new guy connects
|
101
|
+
try {
|
102
|
+
serverEventMethod =
|
103
|
+
parent.getClass().getMethod("serverEvent", Server.class, Client.class);
|
104
|
+
} catch (Exception e) {
|
105
|
+
// no such method, or an error.. which is fine, just ignore
|
106
|
+
}
|
107
|
+
|
108
|
+
} catch (IOException e) {
|
109
|
+
//e.printStackTrace();
|
110
|
+
thread = null;
|
111
|
+
throw new RuntimeException(e);
|
112
|
+
//errorMessage("<init>", e);
|
81
113
|
}
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
thread.start();
|
103
|
-
|
104
|
-
parent.registerMethod("dispose", this);
|
105
|
-
|
106
|
-
// reflection to check whether host applet has a call for
|
107
|
-
// public void serverEvent(Server s, Client c);
|
108
|
-
// which is called when a new guy connects
|
109
|
-
try {
|
110
|
-
serverEventMethod
|
111
|
-
= parent.getClass().getMethod("serverEvent", Server.class, Client.class);
|
112
|
-
} catch (NoSuchMethodException | SecurityException e) {
|
113
|
-
// no such method, or an error.. which is fine, just ignore
|
114
|
-
}
|
115
|
-
|
116
|
-
} catch (IOException e) {
|
117
|
-
//e.printStackTrace();
|
118
|
-
thread = null;
|
119
|
-
throw new RuntimeException(e);
|
120
|
-
//errorMessage("<init>", e);
|
121
|
-
}
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
/**
|
118
|
+
* ( begin auto-generated from Server_disconnect.xml )
|
119
|
+
*
|
120
|
+
* Disconnect a particular client.
|
121
|
+
*
|
122
|
+
* ( end auto-generated )
|
123
|
+
* @brief Disconnect a particular client.
|
124
|
+
* @webref server:server
|
125
|
+
* @param client the client to disconnect
|
126
|
+
*/
|
127
|
+
public void disconnect(Client client) {
|
128
|
+
client.stop();
|
129
|
+
synchronized (clientsLock) {
|
130
|
+
int index = clientIndex(client);
|
131
|
+
if (index != -1) {
|
132
|
+
removeIndex(index);
|
133
|
+
}
|
122
134
|
}
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
public void disconnect(Client client) {
|
136
|
-
client.stop();
|
137
|
-
synchronized (clientsLock) {
|
138
|
-
int index = clientIndex(client);
|
139
|
-
if (index != -1) {
|
140
|
-
removeIndex(index);
|
141
|
-
}
|
142
|
-
}
|
143
|
-
}
|
144
|
-
|
145
|
-
protected void removeIndex(int index) {
|
146
|
-
synchronized (clientsLock) {
|
147
|
-
clientCount--;
|
148
|
-
// shift down the remaining clients
|
149
|
-
for (int i = index; i < clientCount; i++) {
|
150
|
-
clients[i] = clients[i + 1];
|
151
|
-
}
|
152
|
-
// mark last empty var for garbage collection
|
153
|
-
clients[clientCount] = null;
|
154
|
-
}
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
protected void removeIndex(int index) {
|
139
|
+
synchronized (clientsLock) {
|
140
|
+
clientCount--;
|
141
|
+
// shift down the remaining clients
|
142
|
+
for (int i = index; i < clientCount; i++) {
|
143
|
+
clients[i] = clients[i + 1];
|
144
|
+
}
|
145
|
+
// mark last empty var for garbage collection
|
146
|
+
clients[clientCount] = null;
|
155
147
|
}
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
}
|
167
|
-
clientCount = 0;
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
protected void disconnectAll() {
|
152
|
+
synchronized (clientsLock) {
|
153
|
+
for (int i = 0; i < clientCount; i++) {
|
154
|
+
try {
|
155
|
+
clients[i].stop();
|
156
|
+
} catch (Exception e) {
|
157
|
+
// ignore
|
168
158
|
}
|
159
|
+
clients[i] = null;
|
160
|
+
}
|
161
|
+
clientCount = 0;
|
169
162
|
}
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
163
|
+
}
|
164
|
+
|
165
|
+
|
166
|
+
protected void addClient(Client client) {
|
167
|
+
synchronized (clientsLock) {
|
168
|
+
if (clientCount == clients.length) {
|
169
|
+
clients = (Client[]) PApplet.expand(clients);
|
170
|
+
}
|
171
|
+
clients[clientCount++] = client;
|
178
172
|
}
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
return -1;
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
protected int clientIndex(Client client) {
|
177
|
+
synchronized (clientsLock) {
|
178
|
+
for (int i = 0; i < clientCount; i++) {
|
179
|
+
if (clients[i] == client) {
|
180
|
+
return i;
|
188
181
|
}
|
182
|
+
}
|
183
|
+
return -1;
|
189
184
|
}
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
/**
|
189
|
+
* ( begin auto-generated from Server_active.xml )
|
190
|
+
*
|
191
|
+
* Returns true if this server is still active and hasn't run
|
192
|
+
* into any trouble.
|
193
|
+
*
|
194
|
+
* ( end auto-generated )
|
195
|
+
* @webref server:server
|
196
|
+
* @brief Return true if this server is still active.
|
197
|
+
*/
|
198
|
+
public boolean active() {
|
199
|
+
return thread != null;
|
200
|
+
}
|
201
|
+
|
202
|
+
|
203
|
+
static public String ip() {
|
204
|
+
try {
|
205
|
+
return InetAddress.getLocalHost().getHostAddress();
|
206
|
+
} catch (UnknownHostException e) {
|
207
|
+
e.printStackTrace();
|
208
|
+
return null;
|
204
209
|
}
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
210
|
+
}
|
211
|
+
|
212
|
+
|
213
|
+
// the last index used for available. can't just cycle through
|
214
|
+
// the clients in order from 0 each time, because if client 0 won't
|
215
|
+
// shut up, then the rest of the clients will never be heard from.
|
216
|
+
int lastAvailable = -1;
|
217
|
+
|
218
|
+
/**
|
219
|
+
* ( begin auto-generated from Server_available.xml )
|
220
|
+
*
|
221
|
+
* Returns the next client in line with a new message.
|
222
|
+
*
|
223
|
+
* ( end auto-generated )
|
224
|
+
* @brief Returns the next client in line with a new message.
|
225
|
+
* @webref server
|
226
|
+
* @usage application
|
227
|
+
*/
|
228
|
+
public Client available() {
|
229
|
+
synchronized (clientsLock) {
|
230
|
+
int index = lastAvailable + 1;
|
231
|
+
if (index >= clientCount) index = 0;
|
232
|
+
|
233
|
+
for (int i = 0; i < clientCount; i++) {
|
234
|
+
int which = (index + i) % clientCount;
|
235
|
+
Client client = clients[which];
|
236
|
+
//Check for valid client
|
237
|
+
if (!client.active()){
|
238
|
+
removeIndex(which); //Remove dead client
|
239
|
+
i--; //Don't skip the next client
|
240
|
+
//If the client has data make sure lastAvailable
|
241
|
+
//doesn't end up skipping the next client
|
242
|
+
which--;
|
243
|
+
//fall through to allow data from dead clients
|
244
|
+
//to be retreived.
|
211
245
|
}
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
// the clients in order from 0 each time, because if client 0 won't
|
216
|
-
// shut up, then the rest of the clients will never be heard from.
|
217
|
-
int lastAvailable = -1;
|
218
|
-
|
219
|
-
/**
|
220
|
-
* ( begin auto-generated from Server_available.xml )
|
221
|
-
*
|
222
|
-
* Returns the next client in line with a new message.( end auto-generated )
|
223
|
-
*
|
224
|
-
*
|
225
|
-
* @return
|
226
|
-
* @brief Returns the next client in line with a new message.
|
227
|
-
* @webref server
|
228
|
-
* @usage application
|
229
|
-
*/
|
230
|
-
public Client available() {
|
231
|
-
synchronized (clientsLock) {
|
232
|
-
int index = lastAvailable + 1;
|
233
|
-
if (index >= clientCount) {
|
234
|
-
index = 0;
|
235
|
-
}
|
236
|
-
|
237
|
-
for (int i = 0; i < clientCount; i++) {
|
238
|
-
int which = (index + i) % clientCount;
|
239
|
-
Client client = clients[which];
|
240
|
-
//Check for valid client
|
241
|
-
if (!client.active()) {
|
242
|
-
removeIndex(which); //Remove dead client
|
243
|
-
i--; //Don't skip the next client
|
244
|
-
//If the client has data make sure lastAvailable
|
245
|
-
//doesn't end up skipping the next client
|
246
|
-
which--;
|
247
|
-
//fall through to allow data from dead clients
|
248
|
-
//to be retreived.
|
249
|
-
}
|
250
|
-
if (client.available() > 0) {
|
251
|
-
lastAvailable = which;
|
252
|
-
return client;
|
253
|
-
}
|
254
|
-
}
|
246
|
+
if (client.available() > 0) {
|
247
|
+
lastAvailable = which;
|
248
|
+
return client;
|
255
249
|
}
|
256
|
-
|
250
|
+
}
|
257
251
|
}
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
252
|
+
return null;
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
/**
|
257
|
+
* ( begin auto-generated from Server_stop.xml )
|
258
|
+
*
|
259
|
+
* Disconnects all clients and stops the server.
|
260
|
+
*
|
261
|
+
* ( end auto-generated )
|
262
|
+
* <h3>Advanced</h3>
|
263
|
+
* Use this to shut down the server if you finish using it while your applet
|
264
|
+
* is still running. Otherwise, it will be automatically be shut down by the
|
265
|
+
* host PApplet using dispose(), which is identical.
|
266
|
+
* @brief Disconnects all clients and stops the server.
|
267
|
+
* @webref server
|
268
|
+
* @usage application
|
269
|
+
*/
|
270
|
+
public void stop() {
|
271
|
+
dispose();
|
272
|
+
}
|
273
|
+
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Disconnect all clients and stop the server: internal use only.
|
277
|
+
*/
|
278
|
+
public void dispose() {
|
279
|
+
thread = null;
|
280
|
+
|
281
|
+
if (clients != null) {
|
282
|
+
disconnectAll();
|
283
|
+
clientCount = 0;
|
284
|
+
clients = null;
|
276
285
|
}
|
277
286
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
disconnectAll();
|
286
|
-
clientCount = 0;
|
287
|
-
clients = null;
|
288
|
-
}
|
289
|
-
|
290
|
-
try {
|
291
|
-
if (server != null) {
|
292
|
-
server.close();
|
293
|
-
server = null;
|
294
|
-
}
|
295
|
-
} catch (IOException e) {
|
296
|
-
}
|
287
|
+
try {
|
288
|
+
if (server != null) {
|
289
|
+
server.close();
|
290
|
+
server = null;
|
291
|
+
}
|
292
|
+
} catch (IOException e) {
|
293
|
+
e.printStackTrace();
|
297
294
|
}
|
295
|
+
}
|
296
|
+
|
298
297
|
|
299
|
-
|
300
|
-
|
301
|
-
|
298
|
+
@Override
|
299
|
+
public void run() {
|
300
|
+
while (Thread.currentThread() == thread) {
|
301
|
+
try {
|
302
|
+
Socket socket = server.accept();
|
303
|
+
Client client = new Client(parent, socket);
|
304
|
+
synchronized (clientsLock) {
|
305
|
+
addClient(client);
|
306
|
+
if (serverEventMethod != null) {
|
302
307
|
try {
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
// unwrap the exception if it came from the user code
|
314
|
-
if (e instanceof InvocationTargetException && e.getCause() != null) {
|
315
|
-
cause = e.getCause();
|
316
|
-
}
|
317
|
-
serverEventMethod = null;
|
318
|
-
}
|
319
|
-
}
|
320
|
-
}
|
321
|
-
} catch (SocketException e) {
|
322
|
-
//thrown when server.close() is called and server is waiting on accept
|
323
|
-
System.err.println("Server SocketException: " + e.getMessage());
|
324
|
-
thread = null;
|
325
|
-
} catch (IOException e) {
|
326
|
-
//errorMessage("run", e);
|
327
|
-
thread = null;
|
308
|
+
serverEventMethod.invoke(parent, this, client);
|
309
|
+
} catch (Exception e) {
|
310
|
+
System.err.println("Disabling serverEvent() for port " + port);
|
311
|
+
Throwable cause = e;
|
312
|
+
// unwrap the exception if it came from the user code
|
313
|
+
if (e instanceof InvocationTargetException && e.getCause() != null) {
|
314
|
+
cause = e.getCause();
|
315
|
+
}
|
316
|
+
cause.printStackTrace();
|
317
|
+
serverEventMethod = null;
|
328
318
|
}
|
319
|
+
}
|
329
320
|
}
|
321
|
+
} catch (SocketException e) {
|
322
|
+
//thrown when server.close() is called and server is waiting on accept
|
323
|
+
System.err.println("Server SocketException: " + e.getMessage());
|
324
|
+
thread = null;
|
325
|
+
} catch (IOException e) {
|
326
|
+
//errorMessage("run", e);
|
327
|
+
e.printStackTrace();
|
328
|
+
thread = null;
|
329
|
+
}
|
330
330
|
}
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
}
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
/**
|
335
|
+
* ( begin auto-generated from Server_write.xml )
|
336
|
+
*
|
337
|
+
* Writes a value to all the connected clients. It sends bytes out from the
|
338
|
+
* Server object.
|
339
|
+
*
|
340
|
+
* ( end auto-generated )
|
341
|
+
* @webref server
|
342
|
+
* @brief Writes data to all connected clients
|
343
|
+
* @param data data to write
|
344
|
+
*/
|
345
|
+
public void write(int data) { // will also cover char
|
346
|
+
synchronized (clientsLock) {
|
347
|
+
int index = 0;
|
348
|
+
while (index < clientCount) {
|
349
|
+
if (clients[index].active()) {
|
350
|
+
clients[index].write(data);
|
351
|
+
index++;
|
352
|
+
} else {
|
353
|
+
removeIndex(index);
|
355
354
|
}
|
355
|
+
}
|
356
356
|
}
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
357
|
+
}
|
358
|
+
|
359
|
+
|
360
|
+
public void write(byte data[]) {
|
361
|
+
synchronized (clientsLock) {
|
362
|
+
int index = 0;
|
363
|
+
while (index < clientCount) {
|
364
|
+
if (clients[index].active()) {
|
365
|
+
clients[index].write(data);
|
366
|
+
index++;
|
367
|
+
} else {
|
368
|
+
removeIndex(index);
|
369
369
|
}
|
370
|
+
}
|
370
371
|
}
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
372
|
+
}
|
373
|
+
|
374
|
+
|
375
|
+
public void write(String data) {
|
376
|
+
synchronized (clientsLock) {
|
377
|
+
int index = 0;
|
378
|
+
while (index < clientCount) {
|
379
|
+
if (clients[index].active()) {
|
380
|
+
clients[index].write(data);
|
381
|
+
index++;
|
382
|
+
} else {
|
383
|
+
removeIndex(index);
|
383
384
|
}
|
385
|
+
}
|
384
386
|
}
|
387
|
+
}
|
385
388
|
}
|