-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.c
198 lines (158 loc) · 5.59 KB
/
server.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
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include "socket_node.h"
#include "server_util.h"
#include "web_mode.h"
#define EOL "\r\n"
#define EOL_SIZE 2
int main(int argc, char **argv) {
if(argc != 4 && argc != 2){
fprintf(stderr, "The number of arguments is wrong!");
abort();
}
/* client socket address variables */
struct sockaddr_in addr;
unsigned short server_port = atoi(argv[1]);
if (server_port < 18000 || server_port > 18200) {
fprintf(stderr, "Argument port should be in the range of [18000, 18200]!");
abort();
}
/* set argument for www mode */
char* mode = NULL;
char* root_dir = NULL;
if (argc == 4) {
mode = argv[2];
if (strcmp(mode, "www") == 0) {
root_dir = argv[3];
printf("%s", mode);
fprintf(stdout, "web server started ...\n");
}
}
/* socket and option variables */
int sockfd, new_sockfd, max;
/* socket address variables for a connected client */
socklen_t addr_len = sizeof(struct sockaddr_in);
/* maximum number of pending connection requestes */
int BACKLOG = 5;
/* variables for select */
fd_set read_set, write_set;
struct timeval time_out;
int select_retval;
/* linked list for keeping track of connected sockets */
struct node head;
struct node* current;
struct node* next;
/* a buffer to read data */
char* recvbuf;
int BUF_LEN = 65535;//the maxium size in byte
recvbuf = (char *)malloc(BUF_LEN);
if (!recvbuf) {
perror("failed to allocate receiving buffer");
abort();
}
/* initialize dummy head node of linked list */
head.socket = -1;
head.next = 0;
/* binding & listening */
sockfd = server_ready(server_port, BACKLOG);
/* now we keep waiting for incoming connections,
check for incoming data to receive,
check for ready socket to send more data */
while (1){
/* set up the file descriptor bit map that select should be watching */
FD_ZERO (&read_set); /* clear everything */
FD_ZERO (&write_set); /* clear everything */
FD_SET (sockfd, &read_set); /* put the listening socket in */
max = sockfd; /* initialize max */
/* put connected sockets into the read and write sets to monitor them */
for (current = head.next; current; current = current->next) {
FD_SET(current->socket, &read_set);
if (current->pending_data) {
/* there is data pending to be sent, monitor the socket
in the write set so we know when it is ready to take more
data */
FD_SET(current->socket, &write_set);
}
if (current->socket > max) {
/* update max if necessary */
max = current->socket;
}
}
time_out.tv_usec = 100000; /* 1-tenth of a second timeout */
time_out.tv_sec = 0;
/* invoke select, make sure to pass max+1 !!! */
select_retval = select(max + 1, &read_set, &write_set, NULL, &time_out);
if (select_retval < 0) {
perror ("select failed");
abort ();
}
if (select_retval == 0) {
/* no descriptor ready, timeout happened */
continue;
}
/* at least one file descriptor is ready */
if (select_retval > 0) {
/* check the server socket */
if (FD_ISSET(sockfd, &read_set)) {
/* there is an incoming connection, try to accept it */
new_sockfd = accept (sockfd, (struct sockaddr *)&addr, &addr_len);
if (new_sockfd < 0) {
perror ("error accepting connection");
abort ();
}
if (root_dir != NULL) {
recv(new_sockfd, recvbuf, BUF_LEN, 0);
printf("%s\n", recvbuf);
handle_req(recvbuf, new_sockfd, root_dir);
/*send(new_sockfd, "HTTP/1.1 200 OK \r\n", 18, 0);
send(new_sockfd, "Content-Type: text/html \r\n", 26, 0);
send(new_sockfd, "\r\n", 2, 0);
send(new_sockfd, "<html><body><H1>Hello world Jie Zhu</H1></body></html>",46, 0);*/
close(new_sockfd);
continue;
}
/* make the socket non-blocking so send and recv will
return immediately if the socket is not ready.
this is important to ensure the server does not get
stuck when trying to send data to a socket that
has too much data to send already.
*/
if (fcntl (new_sockfd, F_SETFL, O_NONBLOCK) < 0) {
perror ("making socket non-blocking");
abort ();
}
/* the connection is made, everything is ready */
/* let's see who's connecting to us */
fprintf(stdout, "Accepted connection. Client IP address is: %s\n",
inet_ntoa(addr.sin_addr));
/* remember this client connection in our linked list */
add(&head, new_sockfd, addr);
}
/* check other connected sockets, see if there is
anything to read or some socket is ready to send
more pending data */
for (current = head.next; current; current = next) {
next = current->next;
/* number of bytes sent/received */
int count;
/* see if we can now do some previously unsuccessful writes */
if (FD_ISSET(current->socket, &write_set)) {
count = send_non_blocking(current);
handle_error(count, &head, current);
}
if (FD_ISSET(current->socket, &read_set)) {
count = recv_non_blocking(current);
handle_error(count, &head, current);
}
}
}
}
}