Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (9.93 KB)

1 11 emarinel
/**
2 29 jknichel
 * @file ConnectionPool.cpp
3
 *
4 11 emarinel
 * @author Jason Knichel
5
 * @date 7/22/07
6 144 jknichel
 *
7 11 emarinel
 */
8
9
#include <sys/select.h>
10
#include <ctype.h>
11
#include <errno.h>
12
#include <unistd.h>
13
#include <string.h>
14
#include <stdlib.h>
15
#include <stdio.h>
16
17
#include "includes/ConnectionPool.h"
18 128 jknichel
#include "includes/Command.h"
19 11 emarinel
#include "../lib/colonet_defs.h"
20 161 emarinel
21 118 emarinel
#include <colonet_wireless.h>
22 11 emarinel
23 29 jknichel
/**
24
 * @brief The default constructor for ConnectionPool
25
 */
26 11 emarinel
ConnectionPool::ConnectionPool() {
27
  max_file_descriptor = 0;
28
  next_available_slot = 0;
29
  number_clients_ready = 0;
30
31
  FD_ZERO(&ready_set);
32
  FD_ZERO(&read_set);
33
  FD_ZERO(&write_set);
34
35
  memset(&client_file_descriptor_array, 0, sizeof(int)*MAX_CONNECTIONS);
36
  memset(&read_buffer, 0, sizeof(char *)*MAX_CONNECTIONS);
37
  memset(&read_buffer_size, 0, sizeof(int)*MAX_CONNECTIONS);
38
  memset(&write_buffer, 0, sizeof(char *)*MAX_CONNECTIONS);
39
  memset(&write_buffer_size, 0, sizeof(int)*MAX_CONNECTIONS);
40
}
41
42 29 jknichel
/**
43
 * @brief The destructor for ConnectionPool
44
 */
45 11 emarinel
ConnectionPool::~ConnectionPool() {
46
}
47
48 29 jknichel
/**
49
 * @brief Adds a client to the connection pool
50
 *
51
 * @param client_file_descriptor The file descriptor to add to the connection pool
52
 *
53
 * @return 0 on success, negative error code on failure
54
 */
55 11 emarinel
int ConnectionPool::add_client(int client_file_descriptor) {
56
  if (client_file_descriptor < 0) {
57
    return ERROR_INVALID_CLIENT_DESCRIPTOR;
58
  }
59
60
  if (next_available_slot == MAX_CONNECTIONS) {
61
    return ERROR_TOO_MANY_CLIENTS;
62
  }
63
64
  if (client_file_descriptor > max_file_descriptor) {
65
    max_file_descriptor = client_file_descriptor;
66
  }
67
68
  FD_SET(client_file_descriptor, &ready_set);
69
70
  int next_slot = next_available_slot;
71
72
  client_file_descriptor_array[next_slot] = client_file_descriptor;
73
  read_buffer[next_slot] = (char*) malloc(sizeof(char) * READ_BUFFER_SIZE);
74
  if (!(read_buffer[next_slot])) {
75
    return ERROR_ALLOCATING_MEMORY;
76
  }
77
  read_buffer_size[next_slot] = 0;
78
  write_buffer[next_slot] = (char *)malloc(sizeof(char) * WRITE_BUFFER_SIZE);
79
80
  if (!(write_buffer[next_slot])) {
81
    free(read_buffer[next_slot]);
82
    return ERROR_ALLOCATING_MEMORY;
83
  }
84
85
  write_buffer_size[next_slot] = 0;
86
87
  next_available_slot++;
88
89
  return 0;
90
}
91
92 29 jknichel
/**
93
 * @brief Removes a client from the connection pool
94
 *
95
 * @param The index in the pool of the client to remove
96
 *
97
 * @return 0 on success, negative error code on failure
98
 */
99 27 jknichel
int ConnectionPool::remove_client(int pool_index) {
100
  if (pool_index < 0 || pool_index >= next_available_slot) {
101 11 emarinel
    return ERROR_INVALID_CLIENT_DESCRIPTOR;
102
  }
103
104 27 jknichel
  int client_file_descriptor = client_file_descriptor_array[pool_index];
105 11 emarinel
106 27 jknichel
  if (FD_ISSET(client_file_descriptor, &ready_set)) {
107
    FD_CLR(client_file_descriptor, &ready_set);
108 11 emarinel
  }
109 27 jknichel
  if (FD_ISSET(client_file_descriptor, &read_set)) {
110
    FD_CLR(client_file_descriptor, &read_set);
111 11 emarinel
  }
112 27 jknichel
  if (FD_ISSET(client_file_descriptor, &write_set)) {
113
    FD_CLR(client_file_descriptor, &write_set);
114 11 emarinel
  }
115
116 27 jknichel
  free(read_buffer[pool_index]);
117
  free(write_buffer[pool_index]);
118
  for (int j = pool_index; j < next_available_slot - 1; j++) {
119
    client_file_descriptor_array[pool_index] = client_file_descriptor_array[pool_index+1];
120
    read_buffer[pool_index] = read_buffer[pool_index+1];
121
    read_buffer_size[pool_index] = read_buffer_size[pool_index+1];
122
    write_buffer[pool_index] = write_buffer[pool_index+1];
123
    write_buffer_size[pool_index] = write_buffer_size[pool_index+1];
124 11 emarinel
  }
125
  next_available_slot--;
126
  int temp_max_file_descriptor = 0;
127
128
  for (int j = 0; j < next_available_slot; j++) {
129
    if (client_file_descriptor_array[j] > temp_max_file_descriptor)
130
      temp_max_file_descriptor = client_file_descriptor_array[j];
131
  }
132
  max_file_descriptor = temp_max_file_descriptor;
133
134
  printf("Removing client.\n");
135
136
  return 0;
137
}
138
139 29 jknichel
/**
140
 * @brief Checks the status of the clients
141
 *
142
 * Sees is any clients are ready to read from their file descriptor or are
143
 *  ready to write to their file descriptor.
144
 *
145
 * @return 0 on success, negative error code on error
146
 */
147 11 emarinel
//TODO: test that it drops commands properly if it gets sent too much data
148
//      do we want it to drop the data or drop the connection?
149 118 emarinel
int ConnectionPool::check_clients() {
150 11 emarinel
  int i;
151
152
  for (i = 0; i < next_available_slot; i++) {
153 145 jknichel
    int client_file_descriptor = client_file_descriptor_array[i];
154 11 emarinel
155
    if (FD_ISSET(client_file_descriptor, &read_set)) {
156 145 jknichel
      if (read_data(i, client_file_descriptor) == DECREMENT_INDEX_COUNTER) {
157
        i--;
158
        continue;
159 11 emarinel
      }
160
    }
161
162
    if (FD_ISSET(client_file_descriptor, &write_set)) {
163 145 jknichel
      write_data(i, client_file_descriptor);
164 11 emarinel
    }
165
  }
166
167
  return 0;
168
}
169
170 29 jknichel
/**
171
 * @brief Puts text into a write buffer that will be written to a client's file
172
 *  descriptor sometime when the client is ready to write.
173
 *
174
 * @param pool_index Index in the pool of the client to write to
175
 * @param message The message to be written
176
 * @param length The length of the message
177
 *
178
 * @return 0 on success, negative error code on failure
179
 */
180 14 jknichel
int ConnectionPool::write_to_client(int pool_index, char * message, int length) {
181 22 jknichel
  if (pool_index < 0 || pool_index >= next_available_slot) {
182
    return ERROR_INVALID_CLIENT_ID;
183
  }
184
185
  if (!message) {
186
    return ERROR_INVALID_MESSAGE;
187
  }
188
189
  if (length < 0) {
190
    return ERROR_INVALID_MESSAGE_LENGTH;
191
  }
192
193 34 emarinel
  if (length > (WRITE_BUFFER_SIZE-write_buffer_size[pool_index])) {
194
    //TODO: make this a logging statement instead of a print statement
195
    printf("There is not enough room in the write buffer to send the data to the client.\n");
196
    return ERROR_NOT_ENOUGH_ROOM;
197
  }
198 11 emarinel
199 14 jknichel
  memcpy(write_buffer[pool_index], message, length);
200
  write_buffer_size[pool_index] += length;
201
202
  return 0;
203
}
204
205 29 jknichel
/**
206
 * @brief Sets the socket to listen on
207
 *
208
 * @param listen_socket The socket to listen on
209
 *
210
 * @return void
211
 */
212 143 jknichel
void ConnectionPool::add_new_socket_to_pool(int new_socket) {
213
  if (new_socket < 0)
214 58 jknichel
    return;
215
216 143 jknichel
  FD_SET(new_socket, &ready_set);
217
218
  if (new_socket > max_file_descriptor) {
219
    max_file_descriptor = new_socket;
220
  }
221 11 emarinel
}
222
223 29 jknichel
/**
224
 * @brief Find out what file descriptors are ready to write to and read from
225
 *
226
 * @param listen_socket The socket to listen on
227
 * @param select_timeout The timeout for the select statement
228
 *
229
 * @return 0
230
 */
231 58 jknichel
int ConnectionPool::perform_select(int listen_socket) {
232 11 emarinel
  read_set = ready_set;
233
  write_set = ready_set;
234
235 58 jknichel
  struct timeval select_timeout;
236
  memset(&select_timeout, 0, sizeof(select_timeout));
237
238 11 emarinel
  //TODO(Jason): think about why I put this there
239
  if (max_file_descriptor < listen_socket)
240
    max_file_descriptor = listen_socket;
241
242 58 jknichel
  number_clients_ready = select(max_file_descriptor+1, &(read_set), &(write_set), NULL, &select_timeout);
243 11 emarinel
244 59 jknichel
  if (number_clients_ready < 0) {
245
    perror(__FUNCTION__);
246
  }
247
248 11 emarinel
  return 0;
249
}
250
251
int ConnectionPool::is_socket_ready_to_read(int socket) {
252
  return FD_ISSET(socket, &read_set);
253
}
254
255
int ConnectionPool::get_number_clients_ready() {
256
  return number_clients_ready;
257
}
258
259
260 145 jknichel
int ConnectionPool::read_data(int pool_index, int client_file_descriptor) {
261
  char temporary_buffer[READ_BUFFER_SIZE];
262
  char temporary_command_buffer[READ_BUFFER_SIZE+1];
263
  int num_bytes_read;
264
  int length;
265
  int command_length;
266
267
  num_bytes_read = read(client_file_descriptor, temporary_buffer, READ_BUFFER_SIZE);
268
269
  if (num_bytes_read == 0 || (num_bytes_read == -1 && errno == ECONNRESET)) {
270
    remove_client(pool_index);
271
    return DECREMENT_INDEX_COUNTER;
272
  }
273
274
  while (num_bytes_read > 0) {
275
    length = num_bytes_read;
276
277
    if (length + read_buffer_size[pool_index] > READ_BUFFER_SIZE) {
278
      length = READ_BUFFER_SIZE - read_buffer_size[pool_index];
279
    }
280
281
    memcpy(read_buffer[pool_index]+read_buffer_size[pool_index], temporary_buffer, length);
282
    read_buffer_size[pool_index] += length;
283
    num_bytes_read -= length;
284
285
    if (num_bytes_read > 0) {
286
      memmove(temporary_buffer, temporary_buffer+length, READ_BUFFER_SIZE - length);
287
    }
288
289
    printf("Read buffer is %s\n", read_buffer[pool_index]);
290
291
    char* newline_position;
292
293
    while ((newline_position = strstr(read_buffer[pool_index], "\n"))) {
294
295 161 emarinel
      //if no newline if found in the entire readbuffer (when its full),
296 145 jknichel
      //toss out the command
297
      // because either the command being given is too long or someone is trying
298
      // to do something bad to the server
299
      //TODO: this is from before all this code was put in the loop.  reconsider
300
      //      how to check this error condition and do it elsewhere
301
      if (!newline_position && (read_buffer_size[pool_index] == READ_BUFFER_SIZE)) {
302
        read_buffer_size[pool_index] = 0;
303
        break;
304
      }
305
306
      //if no newline is found then there is not a command in the buffer
307
      if (!newline_position) {
308
        break;
309
      }
310
311
      command_length = (newline_position - read_buffer[pool_index])+1;
312
313
      //the newline was found in garbage in the currently not used portion
314
      // of the read buffer
315
      if (command_length > read_buffer_size[pool_index]) {
316
        break;
317
      }
318
319
      memcpy(temporary_command_buffer, read_buffer[pool_index], command_length);
320
      //do command_length-1 to get rid of the newline terminating the command
321
      temporary_command_buffer[command_length-1] = '\0';
322
      //did this because telnet was putting a \r\n on the end instead of just \n
323
      if (isspace(temporary_command_buffer[command_length-2])) {
324
        temporary_command_buffer[command_length-2] = '\0';
325
      }
326
327
      memmove(read_buffer[pool_index], read_buffer[pool_index]+command_length, read_buffer_size[pool_index] - command_length);
328
      read_buffer_size[pool_index] -= command_length;
329
330
      if (command_length > MAX_COMMAND_LEN) {
331
        printf("The command was too long.  Tossing command out.\n");
332
        break;
333
      }
334
335
      Command command(this);
336
      if (command.parse_command(temporary_command_buffer, pool_index) < 0) {
337
        printf("There was an error parsing command\n");
338
        break;
339
      }
340
    }
341
  }
342
343
  return 0;
344
}
345
346
347
int ConnectionPool::write_data(int pool_index, int client_file_descriptor) {
348
  if (write_buffer_size[pool_index] == 0) {
349
    return 0;
350
  }
351
352
  int sent = write(client_file_descriptor, write_buffer[pool_index], write_buffer_size[pool_index]);
353
  memmove(write_buffer[pool_index], write_buffer[pool_index]+sent, WRITE_BUFFER_SIZE - sent);
354 161 emarinel
  write_buffer_size[pool_index] -= sent;
355 145 jknichel
356
  return 0;
357
}