/
main.c
251 lines (224 loc) · 5.91 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/**
* Redistribution of this file is permitted under the GNU General
* Public License v2.
*
* Copyright 2012 by Gabriel Parmer.
* Author: Gabriel Parmer, gparmer@gwu.edu, 2012
*/
/*
* This is a HTTP server. It accepts connections on port 8080, and
* serves a local static document.
*
* The clients you can use are
* - httperf (e.g., httperf --port=8080),
* - wget (e.g. wget localhost:8080 /),
* - or even your browser.
*
* To measure the efficiency and concurrency of your server, use
* httperf and explore its options using the manual pages (man
* httperf) to see the maximum number of connections per second you
* can maintain over, for example, a 10 second period.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <sys/wait.h>
#include <pthread.h>
#include <util.h> /* client_process */
#include <server.h> /* server_accept and server_create */
#include "cas.h"
#include "request.h"
#define MAX_DATA_SZ 1024
#define MAX_CONCURRENCY 256
volatile int var = 0;
/*
* This is the function for handling a _single_ request. Understand
* what each of the steps in this function do, so that you can handle
* _multiple_ requests. Use this function as an _example_ of the
* basic functionality. As you increase the server in functionality,
* you will want to probably keep all of the functions called in this
* function, but define different code to use them.
*/
void
server_single_request(int accept_fd)
{
int fd;
/*
* The server thread will always want to be doing the accept.
* That main thread will want to hand off the new fd to the
* new threads/processes/thread pool.
*/
fd = server_accept(accept_fd);
client_process(fd);
return;
}
void
server_multiple_requests(int accept_fd)
{
for (;;) client_process(server_accept(accept_fd));
return;
}
void
server_processes(int accept_fd)
{
pid_t childID;
int fd, status, endID, i;
int count = 0;
int temp = 0;
pid_t *connect = malloc(sizeof(pid_t)*MAX_CONCURRENCY);
for(;;){
while (count >= MAX_CONCURRENCY) {
wait(&status);
int tempCount = count;
count = 0;
for (i = 0 ; i < tempCount ; i++) {
if (waitpid(connect[i], &status, WNOHANG|WUNTRACED)) {
connect[temp] = connect[i];
temp++;
} else
count++;
}
}
if (count >= MAX_CONCURRENCY)
continue;
else {
fd = server_accept(accept_fd);
if (childID == -1) {
perror("FORK ERROR");
break;
}
else if (childID == 0) {
client_process(fd);
break;
}
else {
childID = fork();
temp++;
connect[temp] = childID;
count++;
}
}
}
return;
}
void
server_dynamic(int accept_fd)
{
return;
}
void
server_thread_per(int accept_fd)
{
// 4! Work here!
pthread_t *threadArray = malloc(sizeof(pthread_t) * MAX_CONCURRENCY);
int fd, i, count = 0;
for(;;) {
if (count < MAX_CONCURRENCY)
{
fd = server_accept(accept_fd);
if (!pthread_create(&threadArray[count], NULL, (void*)&client_process, (void*) fd))
count++;
}
else
for (i = 0; i < MAX_CONCURRENCY ; i++)
if (!pthread_join(threadArray[i], NULL))
count--;
}
return;
}
void
pthread_handle (void)
{
int fd;
for (;;) {
struct request *get = get_request();
if (get != NULL) {
fd = (get->fd);
client_process(fd);
}
}
}
void
server_task_queue(int accept_fd)
{
//5! Work here!
int i;
pthread_t *threads = malloc(sizeof(pthread_t) * MAX_CONCURRENCY);
for (i = 0 ; i < MAX_CONCURRENCY ; i++)
pthread_create(&threads[i], NULL, (void *)&pthread_handle, NULL);
printf("ALL THREADS CREATED\n");
for(;;)
{
int fd = server_accept(accept_fd);
put_request(req_create((void*)fd));
printf("ADDING STUFF!\n");
}
return;
}
void
server_thread_pool(int accept_fd)
{
return;
}
typedef enum {
SERVER_TYPE_ONE = 0,
SERVER_TYPE_SINGLET,
SERVER_TYPE_PROCESS,
SERVER_TYPE_FORK_EXEC,
SERVER_TYPE_SPAWN_THREAD,
SERVER_TYPE_TASK_QUEUE,
SERVER_TYPE_THREAD_POOL,
} server_type_t;
int
main(int argc, char *argv[])
{
server_type_t server_type;
short int port;
int accept_fd;
if (argc != 3) {
printf("Proper usage of http server is:\n%s <port> <#>\n"
"port is the port to serve on, # is either\n"
"0: serve only a single request\n"
"1: use only a single thread for multiple requests\n"
"2: use fork to create a process for each request\n"
"3: Extra Credit: use fork and exec when the path is an executable to run the program dynamically. This is how web servers handle dynamic (program generated) content.\n"
"4: create a thread for each request\n"
"5: use atomic instructions to implement a task queue\n"
"6: use a thread pool\n"
"7: to be defined\n"
"8: to be defined\n"
"9: to be defined\n",
argv[0]);
return -1;
}
port = atoi(argv[1]);
accept_fd = server_create(port);
if (accept_fd < 0) return -1;
server_type = atoi(argv[2]);
switch(server_type) {
case SERVER_TYPE_ONE:
server_single_request(accept_fd);
break;
case SERVER_TYPE_SINGLET:
server_multiple_requests(accept_fd);
break;
case SERVER_TYPE_PROCESS:
server_processes(accept_fd);
break;
case SERVER_TYPE_FORK_EXEC:
server_dynamic(accept_fd);
break;
case SERVER_TYPE_SPAWN_THREAD:
server_thread_per(accept_fd);
break;
case SERVER_TYPE_TASK_QUEUE:
server_task_queue(accept_fd);
break;
case SERVER_TYPE_THREAD_POOL:
server_thread_pool(accept_fd);
break;
}
close(accept_fd);
return 0;
}