Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / ColonetServer / ConnectionPool.cpp @ 118

History | View | Annotate | Download (16.5 KB)

1
/**
2
 * @file ConnectionPool.cpp
3
 *
4
 * @author Jason Knichel
5
 * @date 7/22/07
6
 */
7

    
8
#include <sys/select.h>
9
#include <ctype.h>
10
#include <errno.h>
11
#include <unistd.h>
12
#include <string.h>
13
#include <stdlib.h>
14
#include <stdio.h>
15

    
16
#include "includes/ConnectionPool.h"
17
#include "includes/client.h"
18
#include "../lib/colonet_defs.h"
19
#include <colonet_wireless.h>
20

    
21
/**
22
 * @brief The default constructor for ConnectionPool
23
 */
24
ConnectionPool::ConnectionPool() {
25
  max_file_descriptor = 0;
26
  next_available_slot = 0;
27
  number_clients_ready = 0;
28

    
29
  FD_ZERO(&ready_set);
30
  FD_ZERO(&read_set);
31
  FD_ZERO(&write_set);
32

    
33
  memset(&client_file_descriptor_array, 0, sizeof(int)*MAX_CONNECTIONS);
34
  memset(&read_buffer, 0, sizeof(char *)*MAX_CONNECTIONS);
35
  memset(&read_buffer_size, 0, sizeof(int)*MAX_CONNECTIONS);
36
  memset(&write_buffer, 0, sizeof(char *)*MAX_CONNECTIONS);
37
  memset(&write_buffer_size, 0, sizeof(int)*MAX_CONNECTIONS);
38
}
39

    
40
/**
41
 * @brief The destructor for ConnectionPool
42
 */
43
ConnectionPool::~ConnectionPool() {
44
}
45

    
46
/**
47
 * @brief Adds a client to the connection pool
48
 *
49
 * @param client_file_descriptor The file descriptor to add to the connection pool
50
 *
51
 * @return 0 on success, negative error code on failure
52
 */
53
int ConnectionPool::add_client(int client_file_descriptor) {
54
  if (client_file_descriptor < 0) {
55
    return ERROR_INVALID_CLIENT_DESCRIPTOR;
56
  }
57

    
58
  if (next_available_slot == MAX_CONNECTIONS) {
59
    return ERROR_TOO_MANY_CLIENTS;
60
  }
61

    
62
  if (client_file_descriptor > max_file_descriptor) {
63
    max_file_descriptor = client_file_descriptor;
64
  }
65

    
66
  FD_SET(client_file_descriptor, &ready_set);
67

    
68
  int next_slot = next_available_slot;
69

    
70
  client_file_descriptor_array[next_slot] = client_file_descriptor;
71
  read_buffer[next_slot] = (char*) malloc(sizeof(char) * READ_BUFFER_SIZE);
72
  if (!(read_buffer[next_slot])) {
73
    return ERROR_ALLOCATING_MEMORY;
74
  }
75
  read_buffer_size[next_slot] = 0;
76
  write_buffer[next_slot] = (char *)malloc(sizeof(char) * WRITE_BUFFER_SIZE);
77

    
78
  if (!(write_buffer[next_slot])) {
79
    free(read_buffer[next_slot]);
80
    return ERROR_ALLOCATING_MEMORY;
81
  }
82

    
83
  write_buffer_size[next_slot] = 0;
84

    
85
  next_available_slot++;
86

    
87
  return 0;
88
}
89

    
90
/**
91
 * @brief Removes a client from the connection pool
92
 *
93
 * @param The index in the pool of the client to remove
94
 *
95
 * @return 0 on success, negative error code on failure
96
 */
97
int ConnectionPool::remove_client(int pool_index) {
98
  if (pool_index < 0 || pool_index >= next_available_slot) {
99
    return ERROR_INVALID_CLIENT_DESCRIPTOR;
100
  }
101

    
102
  int client_file_descriptor = client_file_descriptor_array[pool_index];
103

    
104
  if (FD_ISSET(client_file_descriptor, &ready_set)) {
105
    FD_CLR(client_file_descriptor, &ready_set);
106
  }
107
  if (FD_ISSET(client_file_descriptor, &read_set)) {
108
    FD_CLR(client_file_descriptor, &read_set);
109
  }
110
  if (FD_ISSET(client_file_descriptor, &write_set)) {
111
    FD_CLR(client_file_descriptor, &write_set);
112
  }
113

    
114
  free(read_buffer[pool_index]);
115
  free(write_buffer[pool_index]);
116
  for (int j = pool_index; j < next_available_slot - 1; j++) {
117
    client_file_descriptor_array[pool_index] = client_file_descriptor_array[pool_index+1];
118
    read_buffer[pool_index] = read_buffer[pool_index+1];
119
    read_buffer_size[pool_index] = read_buffer_size[pool_index+1];
120
    write_buffer[pool_index] = write_buffer[pool_index+1];
121
    write_buffer_size[pool_index] = write_buffer_size[pool_index+1];
122
  }
123
  next_available_slot--;
124
  int temp_max_file_descriptor = 0;
125

    
126
  for (int j = 0; j < next_available_slot; j++) {
127
    if (client_file_descriptor_array[j] > temp_max_file_descriptor)
128
      temp_max_file_descriptor = client_file_descriptor_array[j];
129
  }
130
  max_file_descriptor = temp_max_file_descriptor;
131

    
132
  printf("Removing client.\n");
133

    
134
  return 0;
135
}
136

    
137
/**
138
 * @brief Checks the status of the clients
139
 *
140
 * Sees is any clients are ready to read from their file descriptor or are
141
 *  ready to write to their file descriptor.
142
 *
143
 * @return 0 on success, negative error code on error
144
 */
145
//TODO: test that it drops commands properly if it gets sent too much data
146
//      do we want it to drop the data or drop the connection?
147
int ConnectionPool::check_clients() {
148
  char temporary_buffer[READ_BUFFER_SIZE];
149
  char temporary_command_buffer[READ_BUFFER_SIZE+1];
150
  int i;
151
  int client_file_descriptor;
152
  int num_bytes_read;
153
  int length;
154
  int sent;
155
  int command_length;
156

    
157
  for (i = 0; i < next_available_slot; i++) {
158
    client_file_descriptor = client_file_descriptor_array[i];
159

    
160
    if (FD_ISSET(client_file_descriptor, &read_set)) {
161
      num_bytes_read = read(client_file_descriptor, temporary_buffer, READ_BUFFER_SIZE);
162

    
163
      if (num_bytes_read == 0 || (num_bytes_read == -1 && errno == ECONNRESET)) {
164
        remove_client(i);
165
        i--;
166
        continue;
167
      }
168

    
169
      while (num_bytes_read > 0) {
170
        length = num_bytes_read;
171

    
172
        if (length + read_buffer_size[i] > READ_BUFFER_SIZE) {
173
          length = READ_BUFFER_SIZE - read_buffer_size[i];
174
        }
175

    
176
        memcpy(read_buffer[i]+read_buffer_size[i], temporary_buffer, length);
177
        read_buffer_size[i] += length;
178
        num_bytes_read -= length;
179

    
180
        if (num_bytes_read > 0) {
181
          memmove(temporary_buffer, temporary_buffer+length, READ_BUFFER_SIZE - length);
182
        }
183

    
184
        printf("Read buffer is %s\n", read_buffer[i]);
185

    
186
        char* newline_position;
187

    
188
        while ((newline_position = strstr(read_buffer[i], "\n"))) {
189

    
190
          //if no newline if found in the entire readbuffer (when its full), 
191
          //toss out the command
192
          // because either the command being given is too long or someone is trying
193
          // to do something bad to the server
194
          //TODO: this is from before all this code was put in the loop.  reconsider
195
          //      how to check this error condition and do it elsewhere
196
          if (!newline_position && (read_buffer_size[i] == READ_BUFFER_SIZE)) {
197
            read_buffer_size[i] = 0;
198
            break;
199
          }
200

    
201
          //if no newline is found then there is not a command in the buffer
202
          if (!newline_position) {
203
            break;
204
          }
205

    
206
          command_length = (newline_position - read_buffer[i])+1;
207

    
208
          //the newline was found in garbage in the currently not used portion
209
          // of the read buffer
210
          if (command_length > read_buffer_size[i]) {
211
            break;
212
          }
213

    
214
          memcpy(temporary_command_buffer, read_buffer[i], command_length);
215
          //do command_length-1 to get rid of the newline terminating the command
216
          temporary_command_buffer[command_length-1] = '\0';
217
          //did this because telnet was putting a \r\n on the end instead of just \n
218
          if (isspace(temporary_command_buffer[command_length-2])) {
219
            temporary_command_buffer[command_length-2] = '\0';
220
          }
221

    
222
          memmove(read_buffer[i], read_buffer[i]+command_length, read_buffer_size[i] - command_length);
223
          read_buffer_size[i] -= command_length;
224

    
225
          if (command_length > MAX_COMMAND_LEN) {
226
            printf("The command was too long.  Tossing command out.\n");
227
            break;
228
          }
229

    
230
          if (parse_command(temporary_command_buffer, i) < 0) {
231
            printf("There was an error parsing command\n");
232
            break;
233
          }
234
        }
235
      }
236
    }
237

    
238
    if (FD_ISSET(client_file_descriptor, &write_set)) {
239
      if (write_buffer_size[i] == 0) {
240
        continue;
241
      }
242

    
243
      sent = write(client_file_descriptor, write_buffer[i], write_buffer_size[i]);
244
      memmove(write_buffer[i], write_buffer[i]+sent, WRITE_BUFFER_SIZE - sent);
245
      write_buffer_size[i] -= sent;
246
    }
247
  }
248

    
249
  return 0;
250
}
251

    
252
/**
253
 * @brief Puts text into a write buffer that will be written to a client's file
254
 *  descriptor sometime when the client is ready to write.
255
 *
256
 * @param pool_index Index in the pool of the client to write to
257
 * @param message The message to be written
258
 * @param length The length of the message
259
 *
260
 * @return 0 on success, negative error code on failure
261
 */
262
int ConnectionPool::write_to_client(int pool_index, char * message, int length) {
263
  if (pool_index < 0 || pool_index >= next_available_slot) {
264
    return ERROR_INVALID_CLIENT_ID;
265
  }
266

    
267
  if (!message) {
268
    return ERROR_INVALID_MESSAGE;
269
  }
270

    
271
  if (length < 0) {
272
    return ERROR_INVALID_MESSAGE_LENGTH;
273
  }
274

    
275
  if (length > (WRITE_BUFFER_SIZE-write_buffer_size[pool_index])) {
276
    //TODO: make this a logging statement instead of a print statement
277
    printf("There is not enough room in the write buffer to send the data to the client.\n");
278
    return ERROR_NOT_ENOUGH_ROOM;
279
  }
280

    
281
  memcpy(write_buffer[pool_index], message, length);
282
  write_buffer_size[pool_index] += length;
283

    
284
  return 0;
285
}
286

    
287
/**
288
 * @brief Sets the socket to listen on
289
 *
290
 * @param listen_socket The socket to listen on
291
 *
292
 * @return void
293
 */
294
void ConnectionPool::set_listen_socket_in_ready_set(int listen_socket) {
295
  if (listen_socket < 0)
296
    return;
297

    
298
  FD_SET(listen_socket, &ready_set);
299
}
300

    
301
/**
302
 * @brief Find out what file descriptors are ready to write to and read from
303
 *
304
 * @param listen_socket The socket to listen on
305
 * @param select_timeout The timeout for the select statement
306
 *
307
 * @return 0
308
 */
309
int ConnectionPool::perform_select(int listen_socket) {
310
  read_set = ready_set;
311
  write_set = ready_set;
312

    
313
  struct timeval select_timeout;
314
  memset(&select_timeout, 0, sizeof(select_timeout));
315

    
316
  //TODO(Jason): think about why I put this there
317
  if (max_file_descriptor < listen_socket)
318
    max_file_descriptor = listen_socket;
319

    
320
  number_clients_ready = select(max_file_descriptor+1, &(read_set), &(write_set), NULL, &select_timeout);
321

    
322
  if (number_clients_ready < 0) {
323
    perror(__FUNCTION__);
324
  }
325

    
326
  return 0;
327
}
328

    
329
int ConnectionPool::is_socket_ready_to_read(int socket) {
330
  return FD_ISSET(socket, &read_set);
331
}
332

    
333
int ConnectionPool::is_socket_ready_to_write(int socket) {
334
  return FD_ISSET(socket, &write_set);
335
}
336

    
337
int ConnectionPool::get_max_file_descriptor() {
338
  return max_file_descriptor;
339
}
340

    
341
void ConnectionPool::set_max_file_descriptor(int new_max_file_descriptor) {
342
  max_file_descriptor = new_max_file_descriptor;
343
}
344

    
345
int ConnectionPool::get_next_available_slot() {
346
  return next_available_slot;
347
}
348

    
349
int ConnectionPool::get_number_clients_ready() {
350
  return number_clients_ready;
351
}
352

    
353
void ConnectionPool::set_number_clients_ready(int new_number_clients_ready) {
354
  number_clients_ready = new_number_clients_ready;
355
}
356

    
357
fd_set ConnectionPool::get_ready_set() {
358
  return ready_set;
359
}
360

    
361
fd_set ConnectionPool::get_read_set() {
362
  return read_set;
363
}
364

    
365
void ConnectionPool::set_read_set(fd_set new_set) {
366
  read_set = new_set;
367
}
368

    
369
fd_set ConnectionPool::get_write_set() {
370
  return write_set;
371
}
372

    
373
void ConnectionPool::set_write_set(fd_set new_set) {
374
  write_set = new_set;
375
}
376

    
377
//TODO: write a function to write data into the write buffers (jason: what was this referring to?)
378
int ConnectionPool::parse_command(char* command, int pool_index) {
379
  char tokens[MAX_TOKENS][MAX_TOKEN_SIZE];
380
  unsigned char int_tokens[MAX_TOKENS];
381
  int number_tokens = 0;
382
  char* end_pointer = NULL;
383
  int command_id;
384
  int i;
385
  unsigned char arguments[PACKET_DATA_LEN];
386

    
387
  memset(arguments, 1, PACKET_DATA_LEN);
388

    
389
  if (!command) {
390
    return -1;
391
  }
392

    
393
  if (pool_index < 0) {
394
    return -1;
395
  }
396

    
397
  if (pool_index >= next_available_slot) {
398
    return -1;
399
  }
400

    
401
  if ((number_tokens = tokenize_command(command, tokens)) < 0) {
402
    return -1;
403
  }
404

    
405
  //the 10 in the function call indicates number is base 10
406
  command_id = strtol(tokens[0], &end_pointer, 10);
407

    
408
  if (!end_pointer || *end_pointer != '\0') {
409
    printf("There was an error converting first token into a number.\n");
410
    return -1;
411
  }
412

    
413
  if (command_id == SEND_TO_ROBOT) {
414
    int number_int_tokens = number_tokens;
415

    
416
    // Convert tokens to ints 
417
    for (i = ROBOT_COMMAND_OFFSET; i < number_int_tokens; i++) {
418
      int_tokens[i-ROBOT_COMMAND_OFFSET] = atoi(tokens[i]);
419
    }
420
    /* we removed the request from robot constant
421
    if (command_id == REQUEST_FROM_ROBOT) {
422
      if (i < MAX_TOKENS) {
423
        for (;i >= 4; i--) {
424
          int_tokens[i] = int_tokens[i-1];
425
        }
426
        int_tokens[3] = pool_index;
427
        number_int_tokens++;
428
      } else {
429
        //TODO: send an error back to client here also
430
        fprintf(stderr, "Client attempted to request data from the robot but there was not enough room to put in the client's id.\n");
431
        return -1;
432
      }
433
    }
434
    */
435

    
436
    // Fill arguments buffer with arguments 
437
    for (i = ROBOT_COMMAND_LEN; i < number_int_tokens-ROBOT_COMMAND_OFFSET;
438
         i++) {
439
      arguments[i-ROBOT_COMMAND_LEN] = int_tokens[i];
440
    }
441

    
442
    // Check the tokens 
443
    if (check_tokens(int_tokens, number_int_tokens) < 0) {
444
      fprintf(stderr, "%s: Error - Invalid command/request.\n", __FUNCTION__);
445
      return 0;
446
    }
447
  
448
    // Send packet to robot 
449
    fprintf(stderr, "Calling colonet_wl_send(%d, %d, %d, arguments)\n", 
450
            int_tokens[0], int_tokens[1], int_tokens[2]);
451
    colonet_wl_send(pool_index, int_tokens[0],
452
                    (ColonetMessageType)int_tokens[1], int_tokens[2],
453
                    arguments);
454
  } else if (command_id == REQUEST_FROM_SERVER) {
455
    if (number_tokens < 2)
456
      return -1;
457

    
458
    end_pointer=NULL;
459
    int second_token = strtol(tokens[1], &end_pointer, 10);
460

    
461
    if (!end_pointer || *end_pointer != '\0') {
462
      printf("There was an error converting second token into a number.\n");
463
      return -1;
464
    }
465
    
466
    if (second_token == REQUEST_BOM_MATRIX) {
467
      //TODO: rename to indicate its actually the response message
468
      char bom_matrix_buffer[MAX_RESPONSE_LEN];
469
      char temp_bom_matrix_buffer[MAX_RESPONSE_LEN];
470
 
471
      //TODO: change after we start keeping track of bom matrix
472
      int number_robots = rand()%10;
473
      int bom_matrix[number_robots][number_robots];
474
      for (int ii = 0; ii < number_robots; ii++) {
475
        for (int j = 0; j < number_robots; j++) {
476
          //do this to generate some -1 values which mean they can't see each other
477
          int matrix_value = rand()%(number_robots+1)-1;
478
          bom_matrix[ii][j] = matrix_value;
479
        }
480
      }
481

    
482
      printf("number of robots is %d\n", number_robots);
483

    
484
      //TODO: separate parameters with spaces
485
      //TODO: make this better
486
      //TODO: make sure I don't need to do MAX_RESPONSE_LENGTH-1
487
      snprintf(bom_matrix_buffer,MAX_RESPONSE_LEN, "%d %d %d", RESPONSE_TO_CLIENT_REQUEST, REQUEST_BOM_MATRIX, number_robots);
488
      for (int ii = 0; ii < number_robots; ii++) {
489
        for (int j = 0; j < number_robots; j++) {
490
          //TODO: don't use strcpy
491
          strcpy(temp_bom_matrix_buffer, bom_matrix_buffer);
492
          //TODO: put length checking in here so array doesn't go out of bounds
493
          //TODO: maybe use strncat?
494
          printf("Buffer is %s\n", bom_matrix_buffer);
495
          strcat(temp_bom_matrix_buffer," %d");
496
          snprintf(bom_matrix_buffer, MAX_RESPONSE_LEN, temp_bom_matrix_buffer, bom_matrix[ii][j]);
497
        }
498
      }
499
      strcat(bom_matrix_buffer,"\n");
500
      write_to_client(pool_index, bom_matrix_buffer, strlen(bom_matrix_buffer));
501
      printf("Sending %s", bom_matrix_buffer);
502
    } else {
503
      char * my_current_message = "Hi, how are you?\n";
504
      printf("Sending %s\n", my_current_message);
505
      write_to_client(pool_index, my_current_message, strlen(my_current_message));
506
    }
507
  }
508

    
509
  return 0;
510
}
511

    
512
/**
513
 * @brief Breaks a command up into tokens
514
 *
515
 * @param command The command to tokenize
516
 * @param tokens A two dimensional character array to store the tokens in
517
 *
518
 * @return 0 on success, negative error code on failure
519
 */
520
int ConnectionPool::tokenize_command(char* command, char tokens[MAX_TOKENS][MAX_TOKEN_SIZE]) {
521
  char* next_token = command;
522
  char* end_token = NULL;
523
  int number_tokens = 0;
524

    
525
  if (!command) {
526
    return -1;
527
  }
528

    
529
  while ((end_token = strstr(next_token, " "))) {
530
    *end_token = '\0';
531

    
532
    if (strlen(next_token) > MAX_TOKEN_SIZE-1) {
533
      return -1;
534
    }
535

    
536
    strcpy(tokens[number_tokens], next_token);
537

    
538
    number_tokens++;
539
    next_token = end_token + 1;
540

    
541
    while (isspace(*next_token) && *next_token != '\0') {
542
      next_token++;
543
    }
544
  }
545

    
546
  if (end_token == NULL && *next_token != '\0') {
547
    if (strlen(next_token) > MAX_TOKEN_SIZE-1) {
548
      return -1;
549
    }
550

    
551
    strcpy(tokens[number_tokens], next_token);
552

    
553
    number_tokens++;
554
  }
555

    
556
  return number_tokens;
557
}
558

    
559
/** 
560
 * @brief checks a list of tokens to see if it's valid
561
 *
562
 * @param tokens The tokens to check
563
 * @param number_tokens The number of tokens contained in the tokens parameter
564
 *
565
 * @return 0 if tokens is valid
566
 */
567
int ConnectionPool::check_tokens(unsigned char* tokens, int number_tokens) {
568
  if (number_tokens > 3 + PACKET_DATA_LEN) {
569
    /* Too many tokens */
570
    return -1;
571
  }
572

    
573
  if (number_tokens < 3) {
574
    /* Not enough tokens */
575
    return -1;
576
  }
577
  
578
  if (tokens[1] != COLONET_REQUEST && tokens[1] != COLONET_COMMAND) {
579
    /* Invalid message type */
580
    return -1;
581
  }
582

    
583
  return 0;
584
}