root / trunk / code / projects / colonet / ColonetServer / ConnectionPool.cpp @ 24
History | View | Annotate | Download (12.1 KB)
1 |
/**
|
---|---|
2 |
* @author Jason Knichel
|
3 |
* @date 7/22/07
|
4 |
*/
|
5 |
|
6 |
#include <sys/select.h> |
7 |
#include <ctype.h> |
8 |
#include <errno.h> |
9 |
#include <unistd.h> |
10 |
#include <string.h> |
11 |
#include <stdlib.h> |
12 |
#include <stdio.h> |
13 |
|
14 |
#include "includes/ConnectionPool.h" |
15 |
#include "includes/client.h" |
16 |
#include "../lib/colonet_defs.h" |
17 |
|
18 |
ConnectionPool::ConnectionPool() { |
19 |
max_file_descriptor = 0;
|
20 |
next_available_slot = 0;
|
21 |
number_clients_ready = 0;
|
22 |
|
23 |
FD_ZERO(&ready_set); |
24 |
FD_ZERO(&read_set); |
25 |
FD_ZERO(&write_set); |
26 |
|
27 |
memset(&client_file_descriptor_array, 0, sizeof(int)*MAX_CONNECTIONS); |
28 |
memset(&read_buffer, 0, sizeof(char *)*MAX_CONNECTIONS); |
29 |
memset(&read_buffer_size, 0, sizeof(int)*MAX_CONNECTIONS); |
30 |
memset(&write_buffer, 0, sizeof(char *)*MAX_CONNECTIONS); |
31 |
memset(&write_buffer_size, 0, sizeof(int)*MAX_CONNECTIONS); |
32 |
} |
33 |
|
34 |
ConnectionPool::~ConnectionPool() { |
35 |
} |
36 |
|
37 |
int ConnectionPool::add_client(int client_file_descriptor) { |
38 |
if (client_file_descriptor < 0) { |
39 |
return ERROR_INVALID_CLIENT_DESCRIPTOR;
|
40 |
} |
41 |
|
42 |
if (next_available_slot == MAX_CONNECTIONS) {
|
43 |
return ERROR_TOO_MANY_CLIENTS;
|
44 |
} |
45 |
|
46 |
if (client_file_descriptor > max_file_descriptor) {
|
47 |
max_file_descriptor = client_file_descriptor; |
48 |
} |
49 |
|
50 |
FD_SET(client_file_descriptor, &ready_set); |
51 |
|
52 |
int next_slot = next_available_slot;
|
53 |
|
54 |
client_file_descriptor_array[next_slot] = client_file_descriptor; |
55 |
read_buffer[next_slot] = (char*) malloc(sizeof(char) * READ_BUFFER_SIZE); |
56 |
if (!(read_buffer[next_slot])) {
|
57 |
return ERROR_ALLOCATING_MEMORY;
|
58 |
} |
59 |
read_buffer_size[next_slot] = 0;
|
60 |
write_buffer[next_slot] = (char *)malloc(sizeof(char) * WRITE_BUFFER_SIZE); |
61 |
|
62 |
if (!(write_buffer[next_slot])) {
|
63 |
free(read_buffer[next_slot]); |
64 |
return ERROR_ALLOCATING_MEMORY;
|
65 |
} |
66 |
|
67 |
write_buffer_size[next_slot] = 0;
|
68 |
|
69 |
next_available_slot++; |
70 |
|
71 |
return 0; |
72 |
} |
73 |
|
74 |
int ConnectionPool::remove_client(int client_file_descriptor) { |
75 |
if (client_file_descriptor < 0 || client_file_descriptor >= next_available_slot) { |
76 |
return ERROR_INVALID_CLIENT_DESCRIPTOR;
|
77 |
} |
78 |
|
79 |
int clientfd = client_file_descriptor_array[client_file_descriptor];
|
80 |
|
81 |
if (FD_ISSET(clientfd, &ready_set)) {
|
82 |
FD_CLR(clientfd, &ready_set); |
83 |
} |
84 |
if (FD_ISSET(clientfd, &read_set)) {
|
85 |
FD_CLR(clientfd, &read_set); |
86 |
} |
87 |
if (FD_ISSET(clientfd, &write_set)) {
|
88 |
FD_CLR(clientfd, &write_set); |
89 |
} |
90 |
|
91 |
free(read_buffer[client_file_descriptor]); |
92 |
free(write_buffer[client_file_descriptor]); |
93 |
for (int j = client_file_descriptor; j < next_available_slot - 1; j++) { |
94 |
client_file_descriptor_array[client_file_descriptor] = client_file_descriptor_array[client_file_descriptor+1];
|
95 |
read_buffer[client_file_descriptor] = read_buffer[client_file_descriptor+1];
|
96 |
read_buffer_size[client_file_descriptor] = read_buffer_size[client_file_descriptor+1];
|
97 |
write_buffer[client_file_descriptor] = write_buffer[client_file_descriptor+1];
|
98 |
write_buffer_size[client_file_descriptor] = write_buffer_size[client_file_descriptor+1];
|
99 |
} |
100 |
next_available_slot--; |
101 |
int temp_max_file_descriptor = 0; |
102 |
|
103 |
for (int j = 0; j < next_available_slot; j++) { |
104 |
if (client_file_descriptor_array[j] > temp_max_file_descriptor)
|
105 |
temp_max_file_descriptor = client_file_descriptor_array[j]; |
106 |
} |
107 |
max_file_descriptor = temp_max_file_descriptor; |
108 |
|
109 |
printf("Removing client.\n");
|
110 |
|
111 |
return 0; |
112 |
} |
113 |
|
114 |
//TODO: test that it drops commands properly if it gets sent too much data
|
115 |
// do we want it to drop the data or drop the connection?
|
116 |
//TODO: give variables a better name
|
117 |
int ConnectionPool::check_clients(ColonetWireless * wireless) {
|
118 |
char temp[READ_BUFFER_SIZE];
|
119 |
char tempCommand[READ_BUFFER_SIZE+1]; |
120 |
int i;
|
121 |
int client_file_descriptor;
|
122 |
int n;
|
123 |
int len;
|
124 |
int sent;
|
125 |
int commandLen;
|
126 |
|
127 |
for (i = 0; i < next_available_slot; i++) { |
128 |
client_file_descriptor = client_file_descriptor_array[i]; |
129 |
|
130 |
if (FD_ISSET(client_file_descriptor, &read_set)) {
|
131 |
n = read(client_file_descriptor, temp, READ_BUFFER_SIZE); |
132 |
|
133 |
if (n == 0 || (n == -1 && errno == ECONNRESET)) { |
134 |
remove_client(i); |
135 |
i--; |
136 |
continue;
|
137 |
} |
138 |
|
139 |
while (n > 0) { |
140 |
len = n; |
141 |
|
142 |
if (len + read_buffer_size[i] > READ_BUFFER_SIZE) {
|
143 |
len = READ_BUFFER_SIZE - read_buffer_size[i]; |
144 |
} |
145 |
|
146 |
memcpy(read_buffer[i]+read_buffer_size[i], temp, len); |
147 |
read_buffer_size[i] += len; |
148 |
n -= len; |
149 |
|
150 |
if (n > 0) { |
151 |
memmove(temp, temp+len, READ_BUFFER_SIZE - len); |
152 |
} |
153 |
|
154 |
printf("Read buffer is %s\n", read_buffer[i]);
|
155 |
|
156 |
char* newLine;
|
157 |
|
158 |
while ((newLine = strstr(read_buffer[i], "\n"))) { |
159 |
|
160 |
//if no newline if found in the entire readbuffer (when its full),
|
161 |
//toss out the command
|
162 |
// because either the command being given is too long or someone is trying
|
163 |
// to do something bad to the server
|
164 |
//TODO: this is from before all this code was put in the loop. reconsider
|
165 |
// how to check this error condition and do it elsewhere
|
166 |
if (!newLine && (read_buffer_size[i] == READ_BUFFER_SIZE)) {
|
167 |
read_buffer_size[i] = 0;
|
168 |
break;
|
169 |
} |
170 |
|
171 |
//if no newline is found then there is not a command in the buffer
|
172 |
if (!newLine) {
|
173 |
break;
|
174 |
} |
175 |
|
176 |
commandLen = (newLine - read_buffer[i])+1;
|
177 |
|
178 |
//the newline was found in garbage in the currently not used portion
|
179 |
// of the read buffer
|
180 |
if (commandLen > read_buffer_size[i]) {
|
181 |
break;
|
182 |
} |
183 |
|
184 |
memcpy(tempCommand, read_buffer[i], commandLen); |
185 |
//do commandLen-1 to get rid of the newline terminating the command
|
186 |
tempCommand[commandLen-1] = '\0'; |
187 |
//did this because telnet was putting a \r\n on the end instead of just \n
|
188 |
if (isspace(tempCommand[commandLen-2])) { |
189 |
tempCommand[commandLen-2] = '\0'; |
190 |
} |
191 |
|
192 |
memmove(read_buffer[i], read_buffer[i]+commandLen, read_buffer_size[i] - commandLen); |
193 |
read_buffer_size[i] -= commandLen; |
194 |
|
195 |
if (commandLen > MAX_COMMAND_LEN) {
|
196 |
printf("The command was too long. Tossing command out.\n");
|
197 |
break;
|
198 |
} |
199 |
|
200 |
if (parse_command(tempCommand, i, wireless) < 0) { |
201 |
printf("There was an error parsing command\n");
|
202 |
break;
|
203 |
} |
204 |
} |
205 |
} |
206 |
} |
207 |
|
208 |
if (FD_ISSET(client_file_descriptor, &write_set)) {
|
209 |
if (write_buffer_size[i] == 0) { |
210 |
continue;
|
211 |
} |
212 |
|
213 |
sent = write(client_file_descriptor, write_buffer[i], write_buffer_size[i]); |
214 |
memmove(write_buffer[i], write_buffer[i]+sent, WRITE_BUFFER_SIZE - sent); |
215 |
write_buffer_size[i] -= sent; |
216 |
} |
217 |
} |
218 |
|
219 |
return 0; |
220 |
} |
221 |
|
222 |
int ConnectionPool::write_to_client(int pool_index, char * message, int length) { |
223 |
if (pool_index < 0 || pool_index >= next_available_slot) { |
224 |
return ERROR_INVALID_CLIENT_ID;
|
225 |
} |
226 |
|
227 |
if (!message) {
|
228 |
return ERROR_INVALID_MESSAGE;
|
229 |
} |
230 |
|
231 |
if (length < 0) { |
232 |
return ERROR_INVALID_MESSAGE_LENGTH;
|
233 |
} |
234 |
|
235 |
if (length > (WRITE_BUFFER_SIZE-write_buffer_size[pool_index]))
|
236 |
{ |
237 |
//TODO: make this a logging statement instead of a print statement
|
238 |
printf("There is not enough room in the write buffer to send the data to the client.\n");
|
239 |
return ERROR_NOT_ENOUGH_ROOM;
|
240 |
} |
241 |
|
242 |
memcpy(write_buffer[pool_index], message, length); |
243 |
write_buffer_size[pool_index] += length; |
244 |
|
245 |
return 0; |
246 |
} |
247 |
|
248 |
|
249 |
//TODO: put error checking on listen_socket to make sure its a valid socket
|
250 |
void ConnectionPool::set_listen_socket_in_ready_set(int listen_socket) { |
251 |
FD_SET(listen_socket, &ready_set); |
252 |
} |
253 |
|
254 |
int ConnectionPool::perform_select(int listen_socket, struct timeval * select_timeout) { |
255 |
read_set = ready_set; |
256 |
write_set = ready_set; |
257 |
|
258 |
//TODO(Jason): think about why I put this there
|
259 |
if (max_file_descriptor < listen_socket)
|
260 |
max_file_descriptor = listen_socket; |
261 |
|
262 |
number_clients_ready = select(max_file_descriptor+1, &(read_set), &(write_set), NULL, select_timeout); |
263 |
|
264 |
return 0; |
265 |
} |
266 |
|
267 |
int ConnectionPool::is_socket_ready_to_read(int socket) { |
268 |
return FD_ISSET(socket, &read_set);
|
269 |
} |
270 |
|
271 |
int ConnectionPool::is_socket_ready_to_write(int socket) { |
272 |
return FD_ISSET(socket, &write_set);
|
273 |
} |
274 |
|
275 |
int ConnectionPool::get_max_file_descriptor() {
|
276 |
return max_file_descriptor;
|
277 |
} |
278 |
|
279 |
void ConnectionPool::set_max_file_descriptor(int new_max_file_descriptor) { |
280 |
max_file_descriptor = new_max_file_descriptor; |
281 |
} |
282 |
|
283 |
int ConnectionPool::get_next_available_slot() {
|
284 |
return next_available_slot;
|
285 |
} |
286 |
|
287 |
int ConnectionPool::get_number_clients_ready() {
|
288 |
return number_clients_ready;
|
289 |
} |
290 |
|
291 |
void ConnectionPool::set_number_clients_ready(int new_number_clients_ready) { |
292 |
number_clients_ready = new_number_clients_ready; |
293 |
} |
294 |
|
295 |
fd_set ConnectionPool::get_ready_set() { |
296 |
return ready_set;
|
297 |
} |
298 |
|
299 |
fd_set ConnectionPool::get_read_set() { |
300 |
return read_set;
|
301 |
} |
302 |
|
303 |
void ConnectionPool::set_read_set(fd_set new_set) {
|
304 |
read_set = new_set; |
305 |
} |
306 |
|
307 |
fd_set ConnectionPool::get_write_set() { |
308 |
return write_set;
|
309 |
} |
310 |
|
311 |
void ConnectionPool::set_write_set(fd_set new_set) {
|
312 |
write_set = new_set; |
313 |
} |
314 |
|
315 |
//TODO: write a function to write data into the write buffers
|
316 |
//TODO: fix names of variables to obey new style
|
317 |
int ConnectionPool::parse_command(char* command, int pool_index, ColonetWireless * wireless) { |
318 |
char tokens[MAX_TOKENS][MAX_TOKEN_SIZE];
|
319 |
unsigned char int_tokens[MAX_TOKENS]; |
320 |
int numTokens = 0; |
321 |
char* endptr = NULL; |
322 |
int commandID;
|
323 |
int i;
|
324 |
unsigned char args[PACKET_DATA_LEN]; |
325 |
|
326 |
memset(args, 1, PACKET_DATA_LEN);
|
327 |
|
328 |
if (!command) {
|
329 |
return -1; |
330 |
} |
331 |
|
332 |
if (pool_index < 0) { |
333 |
return -1; |
334 |
} |
335 |
|
336 |
if (pool_index >= next_available_slot) {
|
337 |
return -1; |
338 |
} |
339 |
|
340 |
if ((numTokens = tokenize_command(command, tokens)) < 0) { |
341 |
return -1; |
342 |
} |
343 |
|
344 |
//the 10 in the function call indicates number is base 10
|
345 |
commandID = strtol(tokens[0], &endptr, 10); |
346 |
|
347 |
if (!endptr || *endptr != '\0') { |
348 |
printf("There was an error converting first token into a number.\n");
|
349 |
return -1; |
350 |
} |
351 |
|
352 |
if (commandID == SEND_TO_ROBOT || REQUEST_FROM_ROBOT) {
|
353 |
int numIntTokens = numTokens;
|
354 |
|
355 |
/* Convert tokens to ints */
|
356 |
for (i = ROBOT_COMMAND_OFFSET; i < numIntTokens; i++) {
|
357 |
int_tokens[i-ROBOT_COMMAND_OFFSET] = atoi(tokens[i]); |
358 |
} |
359 |
if (commandID == REQUEST_FROM_ROBOT) {
|
360 |
if (i < MAX_TOKENS) {
|
361 |
for (;i >= 4; i--) { |
362 |
int_tokens[i] = int_tokens[i-1];
|
363 |
} |
364 |
int_tokens[3] = pool_index;
|
365 |
numIntTokens++; |
366 |
} else {
|
367 |
//TODO: send an error back to client here also
|
368 |
fprintf(stderr, "Client attempted to request data from the robot but there was not enough room to put in the client's id.\n");
|
369 |
return -1; |
370 |
} |
371 |
} |
372 |
|
373 |
/* Fill args buffer with args */
|
374 |
for (i = ROBOT_COMMAND_LEN; i < numIntTokens-ROBOT_COMMAND_OFFSET; i++) {
|
375 |
args[i-ROBOT_COMMAND_LEN] = int_tokens[i]; |
376 |
} |
377 |
|
378 |
/* Check the tokens */
|
379 |
if (check_tokens(int_tokens, numIntTokens) < 0) { |
380 |
fprintf(stderr, "%s: Error - Invalid command/request.\n", __FUNCTION__);
|
381 |
return 0; |
382 |
} |
383 |
|
384 |
/* Send packet to robot */
|
385 |
fprintf(stderr, "Calling wireless->send(%d, %d, %d, args)\n",
|
386 |
int_tokens[0], int_tokens[1], int_tokens[2]); |
387 |
wireless->send(int_tokens[0], int_tokens[1], int_tokens[2], args); |
388 |
} |
389 |
|
390 |
return 0; |
391 |
} |
392 |
|
393 |
int ConnectionPool::tokenize_command(char* command, char tokens[MAX_TOKENS][MAX_TOKEN_SIZE]) { |
394 |
char* nextToken = command;
|
395 |
char* endToken = NULL; |
396 |
int numTokens = 0; |
397 |
|
398 |
if (!command) {
|
399 |
return -1; |
400 |
} |
401 |
|
402 |
while ((endToken = strstr(nextToken, " "))) { |
403 |
*endToken = '\0';
|
404 |
|
405 |
if (strlen(nextToken) > MAX_TOKEN_SIZE-1) { |
406 |
return -1; |
407 |
} |
408 |
|
409 |
strcpy(tokens[numTokens], nextToken); |
410 |
|
411 |
numTokens++; |
412 |
nextToken = endToken + 1;
|
413 |
|
414 |
while (isspace(*nextToken) && *nextToken != '\0') { |
415 |
nextToken++; |
416 |
} |
417 |
} |
418 |
|
419 |
if (endToken == NULL && *nextToken != '\0') { |
420 |
if (strlen(nextToken) > MAX_TOKEN_SIZE-1) { |
421 |
return -1; |
422 |
} |
423 |
|
424 |
strcpy(tokens[numTokens], nextToken); |
425 |
|
426 |
numTokens++; |
427 |
} |
428 |
|
429 |
return numTokens;
|
430 |
} |
431 |
|
432 |
/** @brief checks a list of tokens to see if it's valid
|
433 |
*
|
434 |
* @return 0 if tokens is valid
|
435 |
*/
|
436 |
int ConnectionPool::check_tokens(unsigned char* tokens, int numTokens) { |
437 |
if (numTokens > 3 + PACKET_DATA_LEN) { |
438 |
/* Too many tokens */
|
439 |
return -1; |
440 |
} |
441 |
|
442 |
if (numTokens < 3) { |
443 |
/* Not enough tokens */
|
444 |
return -1; |
445 |
} |
446 |
|
447 |
if (tokens[1] != COLONET_REQUEST && tokens[1] != COLONET_COMMAND) { |
448 |
/* Invalid message type */
|
449 |
return -1; |
450 |
} |
451 |
|
452 |
return 0; |
453 |
} |