Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (9.93 KB)

1
/**
2
 * @file ConnectionPool.cpp
3
 *
4
 * @author Jason Knichel
5
 * @date 7/22/07
6
 *
7
 */
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
#include "includes/Command.h"
19
#include "../lib/colonet_defs.h"
20
#include <colonet_wireless.h>
21

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

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

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

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

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

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

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

    
67
  FD_SET(client_file_descriptor, &ready_set);
68

    
69
  int next_slot = next_available_slot;
70

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

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

    
84
  write_buffer_size[next_slot] = 0;
85

    
86
  next_available_slot++;
87

    
88
  return 0;
89
}
90

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

    
103
  int client_file_descriptor = client_file_descriptor_array[pool_index];
104

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

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

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

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

    
135
  return 0;
136
}
137

    
138
/**
139
 * @brief Checks the status of the clients
140
 *
141
 * Sees is any clients are ready to read from their file descriptor or are
142
 *  ready to write to their file descriptor.
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() {
149
  int i;
150

    
151
  for (i = 0; i < next_available_slot; i++) {
152
    int client_file_descriptor = client_file_descriptor_array[i];
153

    
154
    if (FD_ISSET(client_file_descriptor, &read_set)) {
155
      if (read_data(i, client_file_descriptor) == DECREMENT_INDEX_COUNTER) {
156
        i--;
157
        continue;
158
      }
159
    }
160

    
161
    if (FD_ISSET(client_file_descriptor, &write_set)) {
162
      write_data(i, client_file_descriptor);
163
    }
164
  }
165

    
166
  return 0;
167
}
168

    
169
/**
170
 * @brief Puts text into a write buffer that will be written to a client's file
171
 *  descriptor sometime when the client is ready to write.
172
 *
173
 * @param pool_index Index in the pool of the client to write to
174
 * @param message The message to be written
175
 * @param length The length of the message
176
 *
177
 * @return 0 on success, negative error code on failure
178
 */
179
int ConnectionPool::write_to_client(int pool_index, char * message, int length) {
180
  if (pool_index < 0 || pool_index >= next_available_slot) {
181
    return ERROR_INVALID_CLIENT_ID;
182
  }
183

    
184
  if (!message) {
185
    return ERROR_INVALID_MESSAGE;
186
  }
187

    
188
  if (length < 0) {
189
    return ERROR_INVALID_MESSAGE_LENGTH;
190
  }
191

    
192
  if (length > (WRITE_BUFFER_SIZE-write_buffer_size[pool_index])) {
193
    //TODO: make this a logging statement instead of a print statement
194
    printf("There is not enough room in the write buffer to send the data to the client.\n");
195
    return ERROR_NOT_ENOUGH_ROOM;
196
  }
197

    
198
  memcpy(write_buffer[pool_index], message, length);
199
  write_buffer_size[pool_index] += length;
200

    
201
  return 0;
202
}
203

    
204
/**
205
 * @brief Sets the socket to listen on
206
 *
207
 * @param listen_socket The socket to listen on
208
 *
209
 * @return void
210
 */
211
void ConnectionPool::add_new_socket_to_pool(int new_socket) {
212
  if (new_socket < 0)
213
    return;
214

    
215
  FD_SET(new_socket, &ready_set);
216

    
217
  if (new_socket > max_file_descriptor) {
218
    max_file_descriptor = new_socket;
219
  }
220
}
221

    
222
/**
223
 * @brief Find out what file descriptors are ready to write to and read from
224
 *
225
 * @param listen_socket The socket to listen on
226
 * @param select_timeout The timeout for the select statement
227
 *
228
 * @return 0
229
 */
230
int ConnectionPool::perform_select(int listen_socket) {
231
  read_set = ready_set;
232
  write_set = ready_set;
233

    
234
  struct timeval select_timeout;
235
  memset(&select_timeout, 0, sizeof(select_timeout));
236

    
237
  //TODO(Jason): think about why I put this there
238
  if (max_file_descriptor < listen_socket)
239
    max_file_descriptor = listen_socket;
240

    
241
  number_clients_ready = select(max_file_descriptor+1, &(read_set), &(write_set), NULL, &select_timeout);
242

    
243
  if (number_clients_ready < 0) {
244
    perror(__FUNCTION__);
245
  }
246

    
247
  return 0;
248
}
249

    
250
int ConnectionPool::is_socket_ready_to_read(int socket) {
251
  return FD_ISSET(socket, &read_set);
252
}
253

    
254
int ConnectionPool::get_number_clients_ready() {
255
  return number_clients_ready;
256
}
257

    
258

    
259
int ConnectionPool::read_data(int pool_index, int client_file_descriptor) {
260
  char temporary_buffer[READ_BUFFER_SIZE];
261
  char temporary_command_buffer[READ_BUFFER_SIZE+1];
262
  int num_bytes_read;
263
  int length;
264
  int command_length;
265

    
266
  num_bytes_read = read(client_file_descriptor, temporary_buffer, READ_BUFFER_SIZE);
267

    
268
  if (num_bytes_read == 0 || (num_bytes_read == -1 && errno == ECONNRESET)) {
269
    remove_client(pool_index);
270
    return DECREMENT_INDEX_COUNTER;
271
  }
272

    
273
  while (num_bytes_read > 0) {
274
    length = num_bytes_read;
275

    
276
    if (length + read_buffer_size[pool_index] > READ_BUFFER_SIZE) {
277
      length = READ_BUFFER_SIZE - read_buffer_size[pool_index];
278
    }
279

    
280
    memcpy(read_buffer[pool_index]+read_buffer_size[pool_index], temporary_buffer, length);
281
    read_buffer_size[pool_index] += length;
282
    num_bytes_read -= length;
283

    
284
    if (num_bytes_read > 0) {
285
      memmove(temporary_buffer, temporary_buffer+length, READ_BUFFER_SIZE - length);
286
    }
287

    
288
    printf("Read buffer is %s\n", read_buffer[pool_index]);
289

    
290
    char* newline_position;
291

    
292
    while ((newline_position = strstr(read_buffer[pool_index], "\n"))) {
293

    
294
      //if no newline if found in the entire readbuffer (when its full), 
295
      //toss out the command
296
      // because either the command being given is too long or someone is trying
297
      // to do something bad to the server
298
      //TODO: this is from before all this code was put in the loop.  reconsider
299
      //      how to check this error condition and do it elsewhere
300
      if (!newline_position && (read_buffer_size[pool_index] == READ_BUFFER_SIZE)) {
301
        read_buffer_size[pool_index] = 0;
302
        break;
303
      }
304

    
305
      //if no newline is found then there is not a command in the buffer
306
      if (!newline_position) {
307
        break;
308
      }
309

    
310
      command_length = (newline_position - read_buffer[pool_index])+1;
311

    
312
      //the newline was found in garbage in the currently not used portion
313
      // of the read buffer
314
      if (command_length > read_buffer_size[pool_index]) {
315
        break;
316
      }
317

    
318
      memcpy(temporary_command_buffer, read_buffer[pool_index], command_length);
319
      //do command_length-1 to get rid of the newline terminating the command
320
      temporary_command_buffer[command_length-1] = '\0';
321
      //did this because telnet was putting a \r\n on the end instead of just \n
322
      if (isspace(temporary_command_buffer[command_length-2])) {
323
        temporary_command_buffer[command_length-2] = '\0';
324
      }
325

    
326
      memmove(read_buffer[pool_index], read_buffer[pool_index]+command_length, read_buffer_size[pool_index] - command_length);
327
      read_buffer_size[pool_index] -= command_length;
328

    
329
      if (command_length > MAX_COMMAND_LEN) {
330
        printf("The command was too long.  Tossing command out.\n");
331
        break;
332
      }
333

    
334
      Command command(this);
335
      if (command.parse_command(temporary_command_buffer, pool_index) < 0) {
336
        printf("There was an error parsing command\n");
337
        break;
338
      }
339
    }
340
  }
341

    
342
  return 0;
343
}
344

    
345

    
346
int ConnectionPool::write_data(int pool_index, int client_file_descriptor) {
347
  if (write_buffer_size[pool_index] == 0) {
348
    return 0;
349
  }
350

    
351
  int sent = write(client_file_descriptor, write_buffer[pool_index], write_buffer_size[pool_index]);
352
  memmove(write_buffer[pool_index], write_buffer[pool_index]+sent, WRITE_BUFFER_SIZE - sent);
353
  write_buffer_size[pool_index] -= sent;  
354

    
355
  return 0;
356
}