-
Notifications
You must be signed in to change notification settings - Fork 54
/
server.c
223 lines (191 loc) · 6.77 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
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
#include "./include/server.h"
#include "./include/util_http.h"
#include "./include/cJSON.h"
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
/**
* 创建一个server socket
*/
int server_start() {
int sock_fd;
struct sockaddr_in server_addr;
int flags;
server_addr.sin_family = AF_INET; // 协议族是IPV4协议
server_addr.sin_port = htons(PORT); // 将主机的无符号短整形数转换成网络字节顺序
server_addr.sin_addr.s_addr = inet_addr(HOST); // 用inet_addr函数将字符串host转为int型
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
// 获取服务器socket的设置,并添加"不阻塞"选项
flags = fcntl(sock_fd, F_GETFL, 0);
fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK);
if (bind(sock_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
perror("bind_error");
exit(1);
}
if (listen(sock_fd, REQUEST_QUEUE_LENGTH) == -1) {
perror("listen error");
exit(1);
}
return (sock_fd);
}
/**
* 处理请求参数
*
* @param request_content
*/
char *deal_request(char *request_content, int client_fd) {
struct sockaddr_in client_addr;
socklen_t len_client_addr = sizeof(client_addr);
struct request cgi_request;
parse_request(request_content, &cgi_request);
cgi_request.SERVER_PORT = PORT;
getpeername(client_fd, (struct sockaddr *) &client_addr, &len_client_addr);
inet_ntop(AF_INET, &client_addr.sin_addr, cgi_request.REMOTE_ADDR, sizeof(cgi_request.REMOTE_ADDR));
char *request_json = create_json(&cgi_request);
char *response_json = exec_php(request_json);
// todo parse json and build http_response
return response_json;
}
char *create_json(struct request *cgi_request) {
cJSON *root;
root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "REQUEST_METHOD", cgi_request->REQUEST_METHOD);
cJSON_AddStringToObject(root, "SCRIPT_NAME", cgi_request->SCRIPT_NAME);
cJSON_AddStringToObject(root, "SERVER_PROTOCOL", cgi_request->SERVER_PROTOCOL);
cJSON_AddStringToObject(root, "SERVER_NAME", cgi_request->SERVER_NAME);
cJSON_AddStringToObject(root, "QUERY_STRING", cgi_request->QUERY_STRING);
cJSON_AddStringToObject(root, "REMOTE_ADDR", cgi_request->REMOTE_ADDR);
cJSON_AddStringToObject(root, "CONTENT_TYPE", cgi_request->CONTENT_TYPE);
cJSON_AddStringToObject(root, "POST_DATA", cgi_request->POST_DATA);
cJSON_AddNumberToObject(root, "SERVER_PORT", cgi_request->SERVER_PORT);
cJSON_AddNumberToObject(root, "CONTENT_LENGTH", cgi_request->CONTENT_LENGTH);
char *json = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return json;
}
/*
* todo define response struct and build response header
void parse_json(char *response_json, struct response *cgi_response) {
cJSON *json = cJSON_Parse(response_json);
cgi_response.CONTENT_TYPE = cJSON_GetObjectItem(cJSON, "CONTENT_TYPE");
cgi_response.HTTP_CODE = cJSON_GetObjectItem(cJSON, "HTTP_CODE");
cgi_response.RESPONSE_CONTENT = cJSON_GetObjectItem(cJSON, "RESPONSE_CONTENT");
return;
}
*/
/**
* 执行PHP脚本以返回执行结果
*
* @param args
* @return
*/
char *exec_php(char *args) {
// 这里不能用变长数组,需要给command留下足够长的空间,以存储args参数,不然拼接参数时会栈溢出
char command[BUFF_SIZE] = "php /tmp/index.php ";
FILE *fp;
static char buff[BUFF_SIZE]; // 声明静态变量以返回变量指针地址
char line[BUFF_SIZE];
strcat(command, args);
memset(buff, 0, BUFF_SIZE); // 静态变量会一直保留,这里初始化一下
if ((fp = popen(command, "r")) == NULL) {
strcpy(buff, "服务器内部错误");
} else {
// fgets会在获取到换行时停止,这里将每一行拼接起来
while (fgets(line, BUFF_SIZE, fp) != NULL) {
strcat(buff, line);
};
}
return buff;
}
/**
* 注册epoll事件
*
* @param epoll_fd epoll句柄
* @param fd socket句柄
* @param state 监听状态
*/
void epoll_register(int epoll_fd, int fd, int state) {
struct epoll_event event;
event.events = state;
event.data.fd = fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);
}
/**
* 注销epoll事件
*
* @param epoll_fd epoll句柄
* @param fd socket句柄
* @param state 监听状态
*/
void epoll_cancel(int epoll_fd, int fd, int state) {
struct epoll_event event;
event.events = state;
event.data.fd = fd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event);
}
/**
* 受理客户端连接
*
* @param server_fd
* @param epoll_fd
*/
void accept_client(int server_fd, int epoll_fd) {
int client_fd;
struct sockaddr_in client_addr;
socklen_t len_client_addr = sizeof(client_addr);
client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &len_client_addr);
epoll_register(epoll_fd, client_fd, EPOLLIN);
}
/**
* 处理客户端的请求信息
*
* @param client_fd
* @param epoll_fd
*/
void deal_client(int client_fd, int epoll_fd) {
char *response_content;
char http_request[BUFF_SIZE], response_header[BUFF_SIZE], http_response[BUFF_SIZE];
memset(http_request, 0, BUFF_SIZE); // 清空缓冲区内容
// fixme, 超过BUFF_SIZE的数据接收不到,使用while循环接收数据,直到返回EAGAIN ERROR
recv(client_fd, http_request, BUFF_SIZE, 0);
// 如果收到请求内容为空的,表示客户端正常断开的连接
if (strlen(http_request) == 0) {
epoll_cancel(epoll_fd, client_fd, EPOLLIN);
return;
}
response_content = deal_request(http_request, client_fd);
sprintf(response_header, header_tmpl, strlen(response_content));
sprintf(http_response, "%s%s", response_header, response_content);
send(client_fd, http_response, sizeof(http_response), 0);
}
int main() {
int i;
int event_num;
int server_fd;
int epoll_fd;
int fd;
struct epoll_event events[MAX_EVENTS];
server_fd = server_start();
epoll_fd = epoll_create(FD_SIZE);
epoll_register(epoll_fd, server_fd, EPOLLIN | EPOLLET);// 这里注册socketEPOLL事件为ET模式
while (1) {
event_num = epoll_wait(epoll_fd, events, MAX_EVENTS, 0);
for (i = 0; i < event_num; i++) {
fd = events[i].data.fd;
if ((fd == server_fd) && (events[i].events == EPOLLIN)) {
accept_client(server_fd, epoll_fd);
} else if (events[i].events == EPOLLIN) {
deal_client(fd, epoll_fd);
} else if (events[i].events == EPOLLOUT)
// todo 数据过大,缓冲区不足的情况待处理
continue;
}
usleep(EPOLL_WAIT_US);
}
}