Tinyhttpd精讀解析


       首先,本人剛剛開始開源代碼精讀,寫的不對的地方,大家輕拍,一起進步。本文是對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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM