基於HTTP協議的web伺服器專案
阿新 • • 發佈:2018-12-13
專案簡介: 基於 HTTP 協議實現一個多執行緒伺服器,客戶端通過瀏覽器傳送請求,伺服器接收並分析客戶端的請求方法和資源,從而執行相應的邏輯處理,最終將客戶請求的資源以 HTML 頁面的形式呈現,並能進行差錯處理。 專案思路: 首先實現兩個主機不同程序間的通訊,當伺服器收到請求後,要進行分析請求方法,當方法確定後應該拿到請求的資源,接下來要根據資源是否存在執行相應的邏輯處理。GET 方法:如果沒有引數,進入非 cgi 模式執行;否則,進入 cgi 模式執行;只要是 POST 方法就一定要支援 cgi:直接進入 cgi 模式執行。 專案特點: 1、支援客戶端/伺服器模式,客戶端能夠使用 GET、POST 方法請求資源; 2、簡單快速:客戶向伺服器請求服務時,只需傳送請求方法和路徑; 3、靈活:HTTP 允許傳輸任意型別的資料物件,正在傳輸的型別由 Content-Type 加以標記。
專案所包含目錄: sql_connect wwwroot lib
http.c程式碼
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 #include<ctype.h> 7 #include<sys/stat.h> 8 #include<sys/socket.h> 9 #include<netinet/in.h> 10 #include<arpa/inet.h> 11 #include<sys/sendfile.h> 12 #include<pthread.h> 13 14 #define MAX 1024 15 #define HOME_PAGE "index.html" 16 #define PAGE_404 "wwwroot/404.html" 17 18 static void usage(const char *proc) 19 { 20 printf("Usage:%s [port]\n",proc); 21 } 22 23 static int startup(int port) 24 { 25 int sock = socket(AF_INET,SOCK_STREAM,0); 26 if(sock < 0){ 27 perror("socket"); 28 exit(2); 29 } 30 31 int opt = 1; 32 setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); 33 34 struct sockaddr_in local; 35 local.sin_family = AF_INET; 36 local.sin_addr.s_addr = htonl(INADDR_ANY); 37 local.sin_port = htons(port); 38 39 if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){ 40 perror("bind"); 41 exit(3); 42 } 43 44 if(listen(sock,5) < 0){ 45 perror("listen"); 46 exit(4); 47 } 48 49 return sock; 50 } 51 52 static int getLine(int sock,char line[],int size) 53 { 54 //\r,\r\n,\n -> \n 55 char c = 'a'; 56 int i = 0; 57 while(c != '\n' && i < size - 1){ 58 ssize_t s = recv(sock,&c,1,0); 59 if(c == '\r'){ 60 recv(sock,&c,1,MSG_PEEK); 61 if(c== '\n'){ 62 recv(sock,&c,1,0); 63 }else{ 64 c = '\n'; 65 } 66 } 67 line[i++] = c; 68 } 69 line[i] = '\0'; 70 return i; 71 } 72 73 static void clearHeader(int sock) 74 { 75 char line[MAX]; 76 do{ 77 getLine(sock,line,sizeof(line)); 78 }while(strcmp("\n",line)); 79 } 80 81 void show_404(int sock) 82 { 83 char line[MAX]; 84 struct stat st; 85 sprintf(line,"HTTP/1.0 404 NOT FOUND\r\n"); 86 send(sock,line,strlen(line),0); 87 sprintf(line,"Content-Type:text/html:charset=ISO-8859-1\r\n"); 88 send(sock,line,strlen(line),0); 89 sprintf(line,"\r\n"); 90 send(sock,line,strlen(line),0); 91 int fd = open(PAGE_404,O_RDONLY); 92 stat(PAGE_404,&st); 93 sendfile(sock,fd,NULL,st.st_size); 94 close(fd); 95 } 96 97 static void echoErrMsg(int sock,int status_code) 98 { 99 switch(status_code){ 100 case 301: 101 break; 102 case 302: 103 break; 104 case 307: 105 break; 106 case 400: 107 break; 108 case 403: 109 break; 110 case 404: 111 show_404(sock); 112 break; 113 case 500: 114 break; 115 case 503: 116 break; 117 default: 118 break; 119 } 120 } 121 122 int exe_cgi(int sock,char *method,char *path,char *query_string) 123 { 124 char line[MAX]; 125 int content_length = -1; 126 char method_env[MAX/32]; 127 char query_string_env[MAX]; 128 char content_length_env[MAX/8]; 129 130 if(strcasecmp(method,"GET") == 0){ 131 clearHeader(sock); 132 }else{ //POST 133 do{ 134 getLine(sock,line,sizeof(line)); 135 if(strncasecmp(line,"Content-Length: ",16) == 0){ 136 content_length = atoi(line + 16); 137 } 138 }while(strcmp(line,"\n")); 139 if(content_length == -1){ 140 return 400; 141 } 142 } 143 144 int input[2]; 145 int output[2]; 146 147 pipe(input); 148 pipe(output); 149 150 pid_t id = fork(); 151 if(id < 0){ 152 return 500; 153 } 154 else if(id == 0){ //child 155 close(input[1]); 156 close(output[0]); 157 158 dup2(input[0],0); 159 dup2(output[1],1); 160 161 sprintf(method_env,"METHOD=%s",method); 162 putenv(method_env); 163 if(strcasecmp(method,"GET") == 0){ 164 sprintf(query_string_env,"QUERY_STRING=%s",query_string); 165 putenv(query_string_env); 166 } 167 else{ 168 sprintf(content_length_env,"CONTENT_LENGTH=%d",content_length); 169 putenv(content_length_env); 170 } 171 execl(path,path,NULL); 172 exit(1); 173 } 174 else{ 175 close(input[0]); 176 close(output[1]); 177 178 sprintf(line,"HTTP/1.0 200 OK\r\n"); 179 send(sock,line,strlen(line),0); 180 sprintf(line,"Content-Type: text/html:charset=ISO-8859-1\r\n"); 181 send(sock,line,strlen(line),0); 182 sprintf(line,"\r\n"); 183 send(sock,line,strlen(line),0); 184 185 int i = 0; 186 char c; 187 if(strcasecmp(method,"POST") == 0){ 188 for(; i < content_length; i++){ 189 recv(sock,&c,1,0); 190 write(input[1],&c,1); 191 } 192 } 193 194 while(read(output[0],&c,1) > 0){ 195 send(sock,&c,1,0); 196 } 197 198 waitpid(id,NULL,0); 199 close(input[1]); 200 close(output[0]); 201 } 202 return 200; 203 } 204 205 int echo_www(int sock,char *path,int size) 206 { 207 char line[MAX]; 208 clearHeader(sock); 209 210 int fd = open(path,O_RDONLY); 211 if(fd < 0){ 212 return 404; 213 } 214 215 sprintf(line,"HTTP/1.0 200 OK\r\n"); 216 send(sock,line,strlen(line),0); 217 sprintf(line,"Content-Type: text/html;charset=ISO-8859-1\r\n"); 218 send(sock,line,strlen(line),0); 219 sprintf(line,"\r\n"); 220 send(sock,line,strlen(line),0); 221 222 sendfile(sock,fd,NULL,size); 223 224 close(fd); 225 226 return 200; 227 } 228 229 void *handlerRequest(void *arg) 230 { 231 int sock = (int)arg; 232 int status_code = 200; 233 char line[MAX]; 234 char method[MAX/16]; 235 char url[MAX]; 236 char path[MAX]; 237 int cgi = 0; 238 char *query_string = NULL; 239 240 getLine(sock,line,sizeof(line)); 241 printf("%s",line); 242 int i = 0; 243 int j = 0; 244 while(i < sizeof(method)-1 && j < sizeof(line) && !isspace(line[j])){ 245 method[i] = line[j]; 246 i++,j++; 247 } 248 method[i] = '\0'; 249 250 while(j < sizeof(line) && isspace(line[j])){ 251 j++; 252 } 253 //line[] = GET /a/b/c.html HTTP/1.1 254 i = 0; 255 while(i < sizeof(url) - 1 && j < sizeof(line) && !isspace(line[j])){ 256 url[i] = line[j]; 257 i++,j++; 258 } 259 url[i] = '\0'; 260 printf("method: %s, url: %s\n",method,url); 261 262 if(strcasecmp(method,"GET") == 0){ 263 } 264 else if(strcasecmp(method,"POST") == 0){ 265 cgi = 1; 266 } 267 else{ 268 //method error 269 status_code = 400; 270 clearHeader(sock); 271 goto end; 272 } 273 274 if(strcasecmp(method,"GET") == 0){ 275 query_string = url; 276 while(*query_string){ 277 if(*query_string == '?'){ 278 cgi = 1; 279 *query_string = '\0'; 280 query_string++; 281 break; 282 } 283 query_string++; 284 } 285 } 286 287 sprintf(path,"wwwroot%s",url); 288 if(path[strlen(path)-1] == '/'){ 289 strcat(path,HOME_PAGE); 290 } 291 292 struct stat st; 293 if(stat(path,&st) < 0){ 294 status_code = 404; 295 clearHeader(sock); 296 goto end; 297 } 298 else{ 299 // [wwwroot] /a/b/c \0x=100&y=200 300 if(S_ISDIR(st.st_mode)){ 301 strcat(path,"/"); 302 strcat(path,HOME_PAGE); 303 } 304 else if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)){ 305 cgi = 1; 306 } 307 else{ 308 //Do Nothing 309 } 310 311 //method, path, cgi, get->query_string 312 if(cgi){ 313 status_code = exe_cgi(sock,method,path,query_string); 314 } 315 else{ 316 status_code = echo_www(sock,path,st.st_size); 317 } 318 } 319 320 end: 321 if(status_code != 200){ 322 echoErrMsg(sock,status_code); 323 } 324 close(sock); 325 } 326 327 328 329 int main(int argc,char *argv[]) 330 { 331 if(argc != 2){ 332 usage(argv[0]); 333 return 1; 334 } 335 336 int listen_sock = startup(atoi(argv[1])); 337 for(;;){ 338 struct sockaddr_in client; 339 socklen_t len = sizeof(client); 340 int sock = accept(listen_sock,(struct sockaddr*)&client,&len); 341 if(sock < 0){ 342 perror("accept"); 343 continue; 344 } 345 346 pthread_t tid; 347 pthread_create(&tid,NULL,handlerRequest,(void *)sock); 348 pthread_detach(tid); 349 } 350 }
Makefile檔案
1 WORK_DIR=$(shell pwd) 2 cc=gcc 3 bin=httpd 4 src=httpd.c 5 LDFLAGS=-lpthread 6 7 .PHONY:all 8 all:$(bin) cgi 9 10 $(bin):$(src) 11 $(cc) -o [email protected] $^ $(LDFLAGS) 12 13 cgi: 14 cd wwwroot/cgi;make clean;make;cd - 15 cd sql_connect;make clean;make;cd - 16 17 .PHONY:clean 18 clean: 19 rm -f $(bin) 20 cd wwwroot/cgi;make clean;cd - 21 cd sql_connect;make clean;cd - 22 rm -rf output 23 24 .PHONY:output 25 output: 26 mkdir -p output/wwwroot/image 27 mkdir -p output/wwwroot/cgi 28 mkdir -p output/lib 29 cp httpd output 30 cp -rf lib/lib/* output/lib 31 cp wwwroot/*.html output/wwwroot 32 cp wwwroot/image/* output/wwwroot/image 33 cp wwwroot/cgi/math_cgi output/wwwroot/cgi 34 cp sql_connect/insert_cgi output/wwwroot/cgi 35 cp sql_connect/select_cgi output/wwwroot/cgi
sql_connect目錄中的檔案 comm.h程式碼
1 #pragma once
2
3 #include<iostream>
4 #include<string>
5 #include<mysql.h>
6
7 MYSQL* connectMysql();
8 int insertMysql(MYSQL *myfd,const std::string &name,const std::string &sex,const std::string &hobby);
9 void selectMysql(MYSQL *myfd);
10 void closeMysql(MYSQL *myfd);
comm.cpp程式碼
1 #include "comm.h"
2
3 MYSQL* connectMysql()
4 {
5 MYSQL *myfd = mysql_init(NULL);
6 if(mysql_real_connect(myfd,"127.0.0.1","root","","code",3306,NULL,0) == NULL){
7 std::cerr << "connect error" << std::endl;
8 }
9 else{
10 std::cout << "connect success" << std:: endl;
11 }
12
13 return myfd;
14 }
15
16 int insertMysql(MYSQL *myfd,const std::string &name,const std::string &sex,const std::string &hobby)
17 {
18 std::string sql="INSERT INTO st_table (name,sex,hobby) VALUES(\"";
19 sql += name;
20 sql += "\",\"";
21
22 sql += sex;
23 sql += "\",\"";
24 sql += hobby;
25 sql += "\")";
26
27 std::cout << sql << std::endl;
28
29 return mysql_query(myfd,sql.c_str());
30 }
31
32
33 void selectMysql(MYSQL *myfd)
34 {
35 std::string sql = "SELECT * FROM st_table";
36 mysql_query(myfd,sql.c_str());
37 MYSQL_RES *result = mysql_store_result(myfd);
38 int lines = mysql_num_rows(result);
39 int cols = mysql_num_fields(result);
40 std::cout << "<table border=\"1\"> " << std::endl;
41 MYSQL_FIELD *field = mysql_fetch_fields(result);
42 int i = 0;
43 std::cout << "<tr>" << std::endl;
44 for(; i < cols; i++){
45 std::cout << "<th>" << field[i].name << "</th>";
46 }
47 std::cout << "</tr>" << std::endl;
48
49 for(i=0; i < lines; i++){
50 std::cout << "<tr>" << std::endl;
51 MYSQL_ROW line = mysql_fetch_row(result);
52 int j = 0;
53 for(; j < cols; j++){
54 std::cout << "<td>" << line[j] << "</td>";
55 }
56 std::cout << "</tr>" << std::endl;
57 }
58 std::cout << "</table>" << std::endl;
59 free(result);
60 }
61
62 void closeMysql(MYSQL *myfd)
63 {
64 mysql_close(myfd);
65 }
insert_cgi.cpp程式碼
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "comm.h"
5
6 int main()
7 {
8 char arg[1024];
9 if( getenv("METHOD") ){
10 if(strcasecmp(getenv("METHOD"), "GET") == 0){
11 strcpy(arg, getenv("QUERY_STRING"));
12 }else{
13 char c;
14 int i = 0;
15 int len = atoi(getenv("CONTENT_LENGTH"));
16 for(; i < len; i++ ){
17 read(0, &c, 1);
18 arg[i] = c;
19 }
20 arg[i] = 0;
21 }
22 }
23
24 strtok(arg, "=&");
25 std::string name = strtok(NULL, "=&");
26 strtok(NULL, "=&");
27 std::string sex = strtok(NULL, "=&");
28 strtok(NULL, "=&");
29 std::string hobby = strtok(NULL, "=&");
30
31 MYSQL *myfd = connectMysql();
32 insertMysql(myfd, name,sex, hobby);
33 closeMysql(myfd);
34 }
select_cgi.cpp程式碼
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include "comm.h"
5
6 int main()
7 {
8 char arg[1024];
9 if(getenv("METHOD")){
10 if(strcasecmp(getenv("METHOD"),"GET") == 0){
11 strcpy(arg,getenv("QUERY_STRING"));
12 }
13 else{
14 char c;
15 int i = 0;
16 int len = atoi(getenv("CONTENT_LENGTH"));
17 for(; i < len; i++){
18 read(0,&c,1);
19 arg[i] = c;
20 }
21 arg[i] = 0;
22 }
23 }
24
25 MYSQL *myfd = connectMysql();
26 selectMysql(myfd);
27 closeMysql(myfd);
28 }
Makefile檔案
1 CURR_PATH=$(shell pwd)
2 LIB=$(CURR_PATH)/../lib
3 INCLUDE=-I$(LIB)/include
4 MYSQL_LIB=-L$(LIB)/lib -lmysqlclient
5 cc=g++
6
7
8 .PHONY:all
9 all:insert_cgi select_cgi
10
11 insert_cgi:insert_cgi.cpp comm.cpp
12 g++ -o [email protected] $^ $(INCLUDE) $(MYSQL_LIB)
13 select_cgi:select_cgi.cpp comm.cpp
14 g++ -o [email protected] $^ $(INCLUDE) $(MYSQL_LIB)
15
16 .PHONY:clean
17 clean:
18 rm -f insert_cgi select_cgi
wwwroot目錄中的檔案 404.html
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml 1-transitional.dtd">
2
3 <html xmlns="http://www.w3.org/1999/xhtml">
4
5 <head>
6
7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8
9 <title>404-Sorry!The Page Is Not Exist</title>
10
11 <style type="text/css">
12
13 .head404{ width:580px; height:234px; margin:50px auto 0 auto; background:url(https://www.baidu.com/Pu blic/images/head404.png) no-repeat; }
14
15 .txtbg404{ width:499px; height:169px; margin:10px auto 0 auto; background:url(https://www.baidu.com/P ublic/images/txtbg404.png) no-repeat;}
16
17 .txtbg404 .txtbox{ width:390px; position:relative; top:30px; left:60px;color:#eee; font-size:13px;}
18
19 .txtbg404 .txtbox p {margin:5px 0; line-height:18px;}
20
21 .txtbg404 .txtbox .paddingbox { padding-top:15px;}
22
23 .txtbg404 .txtbox p a { color:#eee; text-decoration:none;}
24
25 .txtbg404 .txtbox p a:hover { color:#FC9D1D; text-decoration:underline;}
26
27 </style>
28
29 </head>
30
31
32
33 <body bgcolor="#494949">
34
35 <div class="head404"></div>
36
37 <div class="txtbg404">
38
39 <div class="txtbox">
40
41 <p>Sorry,The Page Is Not Exist!</p>
42
43 <p class="paddingbox">Please click on the link below to continue browsing the web page.
44 </p>
45
46 <p> <a style="cursor:pointer" onclick="history.back()">Previous Page</a></p>
47
48 <p> <a href="https://www.baidu.com">back home</a></p>
49
50 </div>
51
52 </div>
53
54 </body>
55
56 </html>
index.html
1 CTYPE html>
2 <html>
3 <head lang="en">
4 <meta charset="UTF-8">
5 <title>background-position魚遊動程式碼 - 原始碼之家</title>
6 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale =1.0, minimum-scale=1.0">
8 <style>
9 body{
10 background-color: lightblue;
11 overflow:hidden;
12 }
13 .fishbox{
14 width: 200px;
15 height: 200px;
16 /*background-color: rgba(0,0,0,0.4);*/
17 position: absolute;
18 left:0;
19 top:0;
20 animation: run 20s linear infinite;
21 animation-direction: normal;
22 }
23 .fish{
24 width: 174px;
25 height: 126px;
26 position: absolute;
27 left:35%;
28 top:0;
29 background:url(image/fish8.png) no-repeat left top;
30 /*transition:all 0.8s steps(8) ;*/
31 animation:active1 0.8s steps(8) infinite;
32
33 }
34
35 .sharkbox{
36 width: 509px;
37 height: 270px;
38 animation: run2 20s linear infinite;
39 }
40 .shark{
41 width: 509px;
42 height: 270px;
43 background:url(image/shark1.png) no-repeat;
44 animation: active2 1s steps(8) infinite;
45 }
46
47
48
49
50 @keyframes active1 {
51 0%{
52
53 }
54 100%{
55 background-position: left -1008px;
56 }
57 }
58
59 @keyframes active2 {
60 0%{
61
62 }
63 100%{
64 background-position: left -2160px;
65 }
66 }
67
68 @keyframes run {
69 0%{
70
71 }
72
73 15%{
74 transform:translate(600px,0px) rotate(45deg) ;
75 }
76
77 50%{
78 transform:translate(600px,350px) rotate(135deg) ;
79 }
80
81 75%{
82 transform:translate(50px,300px) rotate(235deg);
83 }
84
85
86 100%{
87 transform:translate(0px,0px) rotate(360deg) ;
88 }
89 }
90
91 @keyframes run2{
92 0%{
93 transform:translateX(-600px);
94 }
95
96 100%{
97 transform:translateX(2000px);
98 }
99 }
100 </style>
101 </head>
102 <body>
103
104 <div class="fishbox">
105 <div class="fish"></div>
106 </div>
107 <div class="sharkbox">
108 <div class="shark"></div>
109 </div>
110
111 <div style="text-align:center;">
112 </div>
113
114 <img border="0" src="/image/cat.jpeg" alt="Pulpit rock" width="304" height="228">
115
116 <form action="/cgi/insert_cgi" method="GET">
117 name:
118 <input type="text" name="name" value="">
119 <br>
120 sex:
121 <input type="text" name="sex" value="man">
122 <br>
123 hobby:
124 <input type="text" name="hobby" value="">
125 <br>
126 <input type="submit" value="Submit">
127 </form>
128 <a href="http://192.168.43.243:9999/cgi/select_cgi">select</a>
129
130 <!--<form action="/cgi/math_cgi" method="GET">
131 x:
132 <input type="text" name="x" value="100">
133 <br>
134 y:
135 <input type="text" name="y" value="200">
136 <br><br>
137 <input type="submit" value="Submit">
138 </form> -->
139
140 </body>
141 </html>
142
image資料夾中存放的是圖片 wwwroot目錄裡面的cgi目錄 math_cgi.c程式碼
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4
5 void math_op(char *buf)
6 {
7 int x,y;
8 sscanf(buf,"x=%d&y=%d",&x,&y);
9 printf("<html>\n");
10
11 printf("<h1>%d + %d = %d</h1>", x, y, x + y);
12 printf("<h1>%d - %d = %d</h1>", x, y, x - y);
13 printf("<h1>%d * %d = %d</h1>", x, y, x * y);
14 if(y == 0){
15 printf("<h1>%d / %d = error</h1>", x, y);
16 printf("<h1>%d % %d = error</h1>", x, y);
17 }else{
18 printf("<h1>%d / %d = %d</h1>", x, y, x / y);
19 printf("<h1>%d % %d = %d</h1>", x, y, x % y);
20 }
21 printf("</html>\n");
22 }
23
24 int main()
25 {
26 char buf[1024];
27 if(getenv("METHOD")){
28 strcpy(buf, getenv("QUERY_STRING"));
29 }else{
30 int len = atoi(getenv("CONTENT_LENGTH"));
31 int i = 0;
32 for(; i < len; i++){
33 read(0, buf+i, 1);
34 }
35 buf[i] = '\0';
36 }
37
38 printf("arg: %s\n", buf);
39 math_op(buf);
40 }
test_cgi.c程式碼
#include <stdio.h>
int main()
{
printf("<html> <hl>hello cgi</hl> </html>\r\n");
}
Makefile檔案
1 .PHONY:all
2 all:math_cgi test_cgi
3
4 math_cgi:math_cgi.c
5 gcc -o [email protected] $^
6 test_cgi:test_cgi.c
7 gcc -o [email protected] $^
8
9 .PHONY:clean
10 clean:
11 rm -f math_cgi test_cgi