搭建简单的http服务器

开发语言C++,平台为Linux。
主要流程为:服务器获得请求–>响应并处理请求–>返回结果。

搭建简单的http服务器_第1张图片

这里着重讲怎么处理请求。
主程序在获得一个请求后会开辟一个线程来处理请求
流程图如下。
搭建简单的http服务器_第2张图片

hand_cgi函数流程图
搭建简单的http服务器_第3张图片

cgi程序流程图。
搭建简单的http服务器_第4张图片
代码:

#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 
#include 
#include 
#include 

#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("\n");
    printf("

%d + %d = %d

\r\n"
, num1, num2, num1+num2); printf("\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; }

运行:
发送请求
搭建简单的http服务器_第5张图片
返回结果:
搭建简单的http服务器_第6张图片

此外,我们可再写一个可以连接mysql数据库的cgi程序,这样我们就可以远程管理数据库了。
cgi程序:



#include
#include
#include
#include
#include
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;
}

搭建简单的http服务器_第7张图片
mod表示操作,where是哪个·表,name是人名。
tbq这个表如下:
搭建简单的http服务器_第8张图片
处理请求的结果:
这里写图片描述
前端知识知之甚少,勿怪啊。。
在这里一个简单的http服务器就搭建完成了,但是我们还可以做很多事。比如,当请求过多时效率太低怎么办?再者,我们可以写一个爬虫程序来提高。除此之外,我们可以在深入调研nginx服务器。
源码地址:https://github.com/fengasdf/-http-(由于整合了协程,python爬虫,以及其他程序,可能有点乱)
http服务器运用协程:
http://blog.csdn.net/fengasdfgh/article/details/76688440

你可能感兴趣的:(网络)