root / trunk / code / projects / colonet / ColonetServer / ConnectionPool.cpp @ 24
History | View | Annotate | Download (12.1 KB)
1 | 11 | emarinel | /**
|
---|---|---|---|
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 | 22 | jknichel | int ConnectionPool::check_clients(ColonetWireless * wireless) {
|
118 | 11 | emarinel | 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 | 22 | jknichel | if (parse_command(tempCommand, i, wireless) < 0) { |
201 | 11 | emarinel | 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 | 14 | jknichel | int ConnectionPool::write_to_client(int pool_index, char * message, int length) { |
223 | 22 | jknichel | 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 | 14 | jknichel | 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 | 22 | jknichel | return ERROR_NOT_ENOUGH_ROOM;
|
240 | 14 | jknichel | } |
241 | 11 | emarinel | |
242 | 14 | jknichel | memcpy(write_buffer[pool_index], message, length); |
243 | write_buffer_size[pool_index] += length; |
||
244 | |||
245 | return 0; |
||
246 | } |
||
247 | |||
248 | |||
249 | 11 | emarinel | //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 | 22 | jknichel | int ConnectionPool::parse_command(char* command, int pool_index, ColonetWireless * wireless) { |
318 | 11 | emarinel | 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 | 22 | jknichel | wireless->send(int_tokens[0], int_tokens[1], int_tokens[2], args); |
388 | 11 | emarinel | } |
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 | } |