手写精简版TinyHttpd项目(一)

news2024/11/24 20:28:08

前言:

        我们在之前的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.coloradmin.cn/o/1836544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安装,管理程序

文章目录 Linuxd应用程序基础应用程序与系统命令的关系 典型应用程序目录常见的软件包装类型 rpm软件包管理工具RPM软件包rpm命令格式查询rpm软件包信息查询已安装的查询未安装的 安装或升级rpm软件卸载指定rpm软件辅助选项 维护RPM数据库解决软件包依赖关系方法 源代码编译安装…

C#观察者模式应用

目录 一、什么是观察者模式 二、C#中观察者模式的实现 三、两种实现的用法 1、事件与委托 2、IObserver和IObservable 四、参考文献 一、什么是观察者模式 观察者&#xff08;Observer&#xff09;模式的定义&#xff1a;指多个对象间存在一对多的依赖关系&#xff0c;当…

嵌入式中间件_3.嵌入式中间件的一般架构

根据嵌入式中间件的不同类型和其应用对象的不同&#xff0c;其架构也有所不同&#xff0c;通常嵌入式中间件没有统一的架构&#xff0c;这里仅仅列举两种中间件架构。 1.消息中间件 1.1消息中间件原理架构 消息中间件是消息传输过程中保存消息的一种容器。它将消息从它的源中…

Swift开发——索引器扩展

扩展用于向已存在的类型(例如,类、结构体、枚举和协议等)中添加新的功能,扩展甚至可以向系统类型(包括无法查阅代码的类型)中添加新的功能,但是扩展不能覆盖原类型中已有的方法,扩展也不能向类中添加新的存储属性。 01、索引器扩展 扩展可为类、结构体等类型添加索引器。程序段…

【热门开源项目推荐】满足不同程序员的需求与关注点

目录 前言一、热门开源项目介绍二、使用开源热门项目的优势&#xff08;一&#xff09;经济方面&#xff08;二&#xff09;技术方面&#xff08;三&#xff09;社区支持及协作方面 三、程序员选择项目模型建议&#xff08;一&#xff09;关键步骤&#xff08;二&#xff09;示…

技术点梳理0618

ann建库&#xff0c;分布式建库&#xff0c;性能优化&#xff0c;precision recall参数优化 hnsw&#xff0c;图索引 1. build a&#xff09;确定层&#xff1a;类似跳表思路建立多层&#xff0c;对每一个插入的节点&#xff0c;random层号l&#xff0c;从图的起始点search_…

【CVPR2021】LoFTR:基于Transformers的无探测器的局部特征匹配方法

LoFTR&#xff1a;基于Transformers的局部检测器 0. 摘要 我们提出了一种新的局部图像特征匹配方法。我们建议先在粗略级别建立像素级密集匹配&#xff0c;然后再在精细级别细化良好匹配&#xff0c;而不是按顺序进行图像特征检测、描述和匹配。与使用成本体积搜索对应关系的密…

vcruntime140_1.dll文件【安装包】【压缩包】【文件】【下载】

安装程序时有时候出现 类似无法启动程序&#xff0c;缺少vcruntime140_1.dll的提示&#xff0c;我们找到该文件并放到对应目录就可以&#xff1b;获取方法有很多&#xff0c;下面介绍两种&#xff1a;&#xff08;方法二更简便&#xff0c;不过建议两种方法都试试&#xff09; …

Java开发的构建神器:Maven以及如何安装部署Maven

目录 一、Maven引言1.1 Maven的核心概念✍. POM (Project Object Model)✌. 依赖管理✍. 生命周期与构建阶段✌. 插件系统 1.2 Maven的工作流程✍. 读取POM文件&#xff1a;✌. 依赖解析&#xff1a;✍. 构建生命周期&#xff1a;✌. 插件执行&#xff1a;✍. 构建输出&#xf…

shell编程中的运算符的讲解

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

基于minhook的Windows HOOK

MinHook是一个基于微软Detours技术的可移植Hook库&#xff0c;它允许开发者在运行时更改函数定义&#xff0c;而无需修改原始函数代码。以下是关于MinHook的详细介绍&#xff1a; 基本概念 定义&#xff1a;MinHook使用内存污染和跳转技术来实现Hook&#xff0c;使得开发者能…

【windows|002】WEB服务和域名介绍

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

【记录46】【案例】echarts 柱状图

echarts环境4.1.0 <template><div id"threefour"></div> </template> <script> import * as echarts from "echarts" export default {name:"",components:{},data(){return {}},methods:{getdata(){var myChart…

【Java】解决Java报错:SocketTimeoutException during Network Communication

文章目录 引言一、SocketTimeoutException的定义与概述1. 什么是SocketTimeoutException&#xff1f;2. SocketTimeoutException的常见触发场景3. 示例代码 二、解决方案1. 合理设置超时时间2. 使用重试机制3. 使用NIO和异步通信4. 使用高层次的网络通信库 三、最佳实践1. 合理…

使用vscode插件du-i18n处理前端项目国际化翻译多语言

前段时间我写了一篇关于项目国际化使用I18n组件的文章&#xff0c;Vue3 TS 使用国际化组件I18n&#xff0c;那个时候还没真正在项目中使用&#xff0c;需求排期还没有定&#xff0c;相当于是预研。 当时就看了一下大概怎么用&#xff0c;改了一个简单的页面&#xff0c;最近需…

Matplotlib(小案例)

1、3D表面形状的绘制 from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np import matplotlib as mplfigplt.figure() axfig.add_subplot(111,projection3d)unp.linspace(0,2*np.pi,100) vnp.linspace(0,np.pi,100) x10*np.outer(n…

python+unity手势控制地球大小

效果图如下 具体操作如下 1 在unity窗口添加一个球体 2 给球体添加材质,材质图片使用地球图片 地球图片如下 unity材质设置截图如下 3 编写地球控制脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class test : MonoBehavio…

codeforces round 953 div2

A Alice and books 题目&#xff1a; 思路&#xff1a;编号最大的肯定会被读到&#xff0c;所以在编号最大的这一组书中不能存在除去最大编号的外书外页数最大的书&#xff0c;并且在另一堆中这本书的编号也应该是最大值 代码&#xff1a; #include <iostream>using…

从“小IPD”到“大IPD”

IPD体系实施或变革是一个长期的系统工程&#xff0c;业界经常有从“小IPD”到“大IPD”的说法。 像华为当年就是从“产品开发流程”开始推行&#xff0c;算是很小范围的“小IPD”了&#xff0c;后面逐步扩大为“大IPD”&#xff0c;如上图所示。其中前端的市场管理&#xff08;…

【讲解下Pip换源】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…