-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.c
More file actions
370 lines (333 loc) · 9.81 KB
/
server.c
File metadata and controls
370 lines (333 loc) · 9.81 KB
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stat.h>
#include <pthread.h>
#define SERVER_PORT 80
#ifndef NULL
#define NULL ((void*)0)
#endif
static int debug = 0;
// socket的错误处理
void err_msg(const int *sockfd, char *name) {
fprintf(stderr, "%s error! reason:%s\n", name, strerror(errno));
close(*sockfd);
exit(1);
}
// 获取请求信息
int get_line(int socket, char *buf, int size) {
int count = 0;
char end = '\0';
int len;
while ((count < size - 1) && end != '\n') {
len = read(socket, &end, 1);
if (len == 1) {
if (end == '\r') {
continue;
} else if (end == '\n') {
break;
}
// 处理一般字符
buf[count] = end;
count++;
} else if (len == -1) {
// 读取出错
perror("read failed!");
count = -1;
break;
} else {
// 返回0,客户端关闭连接
fprintf(stderr, "client close.\n");
count = -1;
break;
}
}
if (count >= 0)
buf[count] = '\0';
return count;
}
// 400请求错误页面
void bad_request(int client_sock) {
const char *reply = "\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<title>400 Bad Request</title>\n\
</head>\n\
<body>\n\
<h1>400 Bad Request</h1>\n\
<p>The server cannot process the request due to a client error.</p>\n\
</body>\n\
</html>";
int len = write(client_sock, reply, strlen(reply));
if (debug)
fprintf(stdout, "%s", reply);
if (len <= 0) {
fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
}
}
// 404未找到页面
void file_not_found(int client_sock) {
const char *reply = "\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<title>404 Not Found</title>\n\
</head>\n\
<body>\n\
<h1>404 Not Found</h1>\n\
<p>The page you requested could not be found.</p>\n\
</body>\n\
</html>";
int len = write(client_sock, reply, strlen(reply));
if (debug)
fprintf(stdout, "%s", reply);
if (len <= 0) {
fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
}
}
// 500服务器内部错误页面
void inner_error(int client_sock) {
const char *reply = "\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<title>500 Internal Server Error</title>\n\
</head>\n\
<body>\n\
<h1>500 Internal Server Error</h1>\n\
<p>Sorry, something went wrong on the server.</p>\n\
</body>\n\
</html>";
int len = write(client_sock, reply, strlen(reply));
if (debug)
fprintf(stdout, "%s", reply);
if (len <= 0) {
fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
}
}
// 501方法未实现页面
void unimplemented(int client_sock) {
const char *reply = "\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<title>501 Not Implemented</title>\n\
</head>\n\
<body>\n\
<h1>501 Not Implemented</h1>\n\
<p>The requested method is not implemented on this server.</p>\n\
</body>\n\
</html>";
int len = write(client_sock, reply, strlen(reply));
if (debug)
fprintf(stdout, "%s", reply);
if (len <= 0) {
fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
}
}
// 发送HTTP头部
int headers(int client_sock, FILE *resource) {
struct stat st;
char tmp[64];
char main_header[1024];
strcpy(main_header, "HTTP/1.0 200 OK\r\nServer: Daj's server\r\nContent-Type: text/html\r\nConnection: Close\r\n");
// 获取文件ID
int file_id = fileno(resource);
// 获取文件ID出错情况
if (fstat(file_id, &st) == -1) {
inner_error(client_sock);
return -1;
}
// 获取HTML长度并将Content-Length追加到响应头
snprintf(tmp, 64, "Content-Length: %ld\r\n\r\n", st.st_size);
strcat(main_header, tmp);
if (debug)
fprintf(stdout, "header: %s\n", main_header);
// 向socket发送消息并输出错误信息
if (send(client_sock, main_header, strlen(main_header), 0) < 0) {
fprintf(stderr, "send failed. data: %s,reason: %s\n", main_header, strerror(errno));
return -1;
}
return 0;
}
// 发送HTML内容
void content(int client_sock, FILE *resource) {
char buf[1024];
fgets(buf, sizeof(buf), resource);
while (!feof(resource)) {
int len = write(client_sock, buf, strlen(buf));
// 写入错误处理
if (len < 0) {
fprintf(stderr, "send content error. reason: %s\n", strerror(errno));
break;
}
if (debug)
fprintf(stdout, "%s", buf);
fgets(buf, sizeof(buf), resource);
}
}
// 响应HTTP请求
void resp_to_req(int client_sock, const char *path) {
int ret;
FILE *resource = NULL;
resource = fopen(path, "r+");
if (resource == NULL) {
file_not_found(client_sock);
return;
}
// 发送HTTP头部
ret = headers(client_sock, resource);
// 发送内容
if (!ret)
content(client_sock, resource);
// 关闭文件
fclose(resource);
}
// 解析HTTP请求
void *parse_http_req(void *pclient_sock) {
// 请求行长度
int len;
// 请求行
char buf[256];
// 请求方法
char req_method[64];
// 请求资源
char url[256];
// 文件路径
char path[256];
// 文件元数据结构体
struct stat st;
// 客户端socket
int client_sock = *(int *) pclient_sock;
// 读取请求行
len = get_line(client_sock, buf, sizeof(buf));
// 读取请求行成功,进行处理
if (len > 0) {
int i = 0, j = 0;
while (!isspace(buf[j]) && i < sizeof(req_method) - 1) {
req_method[i++] = buf[j++];
}
req_method[i] = '\0';
if (debug)
printf("request method: %s\n", req_method);
// 处理GET请求
if (strncasecmp(req_method, "GET", i) == 0) {
if (debug)
printf("request method = GET\n");
// 获取url
while (isspace(buf[j++])); // 跳过空白格
i = 0;
while (!isspace(buf[j]) && i < sizeof(url) - 1) {
url[i++] = buf[j++];
}
url[i] = '\0';
if (debug)
printf("url: %s\n", url);
// 继续读取请求头
do {
len = get_line(client_sock, buf, sizeof(buf));
if (debug)
printf("read: %s\n", buf);
} while (len > 0);
// 定位本地HTML文件
{
char *pos = strchr(url, '?');
if (pos) {
*pos = '\0';
printf("real url: %s\n", url);
}
}
// 将url与目录拼接成路径
sprintf(path, "./html_docs/%s", url);
if (debug)
printf("path: %s\n", path);
// 判断文件是否存在
if (stat(path, &st) == -1) {
// 文件不存在,响应未找到
fprintf(stderr, "stat %s failed, reason: %s\n", path, strerror(errno));
file_not_found(client_sock);
} else {
// 文件存在,执行HTTP响应
if (S_ISDIR(st.st_mode)) {
strcat(path, "/index.html");
}
resp_to_req(client_sock, path);
}
} else {
// 对于非GET请求的响应
fprintf(stderr, "warning! other request [%s]\n", req_method);
do {
len = get_line(client_sock, buf, sizeof(buf));
if (debug)
printf("read: %s\n", buf);
} while (len > 0);
unimplemented(client_sock);
}
} else {
bad_request(client_sock);
}
// 处理完请求后关闭客户端的socket连接
close(client_sock);
// 释放手动分配的内存
if(pclient_sock)
free(pclient_sock);
}
int main() {
// 设置邮箱
int ret;
// 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 创建socket错误处理
if (sockfd == -1) {
err_msg(&sockfd, "create socket");
}
// 定义结构体变量
struct sockaddr_in server_addr;
// 清零结构体
bzero(&server_addr, sizeof(server_addr));
// 选择IPv4协议族
server_addr.sin_family = AF_INET;
// 监听本地所有IP地址
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定端口号
server_addr.sin_port = htons(SERVER_PORT);
// 将socket与结构体绑定
ret = bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
// bind错误处理
if (ret == -1) {
err_msg(&sockfd, "bind");
}
// 设置监听并设定接收队列长度
ret = listen(sockfd, 128);
// listen函数的错误处理
if (ret == -1) {
err_msg(&sockfd, "listen");
}
// 提示语
printf("等待客户端的连接\n");
while (1) {
struct sockaddr_in client;
socklen_t client_addr_len = sizeof(client);
char client_ip[64];
int client_sock, len;
char buf[256];
pthread_t id;
int *pclient_sock = NULL;
// 建立一个socket描述符对客户端进行响应
client_sock = accept(sockfd, (struct sockaddr *) &client, &client_addr_len);
// 输出客户端地址和端口号
printf("client ip: %s\nport: %d\n",
inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)),
ntohs(client.sin_port));
// 启动线程处理HTTP请求
pclient_sock = (int *) malloc(sizeof(int));
*pclient_sock = client_sock;
pthread_create(&id, NULL, parse_http_req, pclient_sock);
}
}