├── htdocs ├── demo.html ├── file │ └── hehe.html ├── get.php ├── index.html ├── README ├── color.cgi └── check.cgi ├── httpd ├── README.md ├── Makefile ├── simpleclient.c └── httpd.c /htdocs/demo.html: -------------------------------------------------------------------------------- 1 | 你好 2 | -------------------------------------------------------------------------------- /htdocs/file/hehe.html: -------------------------------------------------------------------------------- 1 | 呵呵 2 | -------------------------------------------------------------------------------- /httpd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xc9e36/TinyHTTPd/HEAD/httpd -------------------------------------------------------------------------------- /htdocs/get.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 2 | Index 3 | 4 |

Welcome to J. David's webserver. 5 |

CGI demo 6 |
7 | Enter a color: 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /htdocs/README: -------------------------------------------------------------------------------- 1 | These are sample CGI scripts and webpages for tinyhttpd. They can 2 | be redistributed under the terms of the GPL. 3 | 4 | The most impressive demonstration I gave of tinyhttpd to my 5 | professor and my classmates was to load color.cgi with a value of 6 | "chartreuse." :) It's actually a very simple script, guys. 7 | 8 | jdb 9 | -------------------------------------------------------------------------------- /htdocs/color.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -Tw 2 | 3 | use strict; 4 | use CGI; 5 | 6 | my($cgi) = new CGI; 7 | 8 | print $cgi->header; 9 | my($color) = "blue"; 10 | $color = $cgi->param('color') if defined $cgi->param('color'); 11 | 12 | print $cgi->start_html(-title => uc($color), 13 | -BGCOLOR => $color); 14 | print $cgi->h1("This is $color"); 15 | print $cgi->end_html; 16 | -------------------------------------------------------------------------------- /htdocs/check.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -Tw 2 | 3 | use strict; 4 | use CGI; 5 | 6 | my($cgi) = new CGI; 7 | 8 | print $cgi->header('text/html'); 9 | print $cgi->start_html(-title => "Example CGI script", 10 | -BGCOLOR => 'red'); 11 | print $cgi->h1("CGI Example"); 12 | print $cgi->p, "This is an example of CGI\n"; 13 | print $cgi->p, "Parameters given to this script:\n"; 14 | print ""; 20 | print $cgi->end_html, "\n"; 21 | -------------------------------------------------------------------------------- /simpleclient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | int sockfd; 11 | int len; 12 | struct sockaddr_in address; 13 | int result; 14 | char ch = 'A'; 15 | 16 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 17 | address.sin_family = AF_INET; 18 | address.sin_addr.s_addr = inet_addr("127.0.0.1"); 19 | address.sin_port = htons(4000); 20 | len = sizeof(address); 21 | result = connect(sockfd, (struct sockaddr *)&address, len); 22 | 23 | if (result == -1) 24 | { 25 | perror("oops: client1"); 26 | return 0; 27 | } 28 | write(sockfd, &ch, 1); 29 | read(sockfd, &ch, 1); 30 | printf("char from server = %c\n", ch); 31 | close(sockfd); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /httpd.c: -------------------------------------------------------------------------------- 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 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 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define ISspace(x) isspace((int)(x)) 31 | 32 | #define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" 33 | #define STDIN 0 34 | #define STDOUT 1 35 | #define STDERR 2 36 | 37 | void *accept_request(void *); //接受客户端请求 38 | void bad_request(int); //400错误, 请求方法出错 39 | void cat(int, FILE *); //读取文件发送给客户端 40 | void cannot_execute(int); //通知客户端执行cgi脚本失败 41 | void error_die(const char *); //错误处理 42 | void execute_cgi(int, const char *, const char *, const char *); //调用exec函数簇执行cgi脚本 43 | int get_line(int, char *, int); //读取套接字获取一行数据, 返回字符个数 44 | void headers(int, const char *); //发送头信息 45 | void not_found(int); //404错误 46 | void serve_file(int, const char *); //返回静态页面 47 | int startup(u_short *); //套接字创建, 绑定, 监听 48 | void unimplemented(int); //请求方法未实现 49 | 50 | /**********************************************************************/ 51 | /* A request has caused a call to accept() on the server port to 52 | * return. Process the request appropriately. 53 | * Parameters: the socket connected to the client 54 | * 处理每个客户端连接 55 | * */ 56 | /**********************************************************************/ 57 | void *accept_request(void *arg) 58 | { 59 | int client = *(int*)arg; 60 | char buf[1024]; 61 | size_t numchars; 62 | char method[255]; 63 | char url[255]; 64 | char path[512]; 65 | size_t i, j; 66 | struct stat st; 67 | int cgi = 0; /* becomes true if server decides this is a CGI 68 | * program */ 69 | char *query_string = NULL; 70 | 71 | /* 获取请求行, 返回字节数 eg: GET /index.html HTTP/1.1 */ 72 | numchars = get_line(client, buf, sizeof(buf)); 73 | /* debug */ 74 | //printf("%s", buf); 75 | 76 | /* 获取请求方式, 保存在method中 GET或POST */ 77 | i = 0; j = 0; 78 | while (!ISspace(buf[i]) && (i < sizeof(method) - 1)) 79 | { 80 | method[i] = buf[i]; 81 | i++; 82 | } 83 | j=i; 84 | method[i] = '\0'; 85 | 86 | /* 只支持GET 和 POST 方法 */ 87 | if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) 88 | { 89 | unimplemented(client); 90 | return NULL; 91 | } 92 | 93 | /* 如果支持POST方法, 开启cgi */ 94 | if (strcasecmp(method, "POST") == 0) 95 | cgi = 1; 96 | 97 | i = 0; 98 | while (ISspace(buf[j]) && (j < numchars)) 99 | j++; 100 | while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars)) 101 | { 102 | url[i] = buf[j]; 103 | i++; j++; 104 | } 105 | /* 保存请求的url, url上的参数也会保存 */ 106 | url[i] = '\0'; 107 | 108 | //printf("%s\n", url); 109 | 110 | if (strcasecmp(method, "GET") == 0) 111 | { 112 | /* query_string 保存请求参数 index.php?r=param 问号后面的 r=param */ 113 | query_string = url; 114 | while ((*query_string != '?') && (*query_string != '\0')) 115 | query_string++; 116 | /* 如果有?表明是动态请求, 开启cgi */ 117 | if (*query_string == '?') 118 | { 119 | cgi = 1; 120 | *query_string = '\0'; 121 | query_string++; 122 | } 123 | } 124 | 125 | // printf("%s\n", query_string); 126 | 127 | /* 根目录在 htdocs 下, 默认访问当前请求下的index.html*/ 128 | sprintf(path, "htdocs%s", url); 129 | if (path[strlen(path) - 1] == '/') 130 | strcat(path, "index.html"); 131 | 132 | //printf("%s\n", path); 133 | /* 找到文件, 保存在结构体st中*/ 134 | if (stat(path, &st) == -1) { 135 | /* 文件未找到, 丢弃所有http请求头信息 */ 136 | while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 137 | numchars = get_line(client, buf, sizeof(buf)); 138 | /* 404 no found */ 139 | not_found(client); 140 | } 141 | else 142 | { 143 | 144 | //如果请求参数为目录, 自动打开index.html 145 | if ((st.st_mode & S_IFMT) == S_IFDIR) 146 | strcat(path, "/index.html"); 147 | 148 | //文件可执行 149 | if ((st.st_mode & S_IXUSR) || 150 | (st.st_mode & S_IXGRP) || 151 | (st.st_mode & S_IXOTH) ) 152 | cgi = 1; 153 | if (!cgi) 154 | /* 请求静态页面 */ 155 | serve_file(client, path); 156 | else 157 | /* 执行cgi 程序*/ 158 | execute_cgi(client, path, method, query_string); 159 | } 160 | 161 | close(client); 162 | return NULL; 163 | } 164 | 165 | /**********************************************************************/ 166 | /* Inform the client that a request it has made has a problem. 167 | * Parameters: client socket */ 168 | /**********************************************************************/ 169 | void bad_request(int client) 170 | { 171 | char buf[1024]; 172 | 173 | sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); 174 | send(client, buf, sizeof(buf), 0); 175 | sprintf(buf, "Content-type: text/html\r\n"); 176 | send(client, buf, sizeof(buf), 0); 177 | sprintf(buf, "\r\n"); 178 | send(client, buf, sizeof(buf), 0); 179 | sprintf(buf, "

Your browser sent a bad request, "); 180 | send(client, buf, sizeof(buf), 0); 181 | sprintf(buf, "such as a POST without a Content-Length.\r\n"); 182 | send(client, buf, sizeof(buf), 0); 183 | } 184 | 185 | /**********************************************************************/ 186 | /* Put the entire contents of a file out on a socket. This function 187 | * is named after the UNIX "cat" command, because it might have been 188 | * easier just to do something like pipe, fork, and exec("cat"). 189 | * Parameters: the client socket descriptor 190 | * FILE pointer for the file to cat */ 191 | /**********************************************************************/ 192 | void cat(int client, FILE *resource) 193 | { 194 | char buf[1024]; 195 | 196 | fgets(buf, sizeof(buf), resource); 197 | while (!feof(resource)) 198 | { 199 | send(client, buf, strlen(buf), 0); 200 | fgets(buf, sizeof(buf), resource); 201 | } 202 | } 203 | 204 | /**********************************************************************/ 205 | /* Inform the client that a CGI script could not be executed. 206 | * Parameter: the client socket descriptor. 207 | * 服务器500错误 208 | * */ 209 | /**********************************************************************/ 210 | void cannot_execute(int client) 211 | { 212 | char buf[1024]; 213 | 214 | sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); 215 | send(client, buf, strlen(buf), 0); 216 | sprintf(buf, "Content-type: text/html\r\n"); 217 | send(client, buf, strlen(buf), 0); 218 | sprintf(buf, "\r\n"); 219 | send(client, buf, strlen(buf), 0); 220 | sprintf(buf, "

Error prohibited CGI execution.\r\n"); 221 | send(client, buf, strlen(buf), 0); 222 | } 223 | 224 | /**********************************************************************/ 225 | /* Print out an error message with perror() (for system errors; based 226 | * on value of errno, which indicates system call errors) and exit the 227 | * program indicating an error. */ 228 | /**********************************************************************/ 229 | void error_die(const char *sc) 230 | { 231 | perror(sc); 232 | exit(1); 233 | } 234 | 235 | /**********************************************************************/ 236 | /* Execute a CGI script. Will need to set environment variables as 237 | * appropriate. 238 | * Parameters: client socket descriptor 239 | * path to the CGI script */ 240 | /**********************************************************************/ 241 | void execute_cgi(int client, const char *path, 242 | const char *method, const char *query_string) 243 | { 244 | char buf[1024]; 245 | int cgi_output[2]; 246 | int cgi_input[2]; 247 | pid_t pid; 248 | int status; 249 | int i; 250 | char c; 251 | int numchars = 1; 252 | int content_length = -1; 253 | 254 | buf[0] = 'A'; buf[1] = '\0'; 255 | if (strcasecmp(method, "GET") == 0) 256 | /* 读取和丢弃http请求头*/ 257 | while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 258 | numchars = get_line(client, buf, sizeof(buf)); 259 | else if (strcasecmp(method, "POST") == 0) /*POST*/ 260 | { 261 | numchars = get_line(client, buf, sizeof(buf)); 262 | while ((numchars > 0) && strcmp("\n", buf)) 263 | { 264 | buf[15] = '\0'; 265 | /* 获取http消息传输长度 */ 266 | if (strcasecmp(buf, "Content-Length:") == 0) 267 | content_length = atoi(&(buf[16])); 268 | numchars = get_line(client, buf, sizeof(buf)); 269 | } 270 | if (content_length == -1) { 271 | bad_request(client); 272 | return; 273 | } 274 | } 275 | else/*HEAD or other*/ 276 | { 277 | } 278 | 279 | 280 | /* 281 | * 建立两条管道, 用于父子进程之间通信, cig使用标准输入和输出. 282 | * 要获取标准输入输出, 可以把stdin重定向到cgi_input管道, 把stdout重定向到cgi_output管道 283 | * 为什么使用两条管道 ? 一条管道可以看做储存一个信息, 只是一段用来读, 另一端用来写. 我们有标准输入和标准输出两个信息, 所以要两条管道 284 | * */ 285 | if (pipe(cgi_output) < 0) { 286 | cannot_execute(client); 287 | return; 288 | } 289 | if (pipe(cgi_input) < 0) { 290 | cannot_execute(client); 291 | return; 292 | } 293 | 294 | /* 创建子进程执行cgi函数, 获取cgi的标准输出通过管道传给父进程, 由父进程发给客户端. */ 295 | if ( (pid = fork()) < 0 ) { 296 | cannot_execute(client); 297 | return; 298 | } 299 | /* 200 ok状态 */ 300 | sprintf(buf, "HTTP/1.0 200 OK\r\n"); 301 | send(client, buf, strlen(buf), 0); 302 | 303 | /* 子进程执行cgi脚本 */ 304 | if (pid == 0) /* child: CGI script */ 305 | { 306 | char meth_env[255]; 307 | char query_env[255]; 308 | char length_env[255]; 309 | 310 | dup2(cgi_output[1], STDOUT); //标准输出重定向到cgi_output的写端 311 | dup2(cgi_input[0], STDIN); //标准输入重定向到cgi_input的读端 312 | close(cgi_output[0]); //关闭cgi_output读端 313 | close(cgi_input[1]); //关闭cgi_input写端 314 | 315 | /* 添加到子进程的环境变量中 */ 316 | sprintf(meth_env, "REQUEST_METHOD=%s", method); 317 | putenv(meth_env); 318 | if (strcasecmp(method, "GET") == 0) { 319 | //设置QUERY_STRING环境变量 320 | sprintf(query_env, "QUERY_STRING=%s", query_string); 321 | putenv(query_env); 322 | } 323 | else { /* POST */ 324 | sprintf(length_env, "CONTENT_LENGTH=%d", content_length); 325 | putenv(length_env); 326 | } 327 | // 最后,子进程使用exec函数簇,调用外部脚本来执行 328 | execl(path,path,NULL); 329 | exit(0); 330 | } else { /* parent */ 331 | /* 父进程关闭cgi_output的写端和cgi_input的读端 */ 332 | close(cgi_output[1]); 333 | close(cgi_input[0]); 334 | /* 如果是POST方法, 继续读取写入到cgi_input管道, 这是子进程会从此管道读取 */ 335 | if (strcasecmp(method, "POST") == 0) 336 | for (i = 0; i < content_length; i++) { 337 | recv(client, &c, 1, 0); 338 | write(cgi_input[1], &c, 1); 339 | } 340 | /* 从cgi_output管道中读取子进程的输出, 发送给客户端 */ 341 | while (read(cgi_output[0], &c, 1) > 0) 342 | send(client, &c, 1, 0); 343 | /* 关闭管道 */ 344 | close(cgi_output[0]); 345 | close(cgi_input[1]); 346 | /* 等待子进程退出 */ 347 | waitpid(pid, &status, 0); 348 | } 349 | } 350 | 351 | /**********************************************************************/ 352 | /* Get a line from a socket, whether the line ends in a newline, 353 | * carriage return, or a CRLF combination. Terminates the string read 354 | * with a null character. If no newline indicator is found before the 355 | * end of the buffer, the string is terminated with a null. If any of 356 | * the above three line terminators is read, the last character of the 357 | * string will be a linefeed and the string will be terminated with a 358 | * null character. 359 | * Parameters: the socket descriptor 360 | * the buffer to save the data in 361 | * the size of the buffer 362 | * Returns: the number of bytes stored (excluding null) 363 | * 364 | * 读取一行数据 , 以 '\n'结尾, 并在后面添加 '\0' 365 | * 366 | * */ 367 | /**********************************************************************/ 368 | int get_line(int sock, char *buf, int size) 369 | { 370 | int i = 0; 371 | char c = '\0'; 372 | int n; 373 | 374 | while ((i < size - 1) && (c != '\n')) 375 | { 376 | /* 一次接收一个字节 */ 377 | n = recv(sock, &c, 1, 0); 378 | /* DEBUG printf("%02X\n", c); */ 379 | if (n > 0) 380 | { 381 | /* \r\n结尾 */ 382 | if (c == '\r') 383 | { 384 | //读缓冲区下一个字节, 但是不取走. 385 | n = recv(sock, &c, 1, MSG_PEEK); 386 | /* DEBUG printf("%02X\n", c); */ 387 | if ((n > 0) && (c == '\n')) 388 | /* 取走 \n, 赋值给c */ 389 | recv(sock, &c, 1, 0); 390 | else 391 | /* 没有换行符, 则把c设置为\n */ 392 | c = '\n'; 393 | } 394 | //存进缓冲区 395 | buf[i] = c; 396 | i++; 397 | } 398 | else 399 | c = '\n'; 400 | } 401 | /* 结束标识 */ 402 | buf[i] = '\0'; 403 | 404 | return(i); 405 | } 406 | 407 | /**********************************************************************/ 408 | /* Return the informational HTTP headers about a file. */ 409 | /* Parameters: the socket to print the headers on 410 | * the name of the file */ 411 | /**********************************************************************/ 412 | void headers(int client, const char *filename) 413 | { 414 | char buf[1024]; 415 | (void)filename; /* could use filename to determine file type */ 416 | 417 | strcpy(buf, "HTTP/1.0 200 OK\r\n"); 418 | send(client, buf, strlen(buf), 0); 419 | strcpy(buf, SERVER_STRING); 420 | send(client, buf, strlen(buf), 0); 421 | sprintf(buf, "Content-Type: text/html\r\n"); 422 | send(client, buf, strlen(buf), 0); 423 | strcpy(buf, "\r\n"); 424 | send(client, buf, strlen(buf), 0); 425 | } 426 | 427 | /**********************************************************************/ 428 | /* Give a client a 404 not found status message. */ 429 | /**********************************************************************/ 430 | void not_found(int client) 431 | { 432 | char buf[1024]; 433 | 434 | sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); 435 | send(client, buf, strlen(buf), 0); 436 | sprintf(buf, SERVER_STRING); 437 | send(client, buf, strlen(buf), 0); 438 | sprintf(buf, "Content-Type: text/html\r\n"); 439 | send(client, buf, strlen(buf), 0); 440 | sprintf(buf, "\r\n"); 441 | send(client, buf, strlen(buf), 0); 442 | sprintf(buf, "Not Found\r\n"); 443 | send(client, buf, strlen(buf), 0); 444 | sprintf(buf, "

The server could not fulfill\r\n"); 445 | send(client, buf, strlen(buf), 0); 446 | sprintf(buf, "your request because the resource specified\r\n"); 447 | send(client, buf, strlen(buf), 0); 448 | sprintf(buf, "is unavailable or nonexistent.\r\n"); 449 | send(client, buf, strlen(buf), 0); 450 | sprintf(buf, "\r\n"); 451 | send(client, buf, strlen(buf), 0); 452 | } 453 | 454 | /**********************************************************************/ 455 | /* Send a regular file to the client. Use headers, and report 456 | * errors to client if they occur. 457 | * Parameters: a pointer to a file structure produced from the socket 458 | * file descriptor 459 | * the name of the file to serve 460 | * 静态文件 直接发送 461 | * */ 462 | /**********************************************************************/ 463 | void serve_file(int client, const char *filename) 464 | { 465 | FILE *resource = NULL; 466 | int numchars = 1; 467 | char buf[1024]; 468 | 469 | buf[0] = 'A'; buf[1] = '\0'; 470 | while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 471 | numchars = get_line(client, buf, sizeof(buf)); 472 | 473 | resource = fopen(filename, "r"); 474 | if (resource == NULL) 475 | not_found(client); 476 | else 477 | { 478 | //发送头信息 479 | headers(client, filename); 480 | //发送文件内容 481 | cat(client, resource); 482 | } 483 | fclose(resource); 484 | } 485 | 486 | /**********************************************************************/ 487 | /* This function starts the process of listening for web connections 488 | * on a specified port. If the port is 0, then dynamically allocate a 489 | * port and modify the original port variable to reflect the actual 490 | * port. 491 | * Parameters: pointer to variable containing the port to connect on 492 | * Returns: the socket 493 | * 建立socket, 绑定套接字, 并监听端口 494 | * */ 495 | 496 | /**********************************************************************/ 497 | int startup(u_short *port) 498 | { 499 | int httpd = 0; 500 | int on = 1; 501 | struct sockaddr_in name; 502 | 503 | /* 建立套接字, 一条通信的线路 */ 504 | httpd = socket(PF_INET, SOCK_STREAM, 0); 505 | if (httpd == -1) 506 | error_die("socket"); 507 | memset(&name, 0, sizeof(name)); //0填充, struct sockaddr_in +实际多出来sin_zero没有用处. 508 | name.sin_family = AF_INET; //IPV4协议 509 | name.sin_port = htons(*port); //主机字节序转网络字节序 510 | name.sin_addr.s_addr = htonl(INADDR_ANY); //监听任意IP 511 | 512 | /* 允许本地地址与套接字重复绑定 , 也就是说在TCP关闭连接处于TIME_OUT状态时重用socket */ 513 | if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) 514 | { 515 | error_die("setsockopt failed"); 516 | } 517 | 518 | /* 用于socket信息与套接字绑定 */ 519 | if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) 520 | error_die("bind"); 521 | 522 | /* 未设置端口则随机生成 */ 523 | if (*port == 0) /* if dynamically allocating a port */ 524 | { 525 | socklen_t namelen = sizeof(name); 526 | /*使用次函数可回去友内核赋予该连接的端口号*/ 527 | if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) 528 | error_die("getsockname"); 529 | *port = ntohs(name.sin_port); 530 | } 531 | 532 | /* 使套接字处于被监听状态 */ 533 | if (listen(httpd, 5) < 0) 534 | error_die("listen"); 535 | return(httpd); 536 | } 537 | 538 | /**********************************************************************/ 539 | /* Inform the client that the requested web method has not been 540 | * implemented. 541 | * Parameter: the client socket 542 | * 543 | * http 501状态码 544 | * */ 545 | /**********************************************************************/ 546 | void unimplemented(int client) 547 | { 548 | char buf[1024]; 549 | 550 | sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); 551 | send(client, buf, strlen(buf), 0); 552 | sprintf(buf, SERVER_STRING); 553 | send(client, buf, strlen(buf), 0); 554 | sprintf(buf, "Content-Type: text/html\r\n"); 555 | send(client, buf, strlen(buf), 0); 556 | sprintf(buf, "\r\n"); 557 | send(client, buf, strlen(buf), 0); 558 | sprintf(buf, "Method Not Implemented\r\n"); 559 | send(client, buf, strlen(buf), 0); 560 | sprintf(buf, "\r\n"); 561 | send(client, buf, strlen(buf), 0); 562 | sprintf(buf, "

HTTP request method not supported.\r\n"); 563 | send(client, buf, strlen(buf), 0); 564 | sprintf(buf, "\r\n"); 565 | send(client, buf, strlen(buf), 0); 566 | } 567 | 568 | /**********************************************************************/ 569 | 570 | int main(void) 571 | { 572 | /* 定义socket相关信息 */ 573 | int server_sock = -1; 574 | u_short port = 4000; 575 | int client_sock = -1; 576 | struct sockaddr_in client_name; 577 | socklen_t client_name_len = sizeof(client_name); 578 | pthread_t newthread; 579 | 580 | server_sock = startup(&port); 581 | printf("httpd running on port %d\n", port); 582 | 583 | while (1) 584 | { 585 | /* 通过accept接受客户端请求, 阻塞方式 */ 586 | client_sock = accept(server_sock, 587 | (struct sockaddr *)&client_name, 588 | &client_name_len); 589 | if (client_sock == -1) 590 | error_die("accept"); 591 | /* accept_request(&client_sock); */ 592 | /* 开启线程处理客户端请求 */ 593 | if (pthread_create(&newthread , NULL, accept_request, (void *)&client_sock) != 0) 594 | perror("pthread_create"); 595 | } 596 | 597 | close(server_sock); 598 | 599 | return(0); 600 | } 601 | --------------------------------------------------------------------------------