Project

General

Profile

Statistics
| Revision:

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
}