手写精简版TinyHttpd项目(一)

前言:

        我们在之前的TinyHttpd的精读(可以在首页去查看)中已经是基本的了解了显示一个网页的基本过程,那么我们学习后可以通过手写一个精简版的进行巩固下。

0.新工程的建立

        我们也可以顺带复习下如何通过cmake在ubuntu下新建一个工程(记得提前下载cmake)。

        1.新建文件夹My_Tinyhttpd。

        2.新建文件:CMakeLists.txt,service_socket.cpp。

        3.CMakeList.txt的内容:

cmake_minimum_required(VERSION 3.0.0)
project("Main")
add_executable(service service_socket.cpp)

          4.service_socket.cpp:

#include <iostream>
int main(){
    std::cout<<"hello world\n";
    return 1;
}

        5.执行cmake ./ 和 cmake。

                如果没有报错则成果物会被正常的生成出来为service,然后我们执行 ./service 就可以看到控制台输出hello world了。我们的项目就基本构建完成了。接下来就是具体的内容。

1.main函数:

         在这个main函数中我们的目的是新建一个socket链接,然后创建一个线程去接受信息并进行处理。最后当我们处理完信息后我们就关闭socket链接。具体代码如下:

void accept_request(int client_id){
    printf("Get Message...\n");
}
int main(){
    char buffer1[MAXNUM] = {0};
    int service_id,new_socket;
    struct sockaddr_in address;
    socklen_t addlen = sizeof(address);
    //socklen_t addlen;
    // service_id = start(address);
    // int service_id,new_socket;

    service_id = socket(AF_INET,SOCK_STREAM,0);
    //创建socket
    if(service_id == 0){
        printf("socket failed\n");
        return service_id;
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8008);
    printf("Bind...\n");
    //绑定
    if(bind(service_id, (struct sockaddr *)&address, sizeof(address)) < 0){
        printf("bind failed\n");
        printf("Error Message is %s\n",strerror(errno));
        return -1;
    }
    //监听
    printf("Listen...\n");
    if(listen(service_id,3) < 0){
        printf("listen error\n");
        return -1;
    }

    while(1){
        //接受链接:
        int new_socket = accept(service_id,(struct sockaddr*)&address,(socklen_t*)&addlen);
        if(new_socket <0){
            printf("accept client error\n");
        }
        std::thread m_thread(accept_request,new_socket);
        m_thread.detach();
    }
    printf("Close Socket...\n");
    close(service_id);
    return 1;
}

        我们按照这个写完代码后,我们执行make然后运行下service(./service).然后打开浏览器输入

http://localhost:8008/.然后看log是否会打印Get Message...。如果可以的话那么基本上我们的这段代码就基本OK了。然后我们把sokcet通信部分的代码封装在start函数中。

void accept_request(int client_id){
    printf("Get Message...\n");
}

int start(struct sockaddr_in& address){
    int service_id,new_socket;

    service_id = socket(AF_INET,SOCK_STREAM,0);
    //创建socket
    if(service_id == 0){
        printf("socket failed\n");
        return service_id;
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8008);
    printf("Bind...\n");
    //绑定
    if(bind(service_id, (struct sockaddr *)&address, sizeof(address)) < 0){
        printf("bind failed\n");
        printf("Error Message is %s\n",strerror(errno));
        return -1;
    }
    //监听
    printf("Listen...\n");
    if(listen(service_id,3) < 0){
        printf("listen error\n");
        return -1;
    }
    return service_id;
}

int main(){
    char buffer1[MAXNUM] = {0};
    int service_id,new_socket;
    struct sockaddr_in address;
    socklen_t addlen = sizeof(address);
    //socklen_t addlen;
    service_id = start(address);

    while(1){
        //接受链接:
        int new_socket = accept(service_id,(struct sockaddr*)&address,(socklen_t*)&addlen);
        if(new_socket <0){
            printf("accept client error\n");
        }
        std::thread m_thread(accept_request,new_socket);
        m_thread.detach();
    }
    printf("Close Socket...\n");
    close(service_id);
    return 1;
}

然后我们同样可以去验证下看看是否会有Get Message...信息。

2.accept_reques函数

        基本的sokcet通讯已经完成了我们接下来进行数据分析/处理函数的部分。这部分函数会涉及到别的函数,我们暂时先聚焦于这个函数的主体思路。

        主体思路:从我们的socket中提出method方法,来判断我们是显示静态网页和动态网页。

        基于以上思路,我们首先来看下我们从socket反馈中能获取到啥信息,这样才方便我们后面写代码的思路。

void print_buffer(char buffer[]){
    for(int i = 0;i < MAXNUM;i++){
        if(buffer[i] != '\0')
            std::cout<<buffer[i];
    }
}

void accept_request(int client_id){
    char buffer_line[MAXNUM] = {0};
    char buffer[MAXNUM] = {0};
    char method[MAXNUM] = {0};
    char path[MAXNUM] = {0};
    int i = 0,j = 0;
    printf("Get Message...\n");
    auto getmessage = read(client_id,buffer,sizeof(buffer));
    if(getmessage <= 0){
        printf("wait client message\n");
    } else {
        std::cout<<"client say :";
        print_buffer(buffer);
    }
}

        结果如下:

我们发现我们要获取的信息(GET OR POST)是在这个信息的第一行,所以我们第一步得先获取第一行,然后再这行信息中获取GET或者POST方法。

2.1get_line函数:

void get_line(char buffer1[],char buffer2[]){
    int i = 0;
    //buffer1 = {0};
    while(buffer2[i] != '\n'){
        buffer1[i] = buffer2[i];
        i++;
    }
    buffer1[i] = '\0';
}

        思路:以换行符为行数的确定标记,来将第一行复制出来。

2.2获取method

        当我们获取到第一行后我们就需要考虑当前我们是收到的GET还是POST方法呢?从我们的获取到的buffer信息中可以看到当前网页给的第一次是GET方法。那么从一行字符串中获取到一个子串的方法就不在详细赘述了,直接看代码即可。

void accept_request(int client_id){
    char buffer_line[MAXNUM] = {0};
    char buffer[MAXNUM] = {0};
    char method[MAXNUM] = {0};
    char path[MAXNUM] = {0};
    int i = 0,j = 0;
    printf("Get Message...\n");
    auto getmessage = read(client_id,buffer,sizeof(buffer));
    if(getmessage <= 0){
        printf("wait client message\n");
    } else {
        std::cout<<"client say :";
        print_buffer(buffer);
    }
    
    get_line(buffer_line,buffer);
    printf("accept_request:");
    print_buffer(buffer_line);
    // //get method

    while(buffer_line[i] != ' '){
        method[i] = buffer_line[i];
        i++;
    }
    print_buffer(method);
}

2.3判断当前的method方法

        这个就是直接判断是GET还是POST方法,如果是GET方法,我们就显示静态网页,如果是POST我们就显示动态网页。如果两者都不是我们就直接报错。

void accept_request(int client_id){
    char buffer_line[MAXNUM] = {0};
    char buffer[MAXNUM] = {0};
    char method[MAXNUM] = {0};
    char path[MAXNUM] = {0};
    int i = 0,j = 0;
    printf("Get Message...\n");
    auto getmessage = read(client_id,buffer,sizeof(buffer));
    if(getmessage <= 0){
        printf("wait client message\n");
    } else {
        std::cout<<"client say :";
        print_buffer(buffer);
    }
    get_line(buffer_line,buffer);
    printf("accept_request:");
    print_buffer(buffer_line);
    //get method

    while(buffer_line[i] != ' '){
        method[i] = buffer_line[i];
        i++;
    }
    print_buffer(method);
    if(strcasecmp(method,"GET") && strcasecmp(method,"POST")){
        printf("error Method");
    }
    if(strcasecmp(method,"GET") == 0){
        headers(client_id);
        saver_file(client_id);
    }
}

至此,一个基本完整的accept_request函数就基本完成了。那么下一章我们就来处理网页的显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值