root / trunk / code / projects / colonet / ColonetServer / ConnectionPool.cpp @ 11
History | View | Annotate | Download (12 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 | int ConnectionPool::check_clients() {
|
||
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) < 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 | |||
223 | //TODO: put error checking on listen_socket to make sure its a valid socket
|
||
224 | void ConnectionPool::set_listen_socket_in_ready_set(int listen_socket) { |
||
225 | FD_SET(listen_socket, &ready_set); |
||
226 | } |
||
227 | |||
228 | int ConnectionPool::perform_select(int listen_socket, struct timeval * select_timeout) { |
||
229 | read_set = ready_set; |
||
230 | write_set = ready_set; |
||
231 | |||
232 | //TODO(Jason): think about why I put this there
|
||
233 | if (max_file_descriptor < listen_socket)
|
||
234 | max_file_descriptor = listen_socket; |
||
235 | |||
236 | number_clients_ready = select(max_file_descriptor+1, &(read_set), &(write_set), NULL, select_timeout); |
||
237 | |||
238 | return 0; |
||
239 | } |
||
240 | |||
241 | int ConnectionPool::is_socket_ready_to_read(int socket) { |
||
242 | return FD_ISSET(socket, &read_set);
|
||
243 | } |
||
244 | |||
245 | int ConnectionPool::is_socket_ready_to_write(int socket) { |
||
246 | return FD_ISSET(socket, &write_set);
|
||
247 | } |
||
248 | |||
249 | int ConnectionPool::get_max_file_descriptor() {
|
||
250 | return max_file_descriptor;
|
||
251 | } |
||
252 | |||
253 | void ConnectionPool::set_max_file_descriptor(int new_max_file_descriptor) { |
||
254 | max_file_descriptor = new_max_file_descriptor; |
||
255 | } |
||
256 | |||
257 | int ConnectionPool::get_next_available_slot() {
|
||
258 | return next_available_slot;
|
||
259 | } |
||
260 | |||
261 | int ConnectionPool::get_number_clients_ready() {
|
||
262 | return number_clients_ready;
|
||
263 | } |
||
264 | |||
265 | void ConnectionPool::set_number_clients_ready(int new_number_clients_ready) { |
||
266 | number_clients_ready = new_number_clients_ready; |
||
267 | } |
||
268 | |||
269 | fd_set ConnectionPool::get_ready_set() { |
||
270 | return ready_set;
|
||
271 | } |
||
272 | |||
273 | fd_set ConnectionPool::get_read_set() { |
||
274 | return read_set;
|
||
275 | } |
||
276 | |||
277 | void ConnectionPool::set_read_set(fd_set new_set) {
|
||
278 | read_set = new_set; |
||
279 | } |
||
280 | |||
281 | fd_set ConnectionPool::get_write_set() { |
||
282 | return write_set;
|
||
283 | } |
||
284 | |||
285 | void ConnectionPool::set_write_set(fd_set new_set) {
|
||
286 | write_set = new_set; |
||
287 | } |
||
288 | |||
289 | //TODO: remove after a write_to_client or similar method is written so this won't be needed anymore
|
||
290 | int ConnectionPool::get_write_buffer_size(int pool_index) { |
||
291 | return write_buffer_size[pool_index];
|
||
292 | } |
||
293 | |||
294 | //TODO: remove after a write_to_client or similar method is written so this won't be needed anymore
|
||
295 | void ConnectionPool::set_write_buffer_size(int pool_index, int new_buffer_size) { |
||
296 | write_buffer_size[pool_index] = new_buffer_size; |
||
297 | } |
||
298 | |||
299 | //TODO: remove after a write_to_client or similar method is written so this won't be needed anymore
|
||
300 | char * ConnectionPool::get_write_buffer(int pool_index) { |
||
301 | return write_buffer[pool_index];
|
||
302 | } |
||
303 | |||
304 | //TODO: write a function to write data into the write buffers
|
||
305 | //TODO: fix names of variables to obey new style
|
||
306 | int ConnectionPool::parse_command(char* command, int pool_index) { |
||
307 | char tokens[MAX_TOKENS][MAX_TOKEN_SIZE];
|
||
308 | unsigned char int_tokens[MAX_TOKENS]; |
||
309 | int numTokens = 0; |
||
310 | char* endptr = NULL; |
||
311 | int commandID;
|
||
312 | int i;
|
||
313 | unsigned char args[PACKET_DATA_LEN]; |
||
314 | |||
315 | memset(args, 1, PACKET_DATA_LEN);
|
||
316 | |||
317 | if (!command) {
|
||
318 | return -1; |
||
319 | } |
||
320 | |||
321 | if (pool_index < 0) { |
||
322 | return -1; |
||
323 | } |
||
324 | |||
325 | if (pool_index >= next_available_slot) {
|
||
326 | return -1; |
||
327 | } |
||
328 | |||
329 | if ((numTokens = tokenize_command(command, tokens)) < 0) { |
||
330 | return -1; |
||
331 | } |
||
332 | |||
333 | //the 10 in the function call indicates number is base 10
|
||
334 | commandID = strtol(tokens[0], &endptr, 10); |
||
335 | |||
336 | if (!endptr || *endptr != '\0') { |
||
337 | printf("There was an error converting first token into a number.\n");
|
||
338 | return -1; |
||
339 | } |
||
340 | |||
341 | if (commandID == SEND_TO_ROBOT || REQUEST_FROM_ROBOT) {
|
||
342 | int numIntTokens = numTokens;
|
||
343 | |||
344 | /* Convert tokens to ints */
|
||
345 | for (i = ROBOT_COMMAND_OFFSET; i < numIntTokens; i++) {
|
||
346 | int_tokens[i-ROBOT_COMMAND_OFFSET] = atoi(tokens[i]); |
||
347 | } |
||
348 | if (commandID == REQUEST_FROM_ROBOT) {
|
||
349 | if (i < MAX_TOKENS) {
|
||
350 | for (;i >= 4; i--) { |
||
351 | int_tokens[i] = int_tokens[i-1];
|
||
352 | } |
||
353 | int_tokens[3] = pool_index;
|
||
354 | numIntTokens++; |
||
355 | } else {
|
||
356 | //TODO: send an error back to client here also
|
||
357 | fprintf(stderr, "Client attempted to request data from the robot but there was not enough room to put in the client's id.\n");
|
||
358 | return -1; |
||
359 | } |
||
360 | } |
||
361 | |||
362 | /* Fill args buffer with args */
|
||
363 | for (i = ROBOT_COMMAND_LEN; i < numIntTokens-ROBOT_COMMAND_OFFSET; i++) {
|
||
364 | args[i-ROBOT_COMMAND_LEN] = int_tokens[i]; |
||
365 | } |
||
366 | |||
367 | /* Check the tokens */
|
||
368 | if (check_tokens(int_tokens, numIntTokens) < 0) { |
||
369 | fprintf(stderr, "%s: Error - Invalid command/request.\n", __FUNCTION__);
|
||
370 | return 0; |
||
371 | } |
||
372 | |||
373 | /* Send packet to robot */
|
||
374 | fprintf(stderr, "Calling wireless->send(%d, %d, %d, args)\n",
|
||
375 | int_tokens[0], int_tokens[1], int_tokens[2]); |
||
376 | send_wireless_message(int_tokens[0], int_tokens[1], int_tokens[2], args); |
||
377 | } |
||
378 | |||
379 | return 0; |
||
380 | } |
||
381 | |||
382 | int ConnectionPool::tokenize_command(char* command, char tokens[MAX_TOKENS][MAX_TOKEN_SIZE]) { |
||
383 | char* nextToken = command;
|
||
384 | char* endToken = NULL; |
||
385 | int numTokens = 0; |
||
386 | |||
387 | if (!command) {
|
||
388 | return -1; |
||
389 | } |
||
390 | |||
391 | while ((endToken = strstr(nextToken, " "))) { |
||
392 | *endToken = '\0';
|
||
393 | |||
394 | if (strlen(nextToken) > MAX_TOKEN_SIZE-1) { |
||
395 | return -1; |
||
396 | } |
||
397 | |||
398 | strcpy(tokens[numTokens], nextToken); |
||
399 | |||
400 | numTokens++; |
||
401 | nextToken = endToken + 1;
|
||
402 | |||
403 | while (isspace(*nextToken) && *nextToken != '\0') { |
||
404 | nextToken++; |
||
405 | } |
||
406 | } |
||
407 | |||
408 | if (endToken == NULL && *nextToken != '\0') { |
||
409 | if (strlen(nextToken) > MAX_TOKEN_SIZE-1) { |
||
410 | return -1; |
||
411 | } |
||
412 | |||
413 | strcpy(tokens[numTokens], nextToken); |
||
414 | |||
415 | numTokens++; |
||
416 | } |
||
417 | |||
418 | return numTokens;
|
||
419 | } |
||
420 | |||
421 | /** @brief checks a list of tokens to see if it's valid
|
||
422 | *
|
||
423 | * @return 0 if tokens is valid
|
||
424 | */
|
||
425 | int ConnectionPool::check_tokens(unsigned char* tokens, int numTokens) { |
||
426 | if (numTokens > 3 + PACKET_DATA_LEN) { |
||
427 | /* Too many tokens */
|
||
428 | return -1; |
||
429 | } |
||
430 | |||
431 | if (numTokens < 3) { |
||
432 | /* Not enough tokens */
|
||
433 | return -1; |
||
434 | } |
||
435 | |||
436 | if (tokens[1] != COLONET_REQUEST && tokens[1] != COLONET_COMMAND) { |
||
437 | /* Invalid message type */
|
||
438 | return -1; |
||
439 | } |
||
440 | |||
441 | return 0; |
||
442 | } |