认识http的方法、Header、状态码以及简单实现一个http的业务逻辑
文章目录
- http的方法
- http状态码
- http重定向
- http常见Header
- 实现简单业务逻辑
- Protocol.hpp
- Util.hpp
- Server.hpp
- Server.cc
- 效果
http的方法
方法 | 说明 | 支持的HTTP版本 |
---|---|---|
GET | 获取资源 | 1.0/1.1 |
POST | 传输实体主体 | 1.0/1.1 |
PUT | 传输文件 | 1.0/1.1 |
HEAD | 获得报文首部 | 1.0/1.1 |
DELETE | 删除文件 | 1.0/1.1 |
OPTIONS | 询问支持方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINE | 断开连接关系 | 1.0 |
其中最为常见的请求方法为:GET POST
事实上,浏览器向服务器进行数据提交时,本质是前端通过form表单提交的,浏览器会自动将form表单中的内容转换为GET/POST的方法请求
例如在QQ的网址上会有登陆框,查看登陆框的源代码就会发现有form表单
如果输入了账号密码之后点击了登陆按钮,浏览器就会将账号和密码根据指定的GET或者POST方法发送给服务器。
其中两者的区别有:
- GET方法会将获取到的数据作为参数直接通过url 传递,也就是说GET方法会在url 上直接显示出数据,格式为:http://ip:port/XXX/YY?name=value&name2=value2。会直接暴露出数据
- POST方法不是通过url 传递数据,而是直接向请求的正文里提交数据。也就是说参数会存在在正文里,服务器再从正文里提取参数
- 因为GET方法是再url中直接传递参数,所以参数不能太大
- POST在正文传递参数,所以可以参数很大
需要注意的是:
虽然POST方法不会暴露数据,但是并不意味着就是安全的。私密 != 安全。
如果要谈到安全,那就必须要加密,加密内容属于https协议
http状态码
类别 | 原因 | |
---|---|---|
1XX | informational - 信息性状态码 | 接受的请求正在处理 |
2XX | success - 成功状态码 | 请求正常处理完毕 |
3XX | redirection - 重定向状态码 | 需要进行附加操作以完成请求 |
4XX | client error - 客户端错误状态码 | 服务器无法处理请求 |
5XX | server error - 服务端错误状态码 | 服务器处理请求出错 |
其中最常见的就是 404 网页不存在
200 代表OK,404 Not Found,403 Forbiden,302 Redirect 重定向,504 Bad Gateway
http重定向
要实现重定向其实很简单,将状态码修改为307代表重定向,然后在正文里加入重定向的网址即可,这样向服务器请求后,服务器就会将处理的请求重定向到指定的网址
// 服务端处理的回调函数
bool func(const HttpRequest &req, HttpResponse &res)
{// 打印方便调试查看接收到的数据是否正确cout << "---------------http--------------" << endl;cout << req._inbuffer;cout << "_method: " << req._method << endl;cout << " _url: " << req._url << endl;cout << " _httpversion: " << req._httpversion << endl;cout << " _path: " << req._path << endl;cout << " _suffix: " << req._suffix << endl;cout << " _size: " << req._size << endl;cout << "---------------end---------------" << endl;// 状态行// string resline = "HTTP/1.1 200 OK\r\n";string resline = "HTTP/1.1 307 Temporary Redirect\r\n";// 响应报头// 需要注意正确的给客户端返回资源,图片是图片,网页是网页string rescontet = Util::suffixToDesc(req._suffix);// 添加资源长度到报头rescontet += "Content-Length: ";rescontet += to_string(req._size);rescontet += "\r\n";// 添加重定向rescontet += "Location: https://www.qq.com/\r\n";// 空行string resblank = "\r\n";// 响应正文string body;// 判断资源是否存在,不存在就返回错误状态码 - 404if (!Util::FileIsNo(req._path, &body)){Util::FileIsNo(errorhtml, &body);}// 写回响应的数据,后续要发送回客户端res._outbuffer += resline;res._outbuffer += rescontet;res._outbuffer += resblank;res._outbuffer += body;return true;
}
http常见Header
名称 | 意义 |
---|---|
Content-Type | 数据类型(text/html等) |
Content-Length | Body的长度 |
Host | 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上 |
User-Agent | 声明用户的操作系统和浏览器版本信息 |
referer | 当前页面是从哪个页面跳转过来的 |
location | 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问 – 重定向 |
Cookie | 用于在客户端存储少量信息. 通常用于实现会话(session)的功能 |
实现简单业务逻辑
代码里涉及html代码,不详细讲解
由于服务器较弱,所以图片获取直接从网址获取,不从服务器读取
需要注意,一个网页看到的结果,可能是有多个资源组合而成,例如有网页,图片,视频等。所以要获取一张完整的网页效果需要浏览器发送多次请求,那么服务器就要根据请求的类型不同对响应正文处理的方式要指明Content-Type的类型。例如网页为“text/html”,jpg格式的图片为“image/jpeg”,不同的格式可自行搜索
判断格式的方法可以根据 url 中资源的后缀进行判断
以下代码均有注释:
Protocol.hpp
请求响应类
因为浏览器会自动处理收到的响应报文,所以不需要编写处理方法只需要将响应报文发送回浏览器即可
#pragma once#include <iostream>
#include <string>
#include <sstream>
#include "Util.hpp"using namespace std;// 定义分隔符
#define sep "\r\n"
#define default_root "./wwwroot"
#define home_page "index.html"
#define errorhtml "./wwwroot/404.html"// 请求
class HttpRequest
{
public:string _inbuffer; // 接收请求数据string _method; // 处理数据方法的名称string _url; // urlstring _httpversion; // http协议版本string _path; // 查找资源的路径string _suffix;// 资源后缀int _size;//资源长度HttpRequest(){}// 处理收到的数据// 添加默认路径void parse(){// 拿到第一行string line = Util::GetOneLine(_inbuffer, sep);if(line.empty())return;cout << "line: " << line << endl;// 拿到第一行中的三个字段stringstream ss(line);ss >> _method >> _url >> _httpversion;// 添加默认路径_path = default_root;_path += _url;// 如果url为/ 则添加默认路径if(_path[_path.size() - 1] == '/')_path += home_page;// 获取资源的后缀auto pos = _path.rfind(".");if(pos == string::npos)_suffix = ".html";else_suffix = _path.substr(pos);// 获取到长度_size = Util::GetLen(_path);}};// 响应
class HttpResponse
{
public:string _outbuffer;
};
Util.hpp
工具类,将共有的方法定义同个类,方便调用
#pragma once#include <iostream>
#include <string>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>using namespace std;class Util
{
public:// 提取并删除首行// 读到的首行并不需要处理static string GetOneLine(string &inbuffer, const string &sep){auto pos = inbuffer.find(sep);if (pos == string::npos)return "";string sub = inbuffer.substr(0, pos);inbuffer.erase(0, sub.size() + sep.size());return sub;}// 判断请求的资源是否存在static bool FileIsNo(const string resource, string *out){ifstream in(resource);// 打开文件失败说明资源不存在if (!in.is_open())return false;string line;while (getline(in, line))*out += line;in.close();return true;}// 根据后缀指明响应报头类型static string suffixToDesc(const string &suffix){string st = "Content-Type: ";if (suffix == ".html")st += "text/html";else if (suffix == ".jpg")st += "image/jpeg";st += "\r\n";return st;}// 获取资源的长度static int GetLen(const string &path){struct stat s;int n = stat(path.c_str(), &s);if(n == 0)return s.st_size;return -1;}
};
Server.hpp
#pragma once#include "Protocol.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <sys/wait.h>
#include <unistd.h>using func_t = function<bool(const HttpRequest &, HttpResponse &)>;class Server
{
public:Server(func_t func, uint16_t &port): _port(port), _func(func){}void Init(){// 创建负责监听的套接字 面向字节流_listenSock = socket(AF_INET, SOCK_STREAM, 0);if (_listenSock < 0)exit(1);// 绑定网络信息struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_listenSock, (struct sockaddr *)&local, sizeof(local)) < 0)exit(3);// 设置socket为监听状态if (listen(_listenSock, 5) < 0)exit(4);}// 服务端读取处理请求方法void HttpHandler(int sock){// 确保读到完整的http请求char buffer[4096];size_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);HttpRequest req;HttpResponse res;if (n > 0){buffer[n] = 0;req._inbuffer = buffer;// 处理读到的数据req.parse();// 调用回调方法反序列化请求并得到响应结果和序列化响应结果_func(req, res);// 发回客户端send(sock, res._outbuffer.c_str(), res._outbuffer.size(), 0);}}void start(){while (1){// server获取建立新连接struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));socklen_t len = sizeof(peer);// 创建通信的套接字// accept的返回值才是真正用于通信的套接字_sock = accept(_listenSock, (struct sockaddr *)&peer, &len);if (_sock < 0)continue;cout << "sock: " << _sock << endl;// 利用多进程实现pid_t id = fork();if (id == 0) // child{close(_listenSock);// 调用方法包括读取、反序列化、计算、序列化、发送HttpHandler(_sock);close(_sock);exit(0);}close(_sock);// fatherpid_t ret = waitpid(id, nullptr, 0);}}private:int _listenSock; // 负责监听的套接字int _sock; // 通信的套接字uint16_t _port; // 端口号func_t _func;
};
Server.cc
#include "Server.hpp"
#include <memory>// 输出命令错误函数
void Usage(string proc)
{cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";
}// 服务端处理的回调函数
bool func(const HttpRequest &req, HttpResponse &res)
{// 打印方便调试查看接收到的数据是否正确cout << "---------------http--------------" << endl;cout << req._inbuffer;cout << "_method: " << req._method << endl;cout << " _url: " << req._url << endl;cout << " _httpversion: " << req._httpversion << endl;cout << " _path: " << req._path << endl;cout << " _suffix: " << req._suffix << endl;cout << " _size: " << req._size << endl;cout << "---------------end---------------" << endl;// 状态行string resline = "HTTP/1.1 200 OK\r\n";// string resline = "HTTP/1.1 307 Temporary Redirect\r\n";// 响应报头// 需要注意正确的给客户端返回资源,图片是图片,网页是网页string rescontet = Util::suffixToDesc(req._suffix);// 添加资源长度到报头rescontet += "Content-Length: ";rescontet += to_string(req._size);rescontet += "\r\n";// // 添加重定向// rescontet += "Location: https://www.qq.com/\r\n";// 空行string resblank = "\r\n";// 响应正文string body;// 判断资源是否存在,不存在就返回错误状态码 - 404if (!Util::FileIsNo(req._path, &body)){Util::FileIsNo(errorhtml, &body);}// 写回响应的数据,后续要发送回客户端res._outbuffer += resline;res._outbuffer += rescontet;res._outbuffer += resblank;res._outbuffer += body;return true;
}int main(int argc, char *argv[])
{// 启动服务端不需要指定IPif (argc != 2){Usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);unique_ptr<Server> server(new Server(func, port));// 服务端初始化server->Init();// 服务端启动server->start();return 0;
}
效果
在此就不写出html的文件了,看着效果能够实现即可
可以看到浏览器向服务器发送请求,服务器返回响应,响应里就包括了自己编写的html文件,所以浏览器处理后就显示出了自己的网页
相关文章:

认识http的方法、Header、状态码以及简单实现一个http的业务逻辑
文章目录 http的方法http状态码http重定向http常见Header实现简单业务逻辑Protocol.hppUtil.hppServer.hppServer.cc 效果 http的方法 方法说明支持的HTTP版本GET获取资源1.0/1.1POST传输实体主体1.0/1.1PUT传输文件1.0/1.1HEAD获得报文首部1.0/1.1DELETE删除文件1.0/1.1OPTIO…...
Faiss在windows下安装和使用
pip install faiss-cpu 直接安装可能出现问题: error: command swig.exe failed: No such file or directory 安装swig即可解决,安装方式...

【JavaEE进阶】SpringBoot项目的创建
文章目录 一. SpringBoot简介1. 什么是SpringBoot?2. SpringBoot的优点 二. SpringBoot项目创建1. 使用IDEA创建2. 使用网页创建SpringBoot项目 三. 运行SpringBoot项目 一. SpringBoot简介 1. 什么是SpringBoot? Spring Boot 是一个用于快速构建基于 Spring 框架的应用程序…...
c++二进制转化十进制代码(小数)
#include <bits/stdc.h> using namespace std; int mid; double er_shi(string a){int lena;double sum0;int p0;int q-1;int yn1;//判断是否小数 lenaa.length();//字符串长度 for(int i0;i<lena;i){if(a[i].){midi;yn0;break;} }if(yn0){for(int jmid-1;j>0;j--…...
07_ansible, 条件选择、加载客户事件、在roles和includes上面应用’when’语句、条件导入、基于变量选择文件和模版、注册变量
10.条件选择 10.1.When语句 10.2.加载客户事件 10.3.在roles和includes上面应用’when’语句 10.4.条件导入 10.5.基于变量选择文件和模版 10.6.注册变量 10.条件选择 转自:http://www.ansible.com.cn/docs/playbooks_conditionals.html#id3 常常来说,一个play的…...

4个简化IT服务台任务的ChatGPT功能
最近几个月,ChatGPT 风靡全球,这是一个 AI 聊天机器人,使用户能够生成脚本、文章、锻炼图表等。这项技术在各行各业都有无穷无尽的应用,在本文中,我们将研究这种现代技术如何帮助服务台团队增强服务交付和客户体验。 什…...

群晖7.X版安装cpolar内网穿透
群晖7.X版安装cpolar内网穿透套件 文章目录 群晖7.X版安装cpolar内网穿透套件前言1. 下载cpolar的群晖系统套件1.1 在“套件中心” 选择“手动安装”1.2 完成套件安装 2. 进入cpolar软件信息页3. 点击“免费注册”轻松获得cpolar账号 前言 随着群晖系统的更新换代,…...

[保研/考研机试] KY183 素数 北京航空航天大学复试上机题 C++实现
题目链接: 素数https://www.nowcoder.com/share/jump/437195121691718444910 描述 输入一个整数n(2<n<10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1。 输入描述: 输入有多…...

Java基础入门篇——IDEA开发第一个入门程序(五)
目录 一、IDEA层级结构分类 二、IDEA层级结构介绍 三、IDEA层级关系 四、创建IDEA中的第一个代码 一、IDEA层级结构分类 IntelliJ IDEA的项目结构主要分为以下几个层级: Project: 项目Module: 模块Package: 包Class: 类 一个项目里面…...

系统学习Linux-Redis基础
一、redis概述 NoSQL(非关系型数据库、内存存储) 类型 文档型数据库(Document-oriented database)如MongoDB; 列族数据库(Column-family database)如HBase、Cassandra等; 图形数…...
实现缓存el-table分页大小,用户新建标签打开该页面需保持分页大小(考虑是否为嵌入式页面)
需求:每个表格的分页大小 以本地缓存的方式存到浏览器本地,然后用户下次打开的时候 获取这个本地存储的值 如果没有就用页面默认的值,如果有 则先判断是不是有效的(是) 无效用默认 有效就用这个缓存值,需要区分是否为嵌入式页面 分析…...

056B R包ENMeval教程-基于R包ENMeval对MaxEnt模型优化调参和结果评价制图(更新)
056B-1 资料下载 056B-2 R包ENMeval在MaxEnt模型优化调参中的经典案例解读 056B-3 R软件和R包ENMeval工具包安装 056B-4 R软件和R包ENMeval安装报错解决办法 056B-5 环境数据格式要求和处理流程 056B-6 分布数据格式要求和处理流程 056B-7 基于R包ENMeval对MaxEnt模型优化…...

MySQL_数据库的DDL语句(表的创建与修改)
DDL 数据库操作 查看当前有哪些数据库 SHOW databases;#查看哪些数据库查询当前数据库 SELECT database();创建数据库 create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序 规则 ] ;创建一个sycoder数据库, 使用数据库默认的字符集 CREATE…...
常见面试题:字节序判别和转换
在计算机中,字节序指的是多字节数据的存储顺序。最常见的字节序有两种:大端字节序(Big-Endian)和小端字节序(Little-Endian)。 大端字节序是指最高有效位(Most Significant Bit,简称…...
Maxwell与canal工具对比
Maxwell和Canal是两种不同的数据同步工具,都是在数据迁移、数据同步、数据分发等领域发挥作用的工具,但是它们之间存在一些差异。 Maxwell Maxwell是一种开源的MySQL数据库同步工具,它可以将MySQL数据库的binlog转化为JSON格式,…...
SpringBoot——如何运行Python脚本并返回数据
文章目录 运行python脚本并输出如何输入参数 运行python脚本并输出 要让Python脚本返回数据给调用它的Java代码,你可以在Python脚本中使用打印或输出函数将结果输出到标准输出(stdout)中。 以下是一个示例,在Python脚本中返回数…...

在生产环境中部署Elasticsearch:最佳实践和故障排除技巧———索引与数据上传(二)
前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 「推荐专栏」: ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄,vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄ÿ…...

R语言初学者书籍推荐
Home | Bookdown 这个网站上有很多R语言的书籍,并且一直在更新,阅读起来没有难度。 今天搜索材料的时候,检索到下面这本书: 有输入,才会有输出。...
Taro+Vue3,点击按钮把另一个页面分享出去
useShareAppMessage 监听用户点击页面内转发按钮(Button 组件 openTypeshare)或右上角菜单“转发”按钮的行为,并自定义转发内容。等同于 onShareAppMessage 页面生命周期钩子。 使用时,必须为页面配置 enableShareAppMessage…...
CSS基础:学习CSS样式的基本语法和应用,了解如何美化网页。
CSS(层叠样式表)是一种用于描述网页上元素(例如文字、图像、背景等)外观和布局的样式语言。通过使用CSS,您可以控制和改变网页的外观,使其更具吸引力和易于使用。 下面是一些CSS基础知识和常用的语法&#…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...