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