标签:function tin put inter star rom short body void
/* J. David‘s webserver */ /* This is a simple webserver. * Created November 1999 by J. David Blackstone. * CSE 4344 (Network concepts), Prof. Zeigler * University of Texas at Arlington */ /* This program compiles for Sparc Solaris 2.6. * To compile for Linux: * 1) Comment out the #include <pthread.h> line. * 2) Comment out the line that defines the variable newthread. * 3) Comment out the two lines that run pthread_create(). * 4) Uncomment the line that runs accept_request(). * 5) Remove -lsocket from the Makefile. */ #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <sys/wait.h> #include <stdlib.h> //宏定义,是否是空格 #define ISspace(x) isspace((int)(x)) #define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" //每次收到请求,创建一个线程来处理接受到的请求 //把client_sock转成地址作为参数传入pthread_create void accept_request(void *arg); //错误请求 void bad_request(int); //读取文件 void cat(int, FILE *); //无法执行 void cannot_execute(int); //错误输出 void error_die(const char *); //执行cig脚本 void execute_cgi(int, const char *, const char *, const char *); //得到一行数据,只要发现c为\n,就认为是一行结束,如果读到\r,再用MSG_PEEK的方式读入一个字符,如果是\n,从socket用读出 //如果是下个字符则不处理,将c置为\n,结束。如果读到的数据为0中断,或者小于0,也视为结束,c置为\n int get_line(int, char *, int); //返回http头 void headers(int, const char *); //没有发现文件 void not_found(int); //如果不是CGI文件,直接读取文件返回给请求的http客户端 void serve_file(int, const char *); //开启tcp连接,绑定端口等操作 int startup(u_short *); //如果不是Get或者Post,就报方法没有实现 void unimplemented(int); // Http请求,后续主要是处理这个头 // // GET / HTTP/1.1 // Host: 192.168.0.23:47310 // Connection: keep-alive // Upgrade-Insecure-Requests: 1 // User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*; q = 0.8 // Accept - Encoding: gzip, deflate, sdch // Accept - Language : zh - CN, zh; q = 0.8 // Cookie: __guid = 179317988.1576506943281708800.1510107225903.8862; monitor_count = 5 // // POST / color1.cgi HTTP / 1.1 // Host: 192.168.0.23 : 47310 // Connection : keep - alive // Content - Length : 10 // Cache - Control : max - age = 0 // Origin : http ://192.168.0.23:40786 // Upgrade - Insecure - Requests : 1 // User - Agent : Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 55.0.2883.87 Safari / 537.36 // Content - Type : application / x - www - form - urlencoded // Accept : text / html, application / xhtml + xml, application / xml; q = 0.9, image / webp, */*;q=0.8 // Referer: http://192.168.0.23:47310/ // Accept-Encoding: gzip, deflate // Accept-Language: zh-CN,zh;q=0.8 // Cookie: __guid=179317988.1576506943281708800.1510107225903.8862; monitor_count=281 // Form Data // color=gray /**********************************************************************/ /* A request has caused a call to accept() on the server port to * return. Process the request appropriately. * Parameters: the socket connected to the client */ /**********************************************************************/ void accept_request(void *arg) { //socket int client = (intptr_t)arg; char buf[1024]; int numchars; char method[255]; char url[255]; char path[512]; size_t i, j; struct stat st; int cgi = 0; /* becomes true if server decides this is a CGI * program */ char *query_string = NULL; //根据上面的Get请求,可以看到这边就是取第一行 //这边都是在处理第一条http信息 //"GET / HTTP/1.1\n" numchars = get_line(client, buf, sizeof(buf)); i = 0; j = 0; //第一行字符串提取Get while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } //结束 method[i] = ‘\0‘; //判断是Get还是Post if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); return; } //如果是POST,cgi置为1 if (strcasecmp(method, "POST") == 0) cgi = 1; i = 0; //跳过空格 while (ISspace(buf[j]) && (j < sizeof(buf))) j++; //得到 "/" 注意:如果你的http的网址为http://192.168.0.23:47310/index.html // 那么你得到的第一条http信息为GET /index.html HTTP/1.1,那么 // 解析得到的就是/index.html while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = ‘\0‘; //判断Get请求 if (strcasecmp(method, "GET") == 0) { query_string = url; while ((*query_string != ‘?‘) && (*query_string != ‘\0‘)) query_string++; if (*query_string == ‘?‘) { cgi = 1; *query_string = ‘\0‘; query_string++; } } //路径 sprintf(path, "htdocs%s", url); //默认地址,解析到的路径如果为/,则自动加上index.html if (path[strlen(path) - 1] == ‘/‘) strcat(path, "index.html"); //获得文件信息 if (stat(path, &st) == -1) { //把所有http信息读出然后丢弃 while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); //没有找到 not_found(client); } else { if ((st.st_mode & S_IFMT) == S_IFDIR) strcat(path, "/index.html"); //如果你的文件默认是有执行权限的,自动解析成cgi程序,如果有执行权限但是不能执行,会接受到报错信号 if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH) ) cgi = 1; if (!cgi) //接读取文件返回给请求的http客户端 serve_file(client, path); else //执行cgi文件 execute_cgi(client, path, method, query_string); } //执行完毕关闭socket close(client); } /**********************************************************************/ /* Inform the client that a request it has made has a problem. * Parameters: client socket */ /**********************************************************************/ void bad_request(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "<P>Your browser sent a bad request, "); send(client, buf, sizeof(buf), 0); sprintf(buf, "such as a POST without a Content-Length.\r\n"); send(client, buf, sizeof(buf), 0); } /**********************************************************************/ /* Put the entire contents of a file out on a socket. This function * is named after the UNIX "cat" command, because it might have been * easier just to do something like pipe, fork, and exec("cat"). * Parameters: the client socket descriptor * FILE pointer for the file to cat */ /**********************************************************************/ //得到文件内容,发送 void cat(int client, FILE *resource) { char buf[1024]; fgets(buf, sizeof(buf), resource); //循环读 while (!feof(resource)) { send(client, buf, strlen(buf), 0); fgets(buf, sizeof(buf), resource); } } /**********************************************************************/ /* Inform the client that a CGI script could not be executed. * Parameter: the client socket descriptor. */ /**********************************************************************/ void cannot_execute(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<P>Error prohibited CGI execution.\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* Print out an error message with perror() (for system errors; based * on value of errno, which indicates system call errors) and exit the * program indicating an error. */ /**********************************************************************/ void error_die(const char *sc) { perror(sc); exit(1); } /**********************************************************************/ /* Execute a CGI script. Will need to set environment variables as * appropriate. * Parameters: client socket descriptor * path to the CGI script */ /**********************************************************************/ void execute_cgi(int client, const char *path, const char *method, const char *query_string) { //缓冲区 char buf[1024]; //2根管道 int cgi_output[2]; int cgi_input[2]; //进程pid和状态 pid_t pid; int status; int i; char c; //读取的字符数 int numchars = 1; //http的content_length int content_length = -1; //默认字符 buf[0] = ‘A‘; buf[1] = ‘\0‘; //忽略大小写比较字符串 if (strcasecmp(method, "GET") == 0) //读取数据,把整个header都读掉,以为Get写死了直接读取index.html,没有必要分析余下的http信息了 while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); else /* POST */ { numchars = get_line(client, buf, sizeof(buf)); while ((numchars > 0) && strcmp("\n", buf)) { //如果是POST请求,就需要得到Content-Length,Content-Length:这个字符串一共长为15位,所以 //取出头部一句后,将第16位设置结束符,进行比较 //第16位置为结束 buf[15] = ‘\0‘; if (strcasecmp(buf, "Content-Length:") == 0) //内存从第17位开始就是长度,将17位开始的所有字符串转成整数就是content_length content_length = atoi(&(buf[16])); numchars = get_line(client, buf, sizeof(buf)); } if (content_length == -1) { bad_request(client); return; } } sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); //建立output管道 if (pipe(cgi_output) < 0) { cannot_execute(client); return; } //建立input管道 if (pipe(cgi_input) < 0) { cannot_execute(client); return; } // fork后管道都复制了一份,都是一样的 // 子进程关闭2个无用的端口,避免浪费 // ×<------------------------->1 output // 0<-------------------------->× input // 父进程关闭2个无用的端口,避免浪费 // 0<-------------------------->× output // ×<------------------------->1 input // 此时父子进程已经可以通信 //fork进程,子进程用于执行CGI //父进程用于收数据以及发送子进程处理的回复数据 if ( (pid = fork()) < 0 ) { cannot_execute(client); return; } if (pid == 0) /* child: CGI script */ { char meth_env[255]; char query_env[255]; char length_env[255]; //子进程输出重定向到output管道的1端 dup2(cgi_output[1], 1); //子进程输入重定向到input管道的0端 dup2(cgi_input[0], 0); //关闭无用管道口 close(cgi_output[0]); close(cgi_input[1]); //CGI环境变量 sprintf(meth_env, "REQUEST_METHOD=%s", method); putenv(meth_env); if (strcasecmp(method, "GET") == 0) { sprintf(query_env, "QUERY_STRING=%s", query_string); putenv(query_env); } else { /* POST */ sprintf(length_env, "CONTENT_LENGTH=%d", content_length); putenv(length_env); } //替换执行path execl(path, path, NULL); //int m = execl(path, path, NULL); //如果path有问题,例如将html网页改成可执行的,但是执行后m为-1 //退出子进程,管道被破坏,但是父进程还在往里面写东西,触发Program received signal SIGPIPE, Broken pipe. exit(0); } else { /* parent */ //关闭无用管道口 close(cgi_output[1]); close(cgi_input[0]); if (strcasecmp(method, "POST") == 0) for (i = 0; i < content_length; i++) { //得到post请求数据,写到input管道中,供子进程使用 recv(client, &c, 1, 0); write(cgi_input[1], &c, 1); } //从output管道读到子进程处理后的信息,然后send出去 while (read(cgi_output[0], &c, 1) > 0) send(client, &c, 1, 0); //完成操作后关闭管道 close(cgi_output[0]); close(cgi_input[1]); //等待子进程返回 waitpid(pid, &status, 0); } } /**********************************************************************/ /* Get a line from a socket, whether the line ends in a newline, * carriage return, or a CRLF combination. Terminates the string read * with a null character. If no newline indicator is found before the * end of the buffer, the string is terminated with a null. If any of * the above three line terminators is read, the last character of the * string will be a linefeed and the string will be terminated with a * null character. * Parameters: the socket descriptor * the buffer to save the data in * the size of the buffer * Returns: the number of bytes stored (excluding null) */ /**********************************************************************/ //得到一行数据,只要发现c为\n,就认为是一行结束,如果读到\r,再用MSG_PEEK的方式读入一个字符,如果是\n,从socket用读出 //如果是下个字符则不处理,将c置为\n,结束。如果读到的数据为0中断,或者小于0,也视为结束,c置为\n int get_line(int sock, char *buf, int size) { int i = 0; char c = ‘\0‘; int n; while ((i < size - 1) && (c != ‘\n‘)) { n = recv(sock, &c, 1, 0); /* DEBUG printf("%02X\n", c); */ if (n > 0) { if (c == ‘\r‘) { //偷窥一个字节,如果是\n就读走 n = recv(sock, &c, 1, MSG_PEEK); /* DEBUG printf("%02X\n", c); */ if ((n > 0) && (c == ‘\n‘)) recv(sock, &c, 1, 0); else //不是\n(读到下一行的字符)或者没读到,置c为\n 跳出循环,完成一行读取 c = ‘\n‘; } buf[i] = c; i++; } else c = ‘\n‘; } buf[i] = ‘\0‘; return(i); } /**********************************************************************/ /* Return the informational HTTP headers about a file. */ /* Parameters: the socket to print the headers on * the name of the file */ /**********************************************************************/ //加入http的headers void headers(int client, const char *filename) { char buf[1024]; (void)filename; /* could use filename to determine file type */ strcpy(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* Give a client a 404 not found status message. */ /**********************************************************************/ //如果资源没有找到得返回给客户端下面的信息 void not_found(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>The server could not fulfill\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "your request because the resource specified\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "is unavailable or nonexistent.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* Send a regular file to the client. Use headers, and report * errors to client if they occur. * Parameters: a pointer to a file structure produced from the socket * file descriptor * the name of the file to serve */ /**********************************************************************/ //如果不是CGI文件,直接读取文件返回给请求的http客户端 void serve_file(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; //默认字符 buf[0] = ‘A‘; buf[1] = ‘\0‘; while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); resource = fopen(filename, "r"); if (resource == NULL) not_found(client); else { headers(client, filename); cat(client, resource); } fclose(resource); } /**********************************************************************/ /* This function starts the process of listening for web connections * on a specified port. If the port is 0, then dynamically allocate a * port and modify the original port variable to reflect the actual * port. * Parameters: pointer to variable containing the port to connect on * Returns: the socket */ /**********************************************************************/ int startup(u_short *port) { int httpd = 0; struct sockaddr_in name; httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) error_die("socket"); memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); //绑定socket if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind"); //如果端口没有设置,提供个随机端口 if (*port == 0) /* if dynamically allocating a port */ { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); } //监听 if (listen(httpd, 5) < 0) error_die("listen"); return(httpd); } /**********************************************************************/ /* Inform the client that the requested web method has not been * implemented. * Parameter: the client socket */ /**********************************************************************/ //如果方法没有实现,就返回此信息 void unimplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</TITLE></HEAD>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ int main(void) { int server_sock = -1; u_short port = 0; int client_sock = -1; struct sockaddr_in client_name; //这边要为socklen_t类型 socklen_t client_name_len = sizeof(client_name); pthread_t newthread; server_sock = startup(&port); printf("httpd running on port %d\n", port); while (1) { //接受请求,函数原型 //#include <sys/types.h> //#include <sys/socket.h> //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); client_sock = accept(server_sock, (struct sockaddr *)&client_name, &client_name_len); if (client_sock == -1) error_die("accept"); /* accept_request(client_sock); */ //每次收到请求,创建一个线程来处理接受到的请求 //把client_sock转成地址作为参数传入pthread_create if (pthread_create(&newthread, NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0) perror("pthread_create"); } close(server_sock); return(0); }
标签:function tin put inter star rom short body void
原文地址:https://www.cnblogs.com/WindSun/p/11180229.html