Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (10.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 "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
  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
          Command command;
232
          if (command.parse_command(temporary_command_buffer, i, this) < 0) {
233
            printf("There was an error parsing command\n");
234
            break;
235
          }
236
        }
237
      }
238
    }
239

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

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

    
251
  return 0;
252
}
253

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

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

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

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

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

    
286
  return 0;
287
}
288

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

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

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

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

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

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

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

    
328
  return 0;
329
}
330

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

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

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

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

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

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

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

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

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

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

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

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

    
379