首先,本人剛剛開始開源代碼精讀,寫的不對的地方,大家輕拍,一起進步。本文是對Tinyhttpd的一次精讀,大家每天都在用着http服務,很多人也一直活躍在上層,使用IIS、Apache等,大家是否想看看http服務器大概是怎么運作的,通過一個500多行的源碼加上完整的注釋,和大家逛一逛http服務器。Tinyhttpd真的非常適合閱讀尤其是剛入門的,清晰的代碼,簡單的makefile...其實有很多分析tinyghttpd的,這邊抱着人家寫的是人家,自己寫的才是自己的態度,寫的盡量詳細,盡量簡單,很多都寫在代碼注釋里面,也把學習中的一些坑翻出來和大家一起讀讀,吸取將近20年前的大神通過500行代碼帶給我們的財富。不管你是寫c的,或者C#,js亦或java,只要用到http的都歡迎讀讀。
大家看后如果覺得我有寫的不對的地方,歡迎指出~~
附上注解代碼的github地址:https://github.com/nengm/Tinyhttpd
1.首先是一張圖 全解了Tinyhttp是如何運作的,我覺得圖說明比我用文字描述的要清晰,語言功底不太給力。
2. 主要函數簡略說明,代碼中進行了詳細注釋。
main 主函數
startup 綁定監聽套接字
accept_request 每次收到請求,創建一個線程來處理接受到的請求
serve_file 接讀取文件返回給請求的http客戶端
execute_cgi 執行cgi文件
3.注意點
注意點1:
index.html必須沒有執行權限,否則看不到內容,並且會產生Program received signal SIGPIPE, Broken pipe,因為程序中如果有可執行權限會當cgi腳本處理。所以假如html有執行權限先把它去除了,chmod 600 index.html
color.cgi、date.cgi必須要有執行權限。
注意點2:
color.cgi是用perl寫的,相信大家很少接觸了。所以可以引用網上一個簡單的例子,換成一個shell寫的cgi測試
#!/bin/bash
echo "Content-Type: text/html"
echo
echo "<HTML><BODY>"
echo "<CENTER>Today is:</CENTER>"
echo "<CENTER><B>"
date
echo "</B></CENTER>"
echo "</BODY></HTML>"
4.操作
1.執行make,生成成功,./httpd啟動成功。
2.如果在當前linux下的firefox下執行。直接在瀏覽器中輸入
3.在windows中測試,
4.測試執行自帶的perl寫的cgi腳本
5.進入index2.html頁面,測試shell寫的cgi腳本。
5.代碼
1 /* J. David's webserver */ 2 /* This is a simple webserver. 3 * Created November 1999 by J. David Blackstone. 4 * CSE 4344 (Network concepts), Prof. Zeigler 5 * University of Texas at Arlington 6 */ 7 /* This program compiles for Sparc Solaris 2.6. 8 * To compile for Linux: 9 * 1) Comment out the #include <pthread.h> line. 10 * 2) Comment out the line that defines the variable newthread. 11 * 3) Comment out the two lines that run pthread_create(). 12 * 4) Uncomment the line that runs accept_request(). 13 * 5) Remove -lsocket from the Makefile. 14 */ 15 #include <stdio.h> 16 #include <sys/socket.h> 17 #include <sys/types.h> 18 #include <netinet/in.h> 19 #include <arpa/inet.h> 20 #include <unistd.h> 21 #include <ctype.h> 22 #include <strings.h> 23 #include <string.h> 24 #include <sys/stat.h> 25 #include <pthread.h> 26 #include <sys/wait.h> 27 #include <stdlib.h> 28 29 //宏定義,是否是空格 30 #define ISspace(x) isspace((int)(x)) 31 32 #define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" 33 34 //每次收到請求,創建一個線程來處理接受到的請求 35 //把client_sock轉成地址作為參數傳入pthread_create 36 void accept_request(void *arg); 37 38 //錯誤請求 39 void bad_request(int); 40 41 //讀取文件 42 void cat(int, FILE *); 43 44 //無法執行 45 void cannot_execute(int); 46 47 //錯誤輸出 48 void error_die(const char *); 49 50 //執行cig腳本 51 void execute_cgi(int, const char *, const char *, const char *); 52 53 //得到一行數據,只要發現c為\n,就認為是一行結束,如果讀到\r,再用MSG_PEEK的方式讀入一個字符,如果是\n,從socket用讀出 54 //如果是下個字符則不處理,將c置為\n,結束。如果讀到的數據為0中斷,或者小於0,也視為結束,c置為\n 55 int get_line(int, char *, int); 56 57 //返回http頭 58 void headers(int, const char *); 59 60 //沒有發現文件 61 void not_found(int); 62 63 //如果不是CGI文件,直接讀取文件返回給請求的http客戶端 64 void serve_file(int, const char *); 65 66 //開啟tcp連接,綁定端口等操作 67 int startup(u_short *); 68 69 //如果不是Get或者Post,就報方法沒有實現 70 void unimplemented(int); 71 72 // Http請求,后續主要是處理這個頭 73 // 74 // GET / HTTP/1.1 75 // Host: 192.168.0.23:47310 76 // Connection: keep-alive 77 // Upgrade-Insecure-Requests: 1 78 // User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 79 // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*; q = 0.8 80 // Accept - Encoding: gzip, deflate, sdch 81 // Accept - Language : zh - CN, zh; q = 0.8 82 // Cookie: __guid = 179317988.1576506943281708800.1510107225903.8862; monitor_count = 5 83 // 84 85 // POST / color1.cgi HTTP / 1.1 86 // Host: 192.168.0.23 : 47310 87 // Connection : keep - alive 88 // Content - Length : 10 89 // Cache - Control : max - age = 0 90 // Origin : http ://192.168.0.23:40786 91 // Upgrade - Insecure - Requests : 1 92 // User - Agent : Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 55.0.2883.87 Safari / 537.36 93 // Content - Type : application / x - www - form - urlencoded 94 // Accept : text / html, application / xhtml + xml, application / xml; q = 0.9, image / webp, */*;q=0.8 95 // Referer: http://192.168.0.23:47310/ 96 // Accept-Encoding: gzip, deflate 97 // Accept-Language: zh-CN,zh;q=0.8 98 // Cookie: __guid=179317988.1576506943281708800.1510107225903.8862; monitor_count=281 99 // Form Data 100 // color=gray 101 102 /**********************************************************************/ 103 /* A request has caused a call to accept() on the server port to 104 * return. Process the request appropriately. 105 * Parameters: the socket connected to the client */ 106 /**********************************************************************/ 107 void accept_request(void *arg) 108 { 109 //socket 110 int client = (intptr_t)arg; 111 char buf[1024]; 112 int numchars; 113 char method[255]; 114 char url[255]; 115 char path[512]; 116 size_t i, j; 117 struct stat st; 118 int cgi = 0; /* becomes true if server decides this is a CGI 119 * program */ 120 char *query_string = NULL; 121 //根據上面的Get請求,可以看到這邊就是取第一行 122 //這邊都是在處理第一條http信息 123 //"GET / HTTP/1.1\n" 124 numchars = get_line(client, buf, sizeof(buf)); 125 i = 0; j = 0; 126 127 //第一行字符串提取Get 128 while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) 129 { 130 method[i] = buf[j]; 131 i++; j++; 132 } 133 //結束 134 method[i] = '\0'; 135 136 //判斷是Get還是Post 137 if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) 138 { 139 unimplemented(client); 140 return; 141 } 142 143 //如果是POST,cgi置為1 144 if (strcasecmp(method, "POST") == 0) 145 cgi = 1; 146 147 i = 0; 148 //跳過空格 149 while (ISspace(buf[j]) && (j < sizeof(buf))) 150 j++; 151 152 //得到 "/" 注意:如果你的http的網址為http://192.168.0.23:47310/index.html 153 // 那么你得到的第一條http信息為GET /index.html HTTP/1.1,那么 154 // 解析得到的就是/index.html 155 while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) 156 { 157 url[i] = buf[j]; 158 i++; j++; 159 } 160 url[i] = '\0'; 161 162 //判斷Get請求 163 if (strcasecmp(method, "GET") == 0) 164 { 165 query_string = url; 166 while ((*query_string != '?') && (*query_string != '\0')) 167 query_string++; 168 if (*query_string == '?') 169 { 170 cgi = 1; 171 *query_string = '\0'; 172 query_string++; 173 } 174 } 175 176 //路徑 177 sprintf(path, "htdocs%s", url); 178 179 //默認地址,解析到的路徑如果為/,則自動加上index.html 180 if (path[strlen(path) - 1] == '/') 181 strcat(path, "index.html"); 182 183 //獲得文件信息 184 if (stat(path, &st) == -1) { 185 //把所有http信息讀出然后丟棄 186 while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 187 numchars = get_line(client, buf, sizeof(buf)); 188 189 //沒有找到 190 not_found(client); 191 } 192 else 193 { 194 if ((st.st_mode & S_IFMT) == S_IFDIR) 195 strcat(path, "/index.html"); 196 //如果你的文件默認是有執行權限的,自動解析成cgi程序,如果有執行權限但是不能執行,會接受到報錯信號 197 if ((st.st_mode & S_IXUSR) || 198 (st.st_mode & S_IXGRP) || 199 (st.st_mode & S_IXOTH) ) 200 cgi = 1; 201 if (!cgi) 202 //接讀取文件返回給請求的http客戶端 203 serve_file(client, path); 204 else 205 //執行cgi文件 206 execute_cgi(client, path, method, query_string); 207 } 208 //執行完畢關閉socket 209 close(client); 210 } 211 212 /**********************************************************************/ 213 /* Inform the client that a request it has made has a problem. 214 * Parameters: client socket */ 215 /**********************************************************************/ 216 void bad_request(int client) 217 { 218 char buf[1024]; 219 220 sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); 221 send(client, buf, sizeof(buf), 0); 222 sprintf(buf, "Content-type: text/html\r\n"); 223 send(client, buf, sizeof(buf), 0); 224 sprintf(buf, "\r\n"); 225 send(client, buf, sizeof(buf), 0); 226 sprintf(buf, "<P>Your browser sent a bad request, "); 227 send(client, buf, sizeof(buf), 0); 228 sprintf(buf, "such as a POST without a Content-Length.\r\n"); 229 send(client, buf, sizeof(buf), 0); 230 } 231 232 /**********************************************************************/ 233 /* Put the entire contents of a file out on a socket. This function 234 * is named after the UNIX "cat" command, because it might have been 235 * easier just to do something like pipe, fork, and exec("cat"). 236 * Parameters: the client socket descriptor 237 * FILE pointer for the file to cat */ 238 /**********************************************************************/ 239 240 //得到文件內容,發送 241 void cat(int client, FILE *resource) 242 { 243 char buf[1024]; 244 245 fgets(buf, sizeof(buf), resource); 246 //循環讀 247 while (!feof(resource)) 248 { 249 send(client, buf, strlen(buf), 0); 250 fgets(buf, sizeof(buf), resource); 251 } 252 } 253 254 /**********************************************************************/ 255 /* Inform the client that a CGI script could not be executed. 256 * Parameter: the client socket descriptor. */ 257 /**********************************************************************/ 258 void cannot_execute(int client) 259 { 260 char buf[1024]; 261 262 sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); 263 send(client, buf, strlen(buf), 0); 264 sprintf(buf, "Content-type: text/html\r\n"); 265 send(client, buf, strlen(buf), 0); 266 sprintf(buf, "\r\n"); 267 send(client, buf, strlen(buf), 0); 268 sprintf(buf, "<P>Error prohibited CGI execution.\r\n"); 269 send(client, buf, strlen(buf), 0); 270 } 271 272 /**********************************************************************/ 273 /* Print out an error message with perror() (for system errors; based 274 * on value of errno, which indicates system call errors) and exit the 275 * program indicating an error. */ 276 /**********************************************************************/ 277 void error_die(const char *sc) 278 { 279 perror(sc); 280 exit(1); 281 } 282 283 /**********************************************************************/ 284 /* Execute a CGI script. Will need to set environment variables as 285 * appropriate. 286 * Parameters: client socket descriptor 287 * path to the CGI script */ 288 /**********************************************************************/ 289 void execute_cgi(int client, const char *path, 290 const char *method, const char *query_string) 291 { 292 //緩沖區 293 char buf[1024]; 294 295 //2根管道 296 int cgi_output[2]; 297 int cgi_input[2]; 298 299 //進程pid和狀態 300 pid_t pid; 301 int status; 302 303 int i; 304 char c; 305 306 //讀取的字符數 307 int numchars = 1; 308 309 //http的content_length 310 int content_length = -1; 311 312 //默認字符 313 buf[0] = 'A'; buf[1] = '\0'; 314 315 //忽略大小寫比較字符串 316 if (strcasecmp(method, "GET") == 0) 317 //讀取數據,把整個header都讀掉,以為Get寫死了直接讀取index.html,沒有必要分析余下的http信息了 318 while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 319 numchars = get_line(client, buf, sizeof(buf)); 320 else /* POST */ 321 { 322 numchars = get_line(client, buf, sizeof(buf)); 323 while ((numchars > 0) && strcmp("\n", buf)) 324 { 325 //如果是POST請求,就需要得到Content-Length,Content-Length:這個字符串一共長為15位,所以 326 //取出頭部一句后,將第16位設置結束符,進行比較 327 //第16位置為結束 328 buf[15] = '\0'; 329 if (strcasecmp(buf, "Content-Length:") == 0) 330 //內存從第17位開始就是長度,將17位開始的所有字符串轉成整數就是content_length 331 content_length = atoi(&(buf[16])); 332 numchars = get_line(client, buf, sizeof(buf)); 333 } 334 if (content_length == -1) { 335 bad_request(client); 336 return; 337 } 338 } 339 340 sprintf(buf, "HTTP/1.0 200 OK\r\n"); 341 send(client, buf, strlen(buf), 0); 342 //建立output管道 343 if (pipe(cgi_output) < 0) { 344 cannot_execute(client); 345 return; 346 } 347 348 //建立input管道 349 if (pipe(cgi_input) < 0) { 350 cannot_execute(client); 351 return; 352 } 353 // fork后管道都復制了一份,都是一樣的 354 // 子進程關閉2個無用的端口,避免浪費 355 // ×<------------------------->1 output 356 // 0<-------------------------->× input 357 358 // 父進程關閉2個無用的端口,避免浪費 359 // 0<-------------------------->× output 360 // ×<------------------------->1 input 361 // 此時父子進程已經可以通信 362 363 364 //fork進程,子進程用於執行CGI 365 //父進程用於收數據以及發送子進程處理的回復數據 366 if ( (pid = fork()) < 0 ) { 367 cannot_execute(client); 368 return; 369 } 370 if (pid == 0) /* child: CGI script */ 371 { 372 char meth_env[255]; 373 char query_env[255]; 374 char length_env[255]; 375 376 //子進程輸出重定向到output管道的1端 377 dup2(cgi_output[1], 1); 378 //子進程輸入重定向到input管道的0端 379 dup2(cgi_input[0], 0); 380 381 //關閉無用管道口 382 close(cgi_output[0]); 383 close(cgi_input[1]); 384 385 //CGI環境變量 386 sprintf(meth_env, "REQUEST_METHOD=%s", method); 387 putenv(meth_env); 388 if (strcasecmp(method, "GET") == 0) { 389 sprintf(query_env, "QUERY_STRING=%s", query_string); 390 putenv(query_env); 391 } 392 else { /* POST */ 393 sprintf(length_env, "CONTENT_LENGTH=%d", content_length); 394 putenv(length_env); 395 } 396 //替換執行path 397 execl(path, path, NULL); 398 //int m = execl(path, path, NULL); 399 //如果path有問題,例如將html網頁改成可執行的,但是執行后m為-1 400 //退出子進程,管道被破壞,但是父進程還在往里面寫東西,觸發Program received signal SIGPIPE, Broken pipe. 401 exit(0); 402 } else { /* parent */ 403 404 //關閉無用管道口 405 close(cgi_output[1]); 406 close(cgi_input[0]); 407 if (strcasecmp(method, "POST") == 0) 408 for (i = 0; i < content_length; i++) { 409 //得到post請求數據,寫到input管道中,供子進程使用 410 recv(client, &c, 1, 0); 411 write(cgi_input[1], &c, 1); 412 } 413 //從output管道讀到子進程處理后的信息,然后send出去 414 while (read(cgi_output[0], &c, 1) > 0) 415 send(client, &c, 1, 0); 416 417 //完成操作后關閉管道 418 close(cgi_output[0]); 419 close(cgi_input[1]); 420 421 //等待子進程返回 422 waitpid(pid, &status, 0); 423 424 } 425 } 426 427 /**********************************************************************/ 428 /* Get a line from a socket, whether the line ends in a newline, 429 * carriage return, or a CRLF combination. Terminates the string read 430 * with a null character. If no newline indicator is found before the 431 * end of the buffer, the string is terminated with a null. If any of 432 * the above three line terminators is read, the last character of the 433 * string will be a linefeed and the string will be terminated with a 434 * null character. 435 * Parameters: the socket descriptor 436 * the buffer to save the data in 437 * the size of the buffer 438 * Returns: the number of bytes stored (excluding null) */ 439 /**********************************************************************/ 440 441 //得到一行數據,只要發現c為\n,就認為是一行結束,如果讀到\r,再用MSG_PEEK的方式讀入一個字符,如果是\n,從socket用讀出 442 //如果是下個字符則不處理,將c置為\n,結束。如果讀到的數據為0中斷,或者小於0,也視為結束,c置為\n 443 int get_line(int sock, char *buf, int size) 444 { 445 int i = 0; 446 char c = '\0'; 447 int n; 448 449 while ((i < size - 1) && (c != '\n')) 450 { 451 n = recv(sock, &c, 1, 0); 452 /* DEBUG printf("%02X\n", c); */ 453 if (n > 0) 454 { 455 if (c == '\r') 456 { 457 //偷窺一個字節,如果是\n就讀走 458 n = recv(sock, &c, 1, MSG_PEEK); 459 /* DEBUG printf("%02X\n", c); */ 460 if ((n > 0) && (c == '\n')) 461 recv(sock, &c, 1, 0); 462 else 463 //不是\n(讀到下一行的字符)或者沒讀到,置c為\n 跳出循環,完成一行讀取 464 c = '\n'; 465 } 466 buf[i] = c; 467 i++; 468 } 469 else 470 c = '\n'; 471 } 472 buf[i] = '\0'; 473 474 return(i); 475 } 476 477 /**********************************************************************/ 478 /* Return the informational HTTP headers about a file. */ 479 /* Parameters: the socket to print the headers on 480 * the name of the file */ 481 /**********************************************************************/ 482 483 //加入http的headers 484 void headers(int client, const char *filename) 485 { 486 char buf[1024]; 487 (void)filename; /* could use filename to determine file type */ 488 489 strcpy(buf, "HTTP/1.0 200 OK\r\n"); 490 send(client, buf, strlen(buf), 0); 491 strcpy(buf, SERVER_STRING); 492 send(client, buf, strlen(buf), 0); 493 sprintf(buf, "Content-Type: text/html\r\n"); 494 send(client, buf, strlen(buf), 0); 495 strcpy(buf, "\r\n"); 496 send(client, buf, strlen(buf), 0); 497 } 498 499 /**********************************************************************/ 500 /* Give a client a 404 not found status message. */ 501 /**********************************************************************/ 502 503 //如果資源沒有找到得返回給客戶端下面的信息 504 void not_found(int client) 505 { 506 char buf[1024]; 507 508 sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); 509 send(client, buf, strlen(buf), 0); 510 sprintf(buf, SERVER_STRING); 511 send(client, buf, strlen(buf), 0); 512 sprintf(buf, "Content-Type: text/html\r\n"); 513 send(client, buf, strlen(buf), 0); 514 sprintf(buf, "\r\n"); 515 send(client, buf, strlen(buf), 0); 516 sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n"); 517 send(client, buf, strlen(buf), 0); 518 sprintf(buf, "<BODY><P>The server could not fulfill\r\n"); 519 send(client, buf, strlen(buf), 0); 520 sprintf(buf, "your request because the resource specified\r\n"); 521 send(client, buf, strlen(buf), 0); 522 sprintf(buf, "is unavailable or nonexistent.\r\n"); 523 send(client, buf, strlen(buf), 0); 524 sprintf(buf, "</BODY></HTML>\r\n"); 525 send(client, buf, strlen(buf), 0); 526 } 527 528 /**********************************************************************/ 529 /* Send a regular file to the client. Use headers, and report 530 * errors to client if they occur. 531 * Parameters: a pointer to a file structure produced from the socket 532 * file descriptor 533 * the name of the file to serve */ 534 /**********************************************************************/ 535 536 //如果不是CGI文件,直接讀取文件返回給請求的http客戶端 537 void serve_file(int client, const char *filename) 538 { 539 FILE *resource = NULL; 540 int numchars = 1; 541 char buf[1024]; 542 543 //默認字符 544 buf[0] = 'A'; buf[1] = '\0'; 545 while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 546 numchars = get_line(client, buf, sizeof(buf)); 547 548 resource = fopen(filename, "r"); 549 if (resource == NULL) 550 not_found(client); 551 else 552 { 553 headers(client, filename); 554 cat(client, resource); 555 } 556 fclose(resource); 557 } 558 559 /**********************************************************************/ 560 /* This function starts the process of listening for web connections 561 * on a specified port. If the port is 0, then dynamically allocate a 562 * port and modify the original port variable to reflect the actual 563 * port. 564 * Parameters: pointer to variable containing the port to connect on 565 * Returns: the socket */ 566 /**********************************************************************/ 567 int startup(u_short *port) 568 { 569 int httpd = 0; 570 struct sockaddr_in name; 571 572 httpd = socket(PF_INET, SOCK_STREAM, 0); 573 if (httpd == -1) 574 error_die("socket"); 575 memset(&name, 0, sizeof(name)); 576 name.sin_family = AF_INET; 577 name.sin_port = htons(*port); 578 name.sin_addr.s_addr = htonl(INADDR_ANY); 579 //綁定socket 580 if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) 581 error_die("bind"); 582 //如果端口沒有設置,提供個隨機端口 583 if (*port == 0) /* if dynamically allocating a port */ 584 { 585 socklen_t namelen = sizeof(name); 586 if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) 587 error_die("getsockname"); 588 *port = ntohs(name.sin_port); 589 } 590 //監聽 591 if (listen(httpd, 5) < 0) 592 error_die("listen"); 593 return(httpd); 594 } 595 596 /**********************************************************************/ 597 /* Inform the client that the requested web method has not been 598 * implemented. 599 * Parameter: the client socket */ 600 /**********************************************************************/ 601 602 //如果方法沒有實現,就返回此信息 603 void unimplemented(int client) 604 { 605 char buf[1024]; 606 607 sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); 608 send(client, buf, strlen(buf), 0); 609 sprintf(buf, SERVER_STRING); 610 send(client, buf, strlen(buf), 0); 611 sprintf(buf, "Content-Type: text/html\r\n"); 612 send(client, buf, strlen(buf), 0); 613 sprintf(buf, "\r\n"); 614 send(client, buf, strlen(buf), 0); 615 sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); 616 send(client, buf, strlen(buf), 0); 617 sprintf(buf, "</TITLE></HEAD>\r\n"); 618 send(client, buf, strlen(buf), 0); 619 sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); 620 send(client, buf, strlen(buf), 0); 621 sprintf(buf, "</BODY></HTML>\r\n"); 622 send(client, buf, strlen(buf), 0); 623 } 624 625 /**********************************************************************/ 626 627 int main(void) 628 { 629 int server_sock = -1; 630 u_short port = 0; 631 int client_sock = -1; 632 struct sockaddr_in client_name; 633 634 //這邊要為socklen_t類型 635 socklen_t client_name_len = sizeof(client_name); 636 pthread_t newthread; 637 638 server_sock = startup(&port); 639 printf("httpd running on port %d\n", port); 640 641 while (1) 642 { 643 //接受請求,函數原型 644 //#include <sys/types.h> 645 //#include <sys/socket.h> 646 //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 647 client_sock = accept(server_sock, 648 (struct sockaddr *)&client_name, 649 &client_name_len); 650 if (client_sock == -1) 651 error_die("accept"); 652 /* accept_request(client_sock); */ 653 654 //每次收到請求,創建一個線程來處理接受到的請求 655 //把client_sock轉成地址作為參數傳入pthread_create 656 if (pthread_create(&newthread, NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0) 657 perror("pthread_create"); 658 } 659 660 close(server_sock); 661 662 return(0); 663 }