1. 程式人生 > >搭建簡單的http伺服器

搭建簡單的http伺服器

開發語言C++,平臺為Linux。
主要流程為:伺服器獲得請求–>響應並處理請求–>返回結果。

這裡寫圖片描述

這裡著重講怎麼處理請求。
主程式在獲得一個請求後會開闢一個執行緒來處理請求
流程圖如下。
這裡寫圖片描述

hand_cgi函式流程圖
這裡寫圖片描述

cgi程式流程圖。
這裡寫圖片描述
程式碼:

#include"http.h"

int ret = 0;
void printf_log(string s){

        //cout << s << endl;
}

int init_fd(int socketi, char* str){

    sockaddr_in addr;
    bzero(&addr, sizeof
(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(inet_addr(str)); addr.sin_port = htons(PORT); int err = bind(socketi, (struct sockaddr*)&addr, sizeof(addr)); if (err < 0){ printf_log("bind error"); ret = -1; } int on = 0; setsockopt( socketi, SOL_SOCKET, SO_REUSEADDR, &on, sizeof
(on) ); err = listen(socketi, 5); if (err < 0){ printf_log("listen error"); ret = -1; } return socketi; } static int get_line(int fd, char *buf){ char ch = 0; char next = 0; int i = 0; int err = 0; while (ch != '\n'){ int size = read(fd, &ch, 1
); if (size <= 0){ ch = '\n'; } if (ch != '\n' && ch != '\r'){ buf[i++] = ch; } else{ if (ch == '\r'){ err = recv(fd, &next, 1, MSG_PEEK); if (err < 0){ printf_log("recv error"); return -1; } if (next == '\n'){ err = read(fd, &ch, 1); if (err < 0){ printf_log("recv error"); return -1; } }//if ch = '\n'; } }//else }//while buf[i] = 0; return 1; } static void clean_header(int fd){ char ch[100] = {0}; get_line(fd, ch); while (strlen(ch) != 0){ get_line(fd, ch); } } static int hand_cgi(int fd, char* path, char * argument){ char line[SIZE/2] = {0}; char length[10] = {0}; int i_length = 0; char method[10] = {0}; if (strlen(argument) == 0){ sprintf(method, "METHOD=POST"); while (get_line(fd, line) > 0){ if (strncmp(line, "Content-Length", 16) == 0){ i_length = atoi(line + 16); break; } }//while clean_header(fd); sprintf(length, "Content-Length=%d", i_length); putenv(length); }//if else{ sprintf(method, "METHOD=GET"); char str[100] = {0}; sprintf(str, "QESTRING=%s", argument); if (putenv(str) < 0){ printf_log("she zhi huai jing cuo wu"); ret = -1; } clean_header(fd); }//else putenv(method); //reponse char request[] = "HTTP/1.0 200 OK\r\n"; write(fd, request, strlen(request)); int _std[2] = {0}; int _std2[2] = {0}; if (pipe(_std) < 0){ printf_log("pipe error"); ret = -1; } if (pipe(_std2) < 0){ printf_log("pipe2 error"); ret = -1; } pid_t pid = fork(); if (pid < 0){ printf_log("fork error"); ret = -1; return 0; } if (pid == 0){ close(_std[0]); close(_std2[1]); dup2(_std[1], 1); dup2(_std2[0], 0); execl(path,path, NULL); cout << "error" << endl; } else{ close(_std[1]); close(_std2[0]); int ch = 0; char string[SIZE/2] = {0}; for (int i = 0; i < i_length; i++){ read(fd, &ch, 1); string[i] = ch; } char buf[SIZE/2] = {0}; write(_std2[1], string, strlen(string)); read(_std[2], buf, SIZE/2); //返回輸出 write(fd, buf, strlen(buf)); } close(_std[0]); close(_std2[1]); } } static void get(int fd){ char buf[2*SIZE]; char ch; int i = 0; while (read(fd, &ch, 1) > 0){ buf[i++] = ch; } // printf("%s\n", buf); } void *handler_in(void *arg){ pthread_detach(pthread_self()); char method[SIZE/2] = {0}; char buf[SIZE] = {0}; char line[SIZE/2] = {0}; char url[SIZE/2] = {0}; char goal[SIZE/2] = {0}; char argument[SIZE] = {0}; int err = 0; int cgi = 0; int i = 0; char request[] = "HTTP/1.1 200 OK\r\n"; //cout << 123 << endl; err = get_line((int)arg, line); if (err < 0){ ret = 1; goto end; } for (i = 0; line[i] != ' '; i++){ method[i] = line[i]; } i++; for (int j = 0; line[i] != ' '&&line[i] != '?'; i++){ url[j++] = line[i]; } //printf("%s\n" ,method); if (line[i] == '?'){ i++; for (int j = 0; line[i] != ' '&&line[i] != '?'; i++){ argument[j++] = line[i]; } } if (strcasecmp(method, "POSE") != 0 && strcasecmp(method, "GET") != 0){ printf_log("method"); ret = 2; goto end; } if (strcasecmp(method, "POSE") == 0) cgi = 1; if (strcasecmp(method, "GET") == 0 && argument[0] != 0) cgi = 1; if (strlen(url) == 1 && strcasecmp(url , "/") == 0) sprintf(goal, "wwwroot/root.html"); else sprintf(goal, "wwwroot%s", url); //判斷檔案 struct stat file; err = stat(goal, &file); if (err < 0){ ret = 3; printf_log("file not found"); goto end; } if (file.st_mode & S_IFDIR){ // } if (file.st_mode & S_IXUSR || file.st_mode & S_IXGRP || file.st_mode & S_IXOTH){ if (0 == cgi){ ret = 4; printf_log("檔案型別與cgi不匹配"); goto end; } } if (1 == cgi){ err = hand_cgi((int)arg, goal, argument); }//cgi if else{ clean_header((int)arg); write((int)arg, request, strlen(request)); write((int)arg, "\r\n", 2); int fd = open(goal, O_RDONLY); err = sendfile((int)arg, fd, 0, file.st_size); if (err < 0){ ret = 5; printf_log("sendfile error"); printf("%d, %s\n", errno, strerror(errno)); } }//cgi else end: close((int)arg); }

cgi程式示例(2數相加):

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define SIZE 1024
using namespace std;

int hand(char *q){

    char *num = NULL;
  char *num_2 = NULL;
  int num1 = 0;
  int num2 = 0;
  while (*q != '=')
    q++;
    q++;
  num = q;
  while (*q != '&')
    q++;
  *q = 0;
  num1 = atoi(num);

  while (*q != '=')
      q++;
    q++;
  num_2 = q;
  num2 = atoi(num_2);

    printf("<html>\n");
    printf("<head><h2>%d + %d = %d</h2></head>\r\n", num1, num2, num1+num2);
    printf("</html>\n");


}

int main(){

    printf("Content-Type:text/html;charset=ISO-8859-1\n\n");           
    char *p = NULL;
      int i_length = 0;
    char query_string[SIZE/2] = {0};
    char *length = NULL;
   p = getenv("METHOD");
   if (strcmp(p, "GET") == 0){

           char *s = getenv("QESTRING");
           memcpy(query_string, s, strlen(s)); 
   }//if
   else{
       length = getenv("Content-Length");
       i_length = atoi(length);
       char ch = 0;
       for (int i = 0; i < i_length; i++){
           ch = getc(0);
           query_string[i] = ch;
       }
   }

   hand(query_string);
    printf("\n");
    return 0;
}

執行:
傳送請求
這裡寫圖片描述
返回結果:
這裡寫圖片描述

此外,我們可再寫一個可以連線mysql資料庫的cgi程式,這樣我們就可以遠端管理資料庫了。
cgi程式:



#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<mysql.h>
#include<cstring>
using namespace std;


const int SIZE = 100;

//把值賦給相應的引數
void static handing(char* &str, char* &query){

    while (*query != '=')
            query++;
    str = ++query;
    while (*query != '&'&& *query != 0)
        query++;
    *query = 0;
    query++;


}
//取值
static void hand_query(int &mod, char* &name, int &age, char *&where, char *query){

    char *str = NULL;
    handing(str, query);
    mod = atoi(str);
    handing(where, query);
    handing(name, query);
    if (3 == mod)
        return;
    handing(str, query);
    age = atoi(str);
}






void find(MYSQL *conn, char *name, char *where){
    MYSQL_RES *result;
    MYSQL_ROW row;
    char buf[100] = {0};
    sprintf(buf, "SELECT * FROM %s", where);
    mysql_query(conn, buf);
    result = mysql_store_result(conn);
    if (NULL == result)
        cout << "NULL" << endl;
    int num_fields = mysql_num_fields(result);
    while ((row = mysql_fetch_row(result))){

        if (strcmp(row[0], name) == 0)
        for (int i = 0; i < num_fields; i++){
            printf("%s ", row[i] ? row[i] : "NULL");
        }
    }
    mysql_free_result(result);


}




int hand(char *query){

    int mod = 0;
    char *name = NULL;
    char buf[100] ={0};
    int age = 0;
    char *where = NULL;
    hand_query(mod, name, age, where, query);
    MYSQL *conn = new MYSQL;
    conn = mysql_init(NULL);
    if (conn == NULL){

        printf("ERROR %u: %s\n", mysql_errno(conn), mysql_error(conn));

        exit(1);
    }

    if (mysql_real_connect(conn, "localhost", "root", 
                           NULL, "tq", 0, NULL, 0)==NULL){

        printf("ERROR %u:%s\n", mysql_errno(conn), mysql_error(conn));
        exit(1);
    }
    //這裡只列舉最簡單的find操作
    switch(mod){

        case 1:creat(conn, name, age, where);
        break;
        case 2:insert(conn, name, age,where);
        break;
        case 3:find(conn, name,where);
        break;
        case 4:modifier(conn, name, age, where);
        break;
        case 5:my_delete(conn, name, where);
        default:
        break;
    }


    mysql_close(conn);




}

int main(){
    printf("Content-Type:text/html;charset=ISO-8859-1\n\n");           
    char *p = NULL;
      int i_length = 0;
    char query_string[SIZE/2] = {0};
    char *length = NULL;
   p = getenv("METHOD");
   if (strcmp(p, "GET") == 0){

           char *s = getenv("QESTRING");
           memcpy(query_string, s, strlen(s)); 
   }//if
   else{
       length = getenv("Content-Length");
       i_length = atoi(length);
       char ch = 0;
       for (int i = 0; i < i_length; i++){
           ch = getc(0);
           query_string[i] = ch;
       }
   }

    hand(query_string);
    printf("\n");
    return 0;
}

這裡寫圖片描述
mod表示操作,where是哪個·表,name是人名。
tbq這個表如下:
這裡寫圖片描述
處理請求的結果:
這裡寫圖片描述
前端知識知之甚少,勿怪啊。。
在這裡一個簡單的http伺服器就搭建完成了,但是我們還可以做很多事。比如,當請求過多時效率太低怎麼辦?再者,我們可以寫一個爬蟲程式來提高。除此之外,我們可以在深入調研nginx伺服器。
原始碼地址:https://github.com/fengasdf/-http-(由於整合了協程,python爬蟲,以及其他程式,可能有點亂)
http伺服器運用協程:
http://blog.csdn.net/fengasdfgh/article/details/76688440