手写精简版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函数就基本完成了。那么下一章我们就来处理网页的显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/721127.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

统计分析方法-非参数检验-python

文章目录 前言非参数检验特点常见的非参数检验一、Cliffs Delta动机定义二、Wilcoxon Signed-Rank Test定义三、 Friedman检验适用场景公式python 代码Wilcoxon Signed-Rank Test和 cliffs deltaFriedman前言 记录一下自非参数检验的学习过程,如有不对请纠正。 非参数检验 …

ESP32蓝牙BLE连接米家温湿度计

ESP32蓝牙BLE连接米家温湿度计 文章目录 ESP32蓝牙BLE连接米家温湿度计简介需要准备的东西软件调试代码实现修改查找的名称 修改需要连接的服务和属性添加解析数据的代码 上电演示提示 简介 最近在学习低功耗蓝牙BLE(Bluetooth Low Energy)&#xff0c;刚好手里有个米家蓝牙温…

如何将Postman API测试转换为JMeter以进行扩展

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。​编辑https://…

递归与回溯 || 排列问题

目录 前言&#xff1a; 全排列 题解&#xff1a; 全排列 II 题解&#xff1a; 子集 题解&#xff1a; 组合 题解&#xff1a; 组合总和 题解&#xff1a; 电话号码的字母组合 题解&#xff1a; 字母大小写全排列 题解&#xff1a; 优美的排列 题解&#xff1a;…

Hbase搭建教程

Hbase搭建教程 期待您的关注 ☀小白的Hbase学习笔记 目录 Hbase搭建教程 1.上传hbase的jar包并解压 2.重新登录 3.启动zookeeper 4.配置环境变量 5.关闭ZK的默认配置 6.修改hbase-site.xml文件 7.修改regionservers文件 8.将配置好的文件分发给其它节点 9.配置环境变量…

shell编程中的运算符的讲解

在Linux操作系统中也可以使用expr来进行一些数值的运算&#xff0c;expr接受表达式作为参数&#xff0c;并打印计算结果。 对于某些复杂的表达式或早期不支持内嵌算术表达式的Shell环境&#xff0c;expr 仍然是一个可行的选择。 如上图所示&#xff0c;是使用变量sum来承接加和…

【算法训练记录——Day31】

Day31——贪心算法Ⅰ 1. 理论1.1 什么是贪心1.2 什么时候用贪心1.3 贪心算法一般步骤 2.leetcode455——分发饼干3.leetcode376——摆动序列 目标&#xff1a; 理论leetcode455——分发饼干leetcode376——摆动序列leetcode53 —— 最大字序和 1. 理论 算法随想录——贪心 1…

四十七、openlayers官网示例Image Filters——给地图添加锐化、浮雕、边缘等滤镜效果

官网demo示例&#xff1a; Image Filters 这篇讲的是如何给地图添加滤镜。 一看代码&#xff0c;&#xff0c;好家伙&#xff0c;信息量满满&#xff0c;全都看不懂。。。 咱只能一段一段扒。。。 首先添加一个底图到地图上&#xff0c;这个好理解。 const imagery new Til…

第十一章:接口

接口 文章目录 接口一、简介1.1 接口是什么1.2 接口的作用1.3 接口的开发与调用1.4 接口的组成 二、RESTful API三、json-server四、接口测试工具五、接口的创建 一、简介 1.1 接口是什么 接口是前后端通信的桥梁 简单理解&#xff1a;一个接口就是 服务中的一个路由规则&am…

十分钟学会微调大语言模型

有同学给我留言说想知道怎么训练自己的大语言模型&#xff0c;让它更贴合自己的业务场景。完整的大语言模型训练成本比较高昂&#xff0c;不是我们业余玩家能搞的&#xff0c;如果我们只是想在某个业务场景或者垂直的方面加强大模型的能力&#xff0c;可以进行微调训练。 本文…

ssl证书能认证多少个域名

SSL证书能认证的域名数量取决于SSL证书的类型。不同类型的SSL证书支持不同数量的域名&#xff1a; SSL证书&#xff0c;作为网络安全的基石之一&#xff0c;起着至关重要的作用。它通过为网站提供加密连接&#xff0c;确保数据传输的安全性和完整性&#xff0c;同时验证网站的真…

spark常见问题

写文章只是为了学习总结或者工作内容备忘&#xff0c;不保证及时性和准确性&#xff0c;看到的权当个参考哈&#xff01; 1. 执行Broadcast大表时&#xff0c;等待超时异常&#xff08;awaitResult&#xff09; 现象&#xff1a;org.apache.spark.SparkException: Exception…

答应我,完成单位投稿任务用对的方法别让自己受投稿之苦

在这个信息爆炸的时代,单位的形象塑造与品牌传播已成为不可忽视的关键环节。作为单位的信息宣传员,我深知每一次对外发声的重要性,它不仅是展示我们工作成果的窗口,更是连接公众、塑造品牌形象的桥梁。然而,在传统的投稿方式中,尤其是依赖于邮箱投稿,我经历了太多次的挫败与无奈…

LeetCode 算法:合并两个有序链表 c++

原题链接&#x1f517;&#xff1a;合并两个有序链表 难度&#xff1a;简单⭐️ 题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;…

PHP学习笔记--初学

笔录&#xff1a;我是从黑马里面找的资料学习的&#xff0c;如果有人看我的笔记话&#xff0c;建议去看黑马程序课程&#xff0c;更详细一些。 目录 php定义&#xff0c;平台支持&#xff1a; 静态网站&#xff1a; 动态网站&#xff1a; 服务器概念&#xff1a; IP的概念…

JavaFX 分隔符

Separator类表示水平或垂直分隔线。它分割元素&#xff0c;不产生任何动作。 我们可以设计风格&#xff0c;应用视觉效果&#xff0c;并为分隔符设置动画。 默认情况下&#xff0c;分隔符是水平的。我们可以使用setOrientation方法改变它的方向。 Separator类扩展了Node类。…

2024/6/18(RBAC,查询用户权限,细粒度授权,选课,支付宝生成二维码支付,支付结果查询需要内网穿透)

黑马程序员【学成在线项目】,P141 测试沙箱支付宝_黑马学成在线支付宝沙箱-CSDN博客 需要内网穿透

reverse-android-实战喜马拉雅-ollvm

资料 1. apk: com.ximalaya.ting.android.apk. 2020年8月 可以使用 2. 抓包分析 java层分析 so层分析 登录的算法so是在 liblogin_encrypt.so中。 32位的&#xff0c; 用 IDA打开&#xff0c;查看 静态的导出函数。 打开 一个 首先看到 IDA VIEW 是一个横向 比较多的分支&am…

【3D模型库】机械三维模型库整理

1 开拔网 简介&#xff1a;开拔网是中国较早的机械设计交流平台&#xff0c;广受行业内的各个大学&#xff0c;公司以及行业人士的欢迎。网站有非常丰富的3D模型&#xff0c;CAD图纸&#xff0c;以及各类热门软件的下载。同时我们也为行业搭建一个平台&#xff0c;提供各类设计…

AI智能盒子助力中钢天源设备工厂升级安全防护

中钢集团安徽天源科技股份有限公司成立于2002年3月27日,是中央企业中国中钢股份有限公司控股的上市公司&#xff0c;主导产品为永磁铁氧体器件、钕铁硼器件、四氧化三锰、锶铁氧体预烧料及各类磁选机等。 在中钢天源智能化升级过程中&#xff0c;采用并定制开发一系列厂区安全…