linux--http服务器编写
注:这是在linux下编写的一个基于B/S模型的浏览器和服务器之间的通信http服务器项目,底层用的是面向链接、可靠的字节流传输的TCP协议,同时用了sock编程,多线程编程,管道,CGI123 #include"http.h"45 void print_debug(const char * msg)6 {7 #ifdef _DEBUG
·
注:这是在linux下编写的一个基于B/S模型的浏览器和服务器之间的通信http服务器项目,底层用的是面向链接、可靠的字节流传输的TCP协议,同时用了sock编程,多线程编程,管道,CGI
1
2
3 #include"http.h"
4
5 void print_debug(const char * msg)
6 {
7 #ifdef _DEBUG_
8 printf("%s\n", msg);
9 #endif
10 }
11
12 void usage(const char *proc)
13 {
14 printf("Usage : %s [PORT]\n", proc);
15 }
16
17 void print_log(const char *fun, int line, int err_no, const char *err_str)
18 {
19 printf("[%s: %d] [%d] [%s]\n", fun, line, err_no, err_str);
20 }
21 static void bad_request(int client)
22 {
23 print_debug("enter our fault...\n");
24 char buf[1024];
25 sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
26 send(client, buf, strlen(buf), 0);
27 sprintf(buf, "Content-type: text/html\r\n");
28 send(client, buf, strlen(buf), 0);
29 sprintf(buf, "\r\n");
30 send(client, buf, strlen(buf), 0);
31 sprintf(buf, "<html></br><p>your enter message is a bad request</p></br></html>\r\n");
32 send(client, buf, strlen(buf), 0);
33 }
34 //清理头部信息
35 void clear_header(int client)
36 {
37 char buf[1024];
38 memset(buf, '\0', sizeof(buf));
39 int ret = 0;
40 do{
41 ret = get_line(client, buf, sizeof(buf));
42 }while(ret > 0 && strcmp(buf, "\n") != 0 );
43 }
44 //从sock里取出字符存放到buf里
45 int get_line(int sock, char *buf, int max_len)
46 {
47 if( !buf || max_len < 0)
48 return -1;
49
50 int i = 0;
51 int n = 0;
52 char ch = '\0';
53 while( i < max_len-1 && ch != '\n' ){
54 n = recv(sock, &ch, 1, 0);
55 if(n > 0){//success
56 if( ch == '\r' ){
57 n = recv(sock, &ch, 1,MSG_PEEK);//窥探方式取字符
58 if( n > 0 && ch == '\n' ){//windows
59 recv(sock, &ch, 1, 0);//delete
60 }else{
61 ch = '\n';
62 }
63 }
64 buf[i++] = ch;
65 }else{//failed
66 ch = '\n';
67 }
68 }
69 buf[i] = '\0';
70 return i;
71 }
72
73 void echo_error_to_client(int client, int error_code)
74 {
75 switch(error_code){
76 case 400://request error
77 bad_request(client);
78 break;
79 case 404://not found
80 // not_found(client);
81 break;
82 case 500://server error
83 // server_error(client);
84 break;
85 case 503://server unavailable
86 // server_unavailable(client);
87 break;
88 default:
89 // default_error(client);
90 break;
91
92 }
93 }
94 void echo_html(int client, const char *path, unsigned int file_size)
95 {
96 if( !path ){
97 return;
98 }
99 int in_fd = open(path, O_RDONLY);
100 if(in_fd < 0){
101 print_debug("open index.html error");
102 return;
103 }
104 print_debug("open index.html success");
105 char echo_line[1024];
106 memset(echo_line, '\0', sizeof(echo_line));
107 strncpy(echo_line, HTTP_VERSION, strlen(HTTP_VERSION)+1);
108 strcat(echo_line, " 200 OK");
109 strcat(echo_line, "\r\n\r\n");
110 send(client, echo_line,strlen(echo_line), 0);
111 print_debug("send echo head success");
112 if( sendfile(client, in_fd, NULL, file_size) < 0 ){
113 print_debug("send_file error");
114 //echo_error_to_client();
115 close(in_fd);
116 return;
117 }
118 print_debug("sendfile success");
119 close(in_fd);
120 }
121 //请求链接函数
122 void *accept_request(void *arg)
123 {
124 printf("accept_request\n");
125 pthread_detach(pthread_self());//detach让线程分离
126 int sock_client = (int)arg;
127
128 int cgi = 0;
129 char *query_string = NULL; //接受资源后的参数
130 char method[_COMM_SIZE_/10];//方法
131 char url[_COMM_SIZE_]; //资源
132 char buffer[_COMM_SIZE_]; //存放从浏览器里取得的所以字符
133 char path[_COMM_SIZE_]; //运行路径
134
135 memset(method, '\0', sizeof(method));
136 memset(url, '\0', sizeof(url));
137 memset(buffer, '\0', sizeof(buffer));
138 memset(path, '\0', sizeof(path));
139
140 if(get_line(sock_client, buffer, sizeof(buffer)) < 0){
141 return NULL;
142 }
143 int i = 0;
144 int j = 0;//buffer line index
145 //isspace判断是否为空格,如果是空格,返回1,否则返回0
146 while( !isspace(buffer[j]) && i < sizeof(method)-1 && j < sizeof(buffer)-1){
147 method[i] = buffer[j];
148 i++, j++;
149 }
150 //忽略大小写比较
151 if( strcasecmp(method, "GET") && strcasecmp(method, "POST")){
152 return NULL;
153 }
154 while( isspace(buffer[j]) && j < sizeof(buffer)){
155 j++;
156 }
157 i = 0;
158 while(!isspace(buffer[j]) && i < sizeof(url)-1 && j < sizeof(buffer)){
159 url[i] = buffer[j];
160 i++;j++;
161 }
162 print_debug(method);
163 print_debug(url);
164
165 if(strcasecmp(method, "POST") == 0)
166 cgi = 1;
167 if(strcasecmp(method, "GET") == 0){
168 query_string = url;
169 while( *query_string != '?' && *query_string != '\0'){
170 query_string++;
171 }
172 if( *query_string == '?' ){//url = /XXX/XXX + arg
173 *query_string = '\0';
174 query_string++;
175 cgi = 1;
176 }
177 }
178 sprintf(path, "htdocs%s", url);
179 if(path[strlen(path)-1] == '/'){
180 strcat(path, MAIN_PAGE);
181 }
182 print_debug(path);
183
184 struct stat st;
185 if( stat(path, &st) < 0 ){ //failed, does not exist
186 print_debug("miss cgi");
187 clear_header(sock_client);
188 }else{
189 if(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH)
190 cgi = 1;
191
192 if(cgi)
193 exe_cgi(sock_client, path, method, query_string);
194 else{
195 clear_header(sock_client);
196 print_debug("begin enter our echo_html");
197 echo_html(sock_client, path, st.st_size);
198 }
199 }
200 close(sock_client);
201 return NULL;
202 }
203 //执行cgi函数
204 void exe_cgi(int sock_client, const char *path, const char *method,const char *query_string)
205 {
206 print_debug("enter cgi");
207 char buf[_COMM_SIZE_];
208 int numchars = 0;
209 int content_length = -1;
210
211 int cgi_input[2] = {0, 0};
212 int cgi_output[2] = {0, 0};
213
214 pid_t id;
215 print_debug(method);
216
217 if(strcasecmp(method, "GET") == 0){//GET
218 clear_header(sock_client);
219 }else{//POST
220 do{
221 memset(buf, '\0', sizeof(buf));
222 numchars = get_line(sock_client, buf, sizeof(buf));
223 if(strncasecmp(buf, "Content-Length:", strlen("Content-Length:")) == 0){
224 content_length = atoi(&buf[16]);
225 }
226 }while(numchars > 0 && strcmp(buf, "\n") != 0);
227 if( content_length == -1 ){
228 //echo_error_to_client();
229 return;
230 }
231 }
232 memset(buf, '\0', sizeof(buf));
233 strcpy(buf, HTTP_VERSION);
234 strcat(buf, " 200 OK\r\n\r\n");
235 //把buf里的数据发送到sock_client里
236 send(sock_client, buf, strlen(buf), 0);
237 //创建cgi输入管道
238 if( pipe(cgi_input) == -1 ){//pipe error//echo_error_to_client();
239 return;
240 }
241 //创建cgi输出管道
242 if( pipe(cgi_output) == -1 ){
243 close(cgi_input[0]);
244 close(cgi_input[1]);//echo_error_to_client();
245 return;
246 }
247 //创建进程
248 if( (id = fork()) < 0){//fork error
249 close(cgi_input[0]);
250 close(cgi_input[1]);
251 close(cgi_output[0]);
252 close(cgi_output[1]);//echo_error_to_client();
253 return;
254 }else if( id == 0 ){//child
255 char query_env[_COMM_SIZE_/10];
256 char method_env[_COMM_SIZE_];
257 char content_len_env[_COMM_SIZE_];
258
259 memset(method_env, '\0', sizeof(method_env));
260 memset(query_env, '\0', sizeof(query_env));
261 memset(content_len_env, '\0', sizeof(content_len_env));
262
263 close(cgi_input[1]);
264 close(cgi_output[0]);
265 //将标准输出和标准输入重定向到cgi_input和cgi_output
266 dup2(cgi_input[0], 0);
267 dup2(cgi_output[1], 1);
268
269 sprintf(method_env, "REQUEST_METHOD=%s", method);
270 putenv(method_env);
271
272 if(strcasecmp("GET", method) == 0){//POST
273 sprintf(query_env, "QUERY_STRING=%s", query_string);
274 putenv(query_env);
275 }else{//POST
276 sprintf(content_len_env, "CONTENT_LENGTH=%d", content_length);
277 putenv(content_len_env);
278 }
279 execl(path, path, NULL);
280 exit(1);
281 }else{//father
282 close(cgi_input[0]);
283 close(cgi_output[1]);
284 int i = 0;
285 char c = '\0';
286 if(strcasecmp("POST", method) == 0){
287 for(; i < content_length; i++ ){
288 recv(sock_client, &c, 1, 0);//从sock_client里取出一个字符并存入c里
289 write(cgi_input[1], &c, 1); //从c里取出一个字符发送到cgi_input[1]里,即标准输入里
290 }
291 }
292 while( read(cgi_output[0], &c, 1) > 0 ){
293 send(sock_client, &c, 1, 0);//把c里字符send到sock_client中
294 }
295
296 close(cgi_input[1]);
297 close(cgi_output[0]);
298
299 waitpid(id, NULL, 0);//防止僵尸进程产生
300 }
301 }
302
303 //创建端口,并初始化
304 int start(short port)
305 {
306 int listen_sock = socket(AF_INET, SOCK_STREAM, 0);//创建TCP协议的sock(SOCK_STREAM:字节流)
307 if(listen_sock == -1){
308 print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
309 exit(1);
310 }
311 //reuse port
312 int flag = 1;
313 setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));//端口复用
314
315 //初始化sock函数家族
316 struct sockaddr_in local;
317 local.sin_family = AF_INET;
318 local.sin_port = htons(port);//host -> net
319 local.sin_addr.s_addr = htonl(INADDR_ANY);
320 socklen_t len = sizeof(local);
321 //绑定端口号,成功返回0,失败返回-1
322 if(bind(listen_sock, (struct sockaddr*)&local, len) == -1){
323 print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
324 exit(2);
325 }
326 //监听端口号
327 if(listen(listen_sock, _BACK_LOG_) == -1){
328 print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
329 exit(3);
330 }
331 return listen_sock; //sucess
332 }
333 //得到一个随机端口号
334 short get_port(short port)
335 {
336 if(port==0)
337 {
338 socklen_t length=sizeof(local);
339 if(getsockname(listen_sock,(struct sockaddr*)&local,&length)==-1)
340 {
341 print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
342 exit(1);
343 }
344 port=ntohs(local.sin_port);
345 // printf("port is %d\n",port);
346 }
347 return port;
348 }
349 int main(int argc,char *argv[])
350 {
351 short port=0;
352 if(argc >1)
353 port=atoi(argv[1]);
354 else
355 port=get_port(port);//得到一个随机的端口号
356
357 int sock=start(port);
358 printf("***start success sock:%d ***\n",sock);
359 struct sockaddr_in client;
360 socklen_t len=0;
361 int count=1;//线程链接个数
362 char buf[4096];
363 memset(buf,'\0',sizeof(buf));
364 while(1)
365 {
366 //阻塞式等待客服端链接
367 int new_sock=accept(sock,(struct sockaddr*)&client,&len);
368 // recv(new_sock,buf,sizeof(buf),0);
369 // printf("buf:%s\n",buf);
370 if(new_sock<0)
371 {
372 print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
373 continue;
374 }
375 pthread_t new_thread;
376 //创建链接来处理客服端链接
377 pthread_create(&new_thread,NULL,accept_request,(void*)new_sock);
378 printf("get %d new connection\n",count++);
379 }
380 return 0;
381 }
2:http.h
#ifndef _HTTP_H
2 #define _HTTP_H
3
4 #include<stdio.h>
5 #include<stdlib.h>
6 #include<unistd.h>
7 #include<sys/types.h>
8 #include<sys/socket.h>
9 #include<netinet/in.h>
10 #include<arpa/inet.h>
11 #include<errno.h>
12 #include<pthread.h>
13 #include<string.h>
14 #include<sys/sendfile.h>
15 #include<sys/stat.h>
16 #include<fcntl.h>
17
18
19 #define _BACK_LOG_ 5
20 #define _COMM_SIZE_ 1024
21 #define MAIN_PAGE "index.html"
22 #define HTTP_VERSION "HTTP/1.1"
23
24 void print_debug(const char * msg);
25 void usage(const char *proc);
26 void print_log(const char *fun, int line, int err_no, const char *err_str);
27 static void bad_request(int client);
28 void clear_header(int client);
29 int get_line(int sock, char *buf, int max_len);
30 void echo_error_to_client(int client, int error_code);
31 void echo_html(int client, const char *path, unsigned int file_size);
32 void *accept_request(void *arg);
33 void exe_cgi(int sock_client, const char *path, const char *method,const char *query_string);
34 int start(short port);
35 #endif
更多推荐
已为社区贡献1条内容
所有评论(0)