小型HTTP服务器
来源:互联网 发布:秒赞网哪一个源码好 编辑:程序博客网 时间:2024/05/11 16:57
#include "httpd.h" void usage(const char *proc) { printf("Usage : %s [PORT]\n", proc); } static void not_found(int client) { } void print_debug(const char * msg) { #ifdef _DEBUG_ printf("%s\n", msg); #endif } static void bad_request(int client) { print_debug("enter our fault...\n"); char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\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, "<html></br><p>your enter message is a bad request</p></br></html>\r\n"); send(client, buf, strlen(buf), 0); } void print_log(const char *fun, int line, int err_no, const char *err_str) { printf("[%s: %d] [%d] [%s]\n", fun, line, err_no, err_str); } void clear_header(int client) { char buf[1024]; memset(buf, '\0', sizeof(buf)); int ret = 0; do{ ret = get_line(client, buf, sizeof(buf)); }while(ret > 0 && strcmp(buf, "\n") != 0 ); } //return num char, success //return <= 0, failed int get_line(int sock, char *buf, int max_len) { if( !buf || max_len < 0){ return -1; } int i = 0; int n = 0; char c = '\0'; while( i < max_len-1 && c != '\n' ){ n = recv(sock, &c, 1, 0); if(n > 0){//success if( c == '\r' ){ n = recv(sock, &c, 1,MSG_PEEK); if( n > 0 && c == '\n' ){//windows recv(sock, &c, 1, 0);//delete }else{ c = '\n'; } } buf[i++] = c; }else{//failed c = '\n'; } } buf[i] = '\0'; return i; } void echo_error_to_client(int client, int error_code) { switch(error_code){ case 400://request error bad_request(client); break; case 404://not found not_found(client); break; case 500://server error // server_error(client); break; case 503://server unavailable // server_unavailable(client); break; //..................... default: // default_error(client); break; } } void echo_html(int client, const char *path, unsigned int file_size) { if( !path ){ return; } int in_fd = open(path, O_RDONLY);//以只读的形式打开 if(in_fd < 0){ print_debug("open index.html error"); //echo_error_to_client(); return; } print_debug("open index.html success"); char echo_line[1024]; memset(echo_line, '\0', sizeof(echo_line)); strncpy(echo_line, HTTP_VERSION, strlen(HTTP_VERSION)+1); strcat(echo_line, " 200 OK"); strcat(echo_line, "\r\n\r\n"); send(client, echo_line,strlen(echo_line), 0); print_debug("send echo head success"); if( sendfile(client, in_fd, NULL, file_size) < 0 ){ print_debug("send_file error"); //echo_error_to_client(); close(in_fd); return; } print_debug("sendfile success"); close(in_fd); } void exe_cgi(int sock_client, const char *path, const char *method,const char *query_string) { print_debug("enter cgi\n"); char buf[_COMM_SIZE_]; int numchars = 0; int content_length = -1; //pipe int cgi_input[2] = {0, 0}; int cgi_output[2] = {0, 0}; //child proc pid_t id; print_debug(method); if(strcasecmp(method, "GET") == 0){//GET clear_header(sock_client); }else{//POST do{ memset(buf, '\0', sizeof(buf)); numchars = get_line(sock_client, buf, sizeof(buf)); if(strncasecmp(buf, "Content-Length:", strlen("Content-Length:")) == 0){ //函数定义:int strncasecmp(const char *s1, const char *s2, size_t n) //函数说明:strncasecmp()用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异。 content_length = atoi(&buf[16]); } }while(numchars > 0 && strcmp(buf, "\n") != 0);//一行一行的比较直到寻找content_length if( content_length == -1 ){ //echo_error_to_client(); return; } } memset(buf, '\0', sizeof(buf)); strcpy(buf, HTTP_VERSION); strcat(buf, " 200 OK\r\n\r\n"); send(sock_client, buf, strlen(buf), 0); if( pipe(cgi_input) == -1 ){//pipe error //echo_error_to_client(); return; } if( pipe(cgi_output) == -1 ){ close(cgi_input[0]); close(cgi_input[1]); //echo_error_to_client(); return; } if( (id = fork()) < 0){//fork error close(cgi_input[0]); close(cgi_input[1]); close(cgi_output[0]); close(cgi_output[1]); //echo_error_to_client(); return; }else if( id == 0 ){//child char query_env[_COMM_SIZE_/10]; char method_env[_COMM_SIZE_]; char content_len_env[_COMM_SIZE_]; memset(method_env, '\0', sizeof(method_env)); memset(query_env, '\0', sizeof(query_env)); memset(content_len_env, '\0', sizeof(content_len_env)); close(cgi_input[1]); close(cgi_output[0]); //redir dup2(cgi_input[0], 0); dup2(cgi_output[1], 1); sprintf(method_env, "REQUEST_METHOD=%s", method); putenv(method_env); if(strcasecmp("GET", method) == 0){//POST sprintf(query_env, "QUERY_STRING=%s", query_string); putenv(query_env); }else{//POST sprintf(content_len_env, "CONTENT_LENGTH=%d", content_length); putenv(content_len_env); } execl(path, path, NULL);//exec函数族 exit(1); }else{//father close(cgi_input[0]); close(cgi_output[1]); int i = 0; char c = '\0'; if(strcasecmp("POST", method) == 0){ for(; i < content_length; i++ ){ recv(sock_client, &c, 1, 0); write(cgi_input[1], &c, 1); } } while( read(cgi_output[0], &c, 1) > 0 ){ send(sock_client, &c, 1, 0); } close(cgi_input[1]); close(cgi_output[0]); waitpid(id, NULL, 0); } } //GET && POST void *accept_request(void *arg)//这个arg实际上是由socket强转过来的那个参数 { print_debug("get a new connect...\n"); pthread_detach(pthread_self());//detach //let the thread free automaticaly int sock_client = (int)arg;//将客户端套接字恢复回来 //for test //echo_error_to_client(sock_client, 400); //close(sock_client); //return; int cgi = 0; char *query_string = NULL; char method[_COMM_SIZE_/10]; char url[_COMM_SIZE_]; char buffer[_COMM_SIZE_]; char path[_COMM_SIZE_]; memset(method, '\0', sizeof(method)); memset(url, '\0', sizeof(url)); memset(buffer, '\0', sizeof(buffer)); memset(path, '\0', sizeof(path)); //#ifdef _DEBUG_ // //success > 0 // //else <= 0 // while(get_line(sock_client, buffer, sizeof(buffer)) > 0){ // printf("%s", buffer); // fflush(stdout); // } // printf("\n"); //#endif if(get_line(sock_client, buffer, sizeof(buffer)) < 0){//从客户端一行一行读取数据 //echo_error_to_client(); return NULL; } int i = 0; int j = 0;//buffer line index while( !isspace(buffer[j]) &&\//从消息中获取方法保存到method i < sizeof(method)-1 &&\ j < sizeof(buffer)){ method[i] = buffer[j]; i++, j++; } if( strcasecmp(method, "GET") && strcasecmp(method, "POST")){ //echo_error_to_client(); return NULL; } //clear space point useful url start while( isspace(buffer[j]) &&\//如果发现是空行就跳到下一行,指向url开始的地方 j < sizeof(buffer)){ j++; } //get url i = 0; while(!isspace(buffer[j]) &&\//获取url i < sizeof(url)-1 &&\ j < sizeof(buffer)){ url[i] = buffer[j]; i++;j++; } print_debug(method);//打印方法和路径 print_debug(url); if(strcasecmp(method, "POST") == 0){//如果是POST方法,就让cgi=1,是不是POST方法就直接可用呢 cgi = 1; } if(strcasecmp(method, "GET") == 0){//那我们就可以稍微处理一下啦 query_string = url; while( *query_string != '?' && *query_string != '\0'){ query_string++; } if( *query_string == '?' ){//url = /XXX/XXX + arg *query_string = '\0'; query_string++; cgi = 1; } } sprintf(path, "htdocs%s", url);//将url所在文件包装成路径保存在path中 if(path[strlen(path)-1] == '/'){ strcat(path, MAIN_PAGE);//最后在附上主页就好了 } print_debug(path); struct stat st; if( stat(path, &st) < 0 ){ //failed, does not exist // stat()用来将参数path 所指的文件状态, 复制到参数st所指的结构中 print_debug("miss cgi"); clear_header(sock_client); //echo_error_to_client(); }else{//file exist! if(S_ISDIR(st.st_mode)){//如果是个目录文件追加/ strcat(path, "/"); strcat(path, MAIN_PAGE); }else if(st.st_mode & S_IXUSR ||\ st.st_mode & S_IXGRP ||\ st.st_mode & S_IXOTH){ //文件S_IRUSR (S_IREAD) 00400 文件所有者具可读取权限 //S_IWUSR (S_IWRITE)00200 文件所有者具可写入权限 //S_IXUSR (S_IEXEC) 00100 文件所有者具可执行权限 cgi = 1; }else{ //do nothing } if(cgi){ exe_cgi(sock_client, path, method, query_string); }else{ clear_header(sock_client); print_debug("begin enter our echo_html"); echo_html(sock_client, path, st.st_size); } } close(sock_client); return NULL; } //if success return sock //else exit process int start(short port) { int listen_sock = socket(AF_INET, SOCK_STREAM, 0); if(listen_sock == -1){ print_log(__FUNCTION__, __LINE__, errno, strerror(errno)); exit(1); } //reuse port//定义一个地址空间 int flag = 1; setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(port);//host -> net local.sin_addr.s_addr = htonl(INADDR_ANY); socklen_t len = sizeof(local); //将地址空间和定义的套接字绑定在一起 if(bind(listen_sock, (struct sockaddr*)&local, len) == -1){ print_log(__FUNCTION__, __LINE__, errno, strerror(errno)); exit(2); } //不断监听处理套接字的请求 if(listen(listen_sock, _BACK_LOG_) == -1){ print_log(__FUNCTION__, __LINE__, errno, strerror(errno)); exit(3); } //监听成功后返回监听套接字 return listen_sock; //sucess } //./httpd port int main(int argc, char *argv[]) { if(argc != 2){ usage(argv[0]); exit(1); } //daemon(); int port = atoi(argv[1]); int sock = start(port);//listen socket struct sockaddr_in client; socklen_t len = 0; while(1){ int new_sock = accept(sock, (struct sockaddr*)&client, &len);//这个返回的新的套接字称为链接套接字,不同客户端的请求返回的 //新socket自然不一样 if( new_sock < 0 ){//accept error print_log(__FUNCTION__, __LINE__, errno, strerror(errno)); continue; } pthread_t new_thread;//一旦有请求就定义一个新的线程去解决问题 pthread_create(&new_thread, NULL, accept_request, (void*)new_sock);//转成void*后变成参数传给accept_request } return 0; }
阅读全文
0 0
- 小型HTTP服务器
- 小型http服务器
- 小型HTTP服务器
- 小型开源http服务器
- 小型的http服务器处于维护中
- 嵌入式设备中搭建小型http服务器
- 利用http协议实现小型Web服务器
- Linux下基于http的小型web服务器编写
- 基于HTTP协议实现的小型web服务器
- servlet小型服务器程序
- 小型tcp服务器--select
- \t\t让世界最小linux系统----ttylinux成为一个小型http服务器
- 小型的http代理程序
- 如何搭建小型集群服务器?
- Tiny server:小型Web服务器
- 树莓派 小型服务器的搭建
- TCP小型服务器(poll)
- 一个小型的http代理程序
- 技术书籍推荐(C++方向)
- win10下安装ubuntu(微星GE62VR
- 超级简单的底部导航按钮
- 【s5p4418嵌入式学习】u-boot学习之makefile注释07
- Ubuntu使用vim编辑器时方向键乱码和退格键不能使用的问题
- 小型HTTP服务器
- sql语句实现查询
- LeetCode.62
- PAT 1012 排序
- java中System.exit()方法
- [LeetCode] 3. Longest Substring Without Repeating Characters
- Android Studio AIDL实现
- jpg读取exif属性值
- more effective C++条款六解析