Project

General

Profile

Statistics
| Revision:

root / branches / lib_additions / code / projects / colonet / ColonetServer / ConnectionPool.cpp @ 79

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

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

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

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

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

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

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

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

    
65
  FD_SET(client_file_descriptor, &ready_set);
66

    
67
  int next_slot = next_available_slot;
68

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

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

    
82
  write_buffer_size[next_slot] = 0;
83

    
84
  next_available_slot++;
85

    
86
  return 0;
87
}
88

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

    
101
  int client_file_descriptor = client_file_descriptor_array[pool_index];
102

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

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

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

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

    
133
  return 0;
134
}
135

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

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

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

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

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

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

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

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

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

    
187
        char* newline_position;
188

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

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

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

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

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

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

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

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

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

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

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

    
250
  return 0;
251
}
252

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

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

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

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

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

    
285
  return 0;
286
}
287

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

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

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

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

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

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

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

    
327
  return 0;
328
}
329

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

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

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

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

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

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

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

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

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

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

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

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

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

    
388
  memset(arguments, 1, PACKET_DATA_LEN);
389

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

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

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

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

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

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

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

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

    
437
    // Fill arguments buffer with arguments 
438
    for (i = ROBOT_COMMAND_LEN; i < number_int_tokens-ROBOT_COMMAND_OFFSET; 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 wireless->send(%d, %d, %d, arguments)\n", 
450
            int_tokens[0], int_tokens[1], int_tokens[2]);
451
    wireless->send(int_tokens[0], int_tokens[1], int_tokens[2], arguments);
452
  } else if (command_id == REQUEST_FROM_SERVER) {
453
    if (number_tokens < 2)
454
      return -1;
455

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

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

    
480
      printf("number of robots is %d\n", number_robots);
481

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

    
507
  return 0;
508
}
509

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

    
523
  if (!command) {
524
    return -1;
525
  }
526

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

    
530
    if (strlen(next_token) > MAX_TOKEN_SIZE-1) {
531
      return -1;
532
    }
533

    
534
    strcpy(tokens[number_tokens], next_token);
535

    
536
    number_tokens++;
537
    next_token = end_token + 1;
538

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

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

    
549
    strcpy(tokens[number_tokens], next_token);
550

    
551
    number_tokens++;
552
  }
553

    
554
  return number_tokens;
555
}
556

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

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

    
581
  return 0;
582
}