承德做网站的公司/sem优化服务公司
从零开始学习http协议
- 1 什么是http协议
- 2 认识URL
- 3 http的请求和应答
- 3.1 服务端设计
- 3.2 如何让外界可以访问Linux云服务器
- 3.3 运行测试
- 4 理解http请求与应答
- 4.1 宏观理解
- 4.2 http请求反序列化
1 什么是http协议
虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用。 HTTP(超文本传输协议)就是其中之一。http应用十分的广泛,几乎每一名程序员(无论前后端 无论C++/Java/Go…)都会接触到!
在互联网世界中, HTTP(HyperText Transfer Protocol, 超文本传输协议) 是一个至关重要的协议。 它定义了客户端(如浏览器) 与服务器之间如何通信, 以交换或传输超文本,超文本支持视频,网页 ,图片等等!
HTTP 协议是客户端与服务器之间通信的基础。 客户端通过 HTTP 协议向服务器发送请求, 服务器收到请求后处理并返回响应。 HTTP 协议是一个无连接、 无状态的协议, 即每次请求都需要建立新的连接, 且服务器不会保存客户端的状态信息。
但是有个疑问?http是基于TCP协议的,也就是面向连接的,为什么http确是无连接的协议呢?
因为http会使用Tcp建立的链接,无需再次建立新的链接。就好比之前我们实现的网络计算器,服务端和客户端的连接是通过TCP建立的,但是通信传输Request和Response直接通过Tcp建立的连接即可,无需再次建立连接!
2 认识URL
平时我们浏览的网站:百度 , 哔哩哔哩 ,力扣…等等网站都有一个域名
百度 :https://www.baidu.com/
哔哩哔哩:https://www.bilibili.com/
力扣:https://leetcode.cn/
...
这些网址都是https协议,这些网址其实就是URL!
访问时会将网址解析成IP地址!一般成熟的协议名称与端口号是强关联的,称之为知名端口号!
- HTTP (Hypertext Transfer Protocol):端口号:80 ,用于在Web服务器和客户端之间传输网页。
- HTTPS (HTTP Secure):端口号:443, HTTP的安全版本,通过SSL/TLS加密传输数据。
- FTP (File Transfer Protocol):
- 控制端口:21 ,用于文件传输。
- 数据端口:20,(主动模式)或随机端口(被动模式)
- SSH (Secure Shell):端口号:22 ,用于安全地访问远程服务器。
为什么平时访问网站并没有输入端口号?
只有同时具备IP地址和端口号才可以访问到对应的服务器,浏览器发起请求时会自动拼接端口号80!就类似日常生活中报警会自觉想到拨打110 , 火灾会自然的想到拨打119!
通信中离不开“资源”两个字,通信要么是从别处获取资源,要么是向对方发送资源。http协议下的资源是超文本!
网页,图片,音频,视频都是超文本!在进行通信之前,用户想要获取的资源都在后端的云服务器中,云服务器一般都是Linux系统,那么在Linux视角下不就都是文件吗!
为了将这个文件(资源)发给客户端,就必须要找到这个文件,那么怎么找到这个文件呢?当然是通过文件的唯一标识符 — 路径来实现!在URL中后半部分不就是我们的路径吗!这样通过IP地址确定的唯一主机+唯一的路径就可以标识互联网中的唯一的文件资源!
注意第一个斜杠不是Linux服务器的根目录,而是web根目录,web根目录可以是Linux中的任何目录!
所以URL就叫:统一资源定位符
urlencode 和 urldecode
- 像 / ? : 等这样的字符,已经被 url 当做特殊意义理解了。因此这些字符不能随意出现。比如,某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义。
- 转义的规则如下: 将需要转码的字符转为 16 进制, 然后从右到左, 取 4位(不足 4 位直接处理), 每 2 位做一位, 前面加上%,编码成%XY 格式
3 http的请求和应答
3.1 服务端设计
下图是http请求的一个信息:
接下来我们来通过代码实验,来测试一下是否可以获取到这些信息!
首先我们简化一下代码,在传输层直接进行IO,直接在Socket文件中获取数据流,将线程的函数方法修改为以下形式:
// 注意设置为静态函数 , 不然参数默认会有TcpServer* this!!!static void *Execute(void *args){pthread_detach(pthread_self()); // 线程分离!!!// 执行Service函数TcpServer::ThreadData *td = static_cast<TcpServer::ThreadData *>(args);// 直接进行IOstd::string reqstr;// 这里默认读取到的是完整的请求ssize_t n = td->_sockfd->Recv(&reqstr);if (n > 0){std::string resstr = td->_this->_service(reqstr);td->_sockfd->Send(resstr);}td->_sockfd->Close();delete td;return nullptr;}
回调函数单独设计一个HttpServer
类来获取客户端申请的数据,目前直接进行简单的打印处理就好!
#include <iostream>
#include <string>class HttpServer
{
public:HttpServer(){}std::string HandlerHelperRequest(std::string& Requeststr){std::cout<<"------------------"<<std::endl;std::cout << Requeststr << std::endl;return std::string();//暂时这样}~HttpServer(){}
};
然后ServerMain中,将HandlerHelperRequest
作为回调函数构造TcpServer!进行启动即可!那么接下来我们是不是就可以在外界通过IP地址和端口号就可以访问Linux服务器上启动的进程了呢???还不可以,我们需要对Linux云服务器做一些处理,才能让外界成功的访问!
3.2 如何让外界可以访问Linux云服务器
让外界可以访问Linux云服务器需要两步操作:云服务器的安全组设置和服务器操作系统层面的防火墙设置。
云服务器的安全组设置操作步骤如下,这里以阿里云服务器为例:
- 首先在控制台中找到安全组,打开需要操作的实例对象
- 在实例中手动添加需要使用什么协议开放哪些端口,手动保存即可:
- 协议类型:选择 TCP
- 端口范围:填写“8888/8888”或“8888-8888”
- 授权对象:可以设置为“0.0.0.0/0”以允许所有IP访问,但出于安全考虑,建议限制为您的IP地址或特定IP范围。
云服务器设置好时候,接下来就进行服务器操作系统层面的防火墙设置:
- 对于CentOS:
# 查看防火墙状态 sudo systemctl status firewalld # 如果需要,启动防火墙服务 sudo systemctl start firewalld # 检查端口8888是否开放 sudo firewall-cmd --zone=public --query-port=8888/tcp # 如果端口未开放,添加端口规则 sudo firewall-cmd --zone=public --add-port=8888/tcp --permanent # 重新加载防火墙规则 sudo firewall-cmd --reload
- 对于Ubuntu:
# 查看防火墙状态 sudo ufw status # 如果需要,启用ufw sudo ufw enable # 检查端口8888是否开放 sudo ufw allow 8888/tcp # 如果端口未开放,添加规则 sudo ufw allow 8888/tcp # 重新加载防火墙规则 sudo ufw reload
这样外界就可以通过IP地址和端口访问到对应的进程了!!!
3.3 运行测试
测试之前我们先获取一个当前机器的IP地址:
- 使用 curl 命令:
curl ifconfig.me
- 使用 wget 命令:
wget -qO- ifconfig.me
都可以获取到机器的外网IP!
我们启动程序,等待外部的链接:
可以通过手机或者电脑的浏览器通过IP地址和端口号来进行访问:
进行访问之后,会获取到对应的信息:
可以看到电脑WIndows系统和手机IPhone都成功的访问了我们的服务器!!!非常cool!!!
4 理解http请求与应答
4.1 宏观理解
请求和应答是http协议中双方都认识的结构化数据:
一个基本的http请求的格式是这个样子的,按行为单位!
- 请求行:指出请求类型(如GET或POST)、资源路径和使用的HTTP版本:
- 方法(Method):表明对资源的请求类型,如 GET(获取资源)、POST(提交数据)、PUT(更新资源)、DELETE(删除资源)等。
- URI(Uniform Resource Identifier):请求的资源的路径,例如一个网页的地址的后半部分。
- HTTP版本(HTTP Version):表明使用的HTTP协议版本,如 HTTP/1.1 或 HTTP/2。
- 请求报头:提供关于客户端环境和请求本身的信息,如用户代理、接受的内容类型等。其中是以键值对的方式进行存储。
- 空行:请求报头和请求正文之间的分隔符。
- 请求正文(可选):包含要发送给服务器的数据,如表单数据。
http的应答与请求的格式很类似:
- 状态行:包含HTTP版本、状态码和状态消息。例如,HTTP/1.1 200 OK 表示服务器成功处理了请求。
- 响应报头:提供关于响应的信息,如内容类型、内容长度、服务器类型、设置Cookie等。例如:
- 空行:响应报头和响应正文之间的分隔符。
- 响应正文(可选):包含从服务器返回的实际内容,如HTML页面、图片或其他数据。
知道了请求和报文的结构,其本质上还是报文,那么如何将其报头与有效载荷进行分离呢?
我们看到的请求和应答的结构可以看到,报头和报文是通过换行符进行分割的!巧了我们之前不也是这样进行操作的吗!而且只要有正文,就会有对应的content-length:xxx
来帮我我们判断正文的是否完整!
4.2 http请求反序列化
接下来我们简单设计一下HttpRequest
http请求的结构化数据!
首先根据其整体的结构我们可以加入四个成员变量:请求行 ,请求报头, 空行 ,请求正文
// 设计http协议
class HttpRequest
{
public:HttpRequest(){}void Serialization(std::string &reqstr){}void Deserialization(std::string &reqstr){}~HttpRequest(){}private:std::string _req_line; // 请求行std::vector<std::string> _req_headers; // 请求报头std::string _blank_line; // 分割行std::string _req_body_text; // 正文
};
这是最基本的四块数据,我们先对这四部分进行反序列化。因为他们都是根据分隔符\r\n
进行分割的字符串,所以十分好处理:
std::string GetLine(std::string &reqstr){// 寻找分隔符auto pos = reqstr.find(base_sep);if (pos == std::string::npos)return std::string();std::string line = reqstr.substr(0, pos);if (line.empty())return base_sep;// 在原字符串中删除reqstr.erase(0, base_sep.size() + line.size());return line;}void Deserialization(std::string &reqstr){// 进行反序列化_req_line = GetLine(reqstr);//请求行do{std::string header = GetLine(reqstr);if (header == "")break;else if (header == base_sep)break;else_req_headers.push_back(header);} while (true);//请求报头_blank_line = GetLine(reqstr);//空行_req_body_text = GetLine(reqstr);//请求正文}
这样就可以将一个字符串切分为四个部分了:
接下来我们可以对数据进行进一步处理:我们加入更加具体的成员变量
std::string _method; //请求方法std::string _url; //请求路径std::string _version;//版本std::unordered_map<std::string , std::string> _kv_headers;//报头
处理很简单:按照字符串结构编写代码即可!
void ParseReqLine(){// 优雅的操作!!!std::stringstream ss(_req_line);ss >> _method >> _url >> _version;}void ParseReqHeader(){for (auto &header : _req_headers){auto pos = header.find(line_sep);if (pos == std::string::npos)continue;std::string k = header.substr(0, pos);std::string v = header.substr(pos + line_sep.size());if (k.empty() || v.empty())continue;_kv_headers.insert(std::make_pair(k, v));}}void Deserialization(std::string &reqstr){// 进行反序列化//...//----------------具体数据的处理-------------------ParseReqLine(); // 请求行的处理!!!ParseReqHeader(); // 处理报头}
我们可以将结果打印出来看看:
非常好,我们成功将reqstr进行了反序列化,之后我们再来实现业务逻辑的代码!!!
后续文章,敬请期待!
相关文章:

【计网】从零开始学习http协议 --- http的请求与应答
如果你不能飞,那就跑; 如果跑不动,那就走; 实在走不了,那就爬。 无论做什么,你都要勇往直前。 --- 马丁路德金 --- 从零开始学习http协议 1 什么是http协议2 认识URL3 http的请求和应答3.1 服务端设计…...

记录linux环境下搭建本地MQTT服务器实现mqtt的ssl加密通讯
1、ubuntu安装mosquitto sudo apt-get update//安装服务端 sudo apt-get install mosquitto//安装客户端 sudo apt-get install mosquitto-clients 2、安装openssl 3、mqtts/tls加密传输 mosquitto原生支持了TLS加密,TLS(传输层安全)是SSL&…...

基于python+django+vue的电影数据分析及可视化系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…...

HJ50-四则运算:栈的运用、中缀表达式转后缀表达式并计算结果
文章目录 题目一、分析1.1表达式预处理1.2中缀表达式转后缀1.3 后缀表达式计算结果 二、答案 题目 一、分析 通过利用栈将中缀表达式转换为后缀表达式,在根据后缀表达式计算运算结果。由于包含负数操作数的情况,并且操作数位数不固定为1,因此…...

C++编程:实现简单的高精度时间日志记录小程序
0. 概述 为了检查是否存在系统时间跳变,本文使用C实现了一个简单的高精度时间日志记录小程序。该程序能够每隔指定时间(默认40毫秒)记录一次系统时间到文件中,并具备以下功能: 自定义时间间隔和文件名:通…...

QQ机器人搭建
使用QQ官方机器人Python SDK和三方框架搭建QQ群聊机器人 文章目录 使用QQ官方机器人Python SDK和三方框架搭建QQ群聊机器人前言编写机器人代码机器人监听群聊进行文字回复机器人监听群聊进行图片回复机器人监听群聊进行文件发送机器人监听群聊进行视频发送机器人监听群聊进行语…...

flink设置保存点和恢复保存点
增加了hdfs package com.qyt;import org.apache.flink.api.java.functions.KeySelector; import org.apache.flink.api.java.tuple.Tuple2;import org.apache.flink.runtime.state.storage.FileSystemCheckpointStorage;import org.apache.flink.streaming.api.datastream.Dat…...

使用python获取百度一下,热搜TOP数据详情
一、查找对应链接 # 警告:以下代码仅供学习和交流使用,严禁用于任何违法活动。 # 本代码旨在帮助理解和学习编程概念,不得用于侵犯他人权益或违反法律法规的行为。 1、打开百度页面 百度一下,你就知道 2、点击F12 或 右键鼠标…...

Go conc库学习与使用
文章目录 主要功能和特点conc 的安装典型使用场景示例代码并行执行多个 Goroutines错误处理限制并发 Goroutines 数量使用 context.Context 进行任务控制 常见问题1. **任务中发生 panic**原因:解决方法: 2. **conc.Group 重复调用 Wait()**原因…...

大模型prompt先关
对于未出现的任务,prompt编写技巧: 1、假设你是资深的摘要生成专家,根据提供的内容,总结对应的摘要信息。请生成一个指令,指令中带有一个使用例子。直接提供给大型模型以执行此任务。 2、基于大模型提供的内容再进行二…...

尚品汇-自动化部署-Jenkins的安装与环境配置(五十六)
目录: 自动化持续集成 (1)环境准备 (2)初始化 Jenkins 插件和管理员用户 (3)工作流程 (4)配置 Jenkins 构建工具 自动化持续集成 互联网软件的开发和发布…...

【尚跑】2024铜川红色照金半程马拉松赛,大爬坡152安全完赛
1、赛事背景 2024年9月22日8点,2024铜川红色照金半程马拉松赛于照金1933广场鸣枪起跑! 起跑仪式上,6000位选手们合唱《歌唱祖国》,熟悉的旋律响彻陕甘边革命根据地照金纪念馆前,激昂的歌声凝聚心中不变的热爱。随着国…...

WPS中让两列数据合并的方法
有这样一个需求,就是把A列数据和B列数据进行合并(空单元格略过)具体实现效果如图下: 该如何操作呢? 首先在新的一列第一个单元格中输入公式"A1&B1" 然后回车,就出现了两列单元格数据合并的效…...

使用yum为centos系统安装软件以及使用(包含阿里云yum源配置)
centos系统配置阿里云yum源 因为centos7官方停止维护,自带yum源用不了了,所以可以更换成阿里云yum源 方法: 使用root权限执行以下语句 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo CentOS…...

《深度学习》【项目】OpenCV 发票识别 透视变换、轮廓检测解析及案例解析
目录 一、透视变换 1、什么是透视变换 2、操作步骤 1)选择透视变换的源图像和目标图像 2)确定透视变换所需的关键点 3)计算透视变换的变换矩阵 4)对源图像进行透视变换 5)对变换后的图像进行插值处理 二、轮廓检测…...

Linux 线程互斥
前言 对于初学线程的伙伴来讲,多线程并发访问导致的数据不一致问题,总是让人陷入怀疑,很多人只是给你说加锁!但没有人告诉你为什么?本篇博客将详解! 目录 前言 一、线程互斥 • 为什么票会出现负数的情…...

【Redis 源码】6AOF持久化
1 AOF功能说明 aof.c 文件是 Redis 中负责 AOF(Append-Only File)持久化的核心文件。AOF 持久化通过记录服务器接收到的每个写命令来实现数据的持久化。这样,在 Redis 重启时,可以通过重放这些命令来恢复数据。 2 AOF相关配置 a…...

6.MySQL基本查询
目录 表的增删查改Insert(插入)插入替换插入替换2 Retrieve(查找)SELECT 列全列查找指定列查询查询字段为表达式为查询结果指定别名结果去重 WHERE 条件order by子句筛选分页结果 Update(更新)delete&#…...

Linux字符设备驱动开发
Linux 字符设备驱动开发是内核模块开发中的一个重要部分,主要用于处理字节流数据设备(如串口、键盘、鼠标等)。字符设备驱动的核心任务是定义如何与用户空间程序交互,通常通过一组文件操作函数进行。这些函数会映射到 open、read、…...

HTML5+JavaScript绘制闪烁的网格错觉
HTML5JavaScript绘制闪烁的网格错觉 闪烁的网格错觉(scintillating grid illusion)是一种视觉错觉,通过简单的黑白方格网格和少量的精心设计,能够使人眼前出现动态变化的效果。 闪烁的栅格错觉,是一种经典的视觉错觉…...

每日OJ题_牛客_拼三角_枚举/DFS_C++_Java
目录 牛客_拼三角_枚举/DFS 题目解析 C代码1 C代码2 Java代码 牛客_拼三角_枚举/DFS 拼三角_枚举/DFS 题目解析 简单枚举,不过有很多种枚举方法,这里直接用简单粗暴的枚举方式。 C代码1 #include <iostream> #include <algorithm> …...

[uni-app]小兔鲜-01项目起步
项目介绍 效果演示 技术架构 创建项目 HBuilderX创建 下载HBuilderX编辑器 HBuilderX/创建项目: 选择模板/选择Vue版本/创建 安装插件: 工具/插件安装/uni-app(Vue3)编译器 vue代码不能直接运行在小程序环境, 编译插件帮助我们进行代码转换 绑定微信开发者工具: 指定微信开…...

安全的价值:构建现代企业的基础
物理安全对于组织来说并不是事后才考虑的问题:它是关键的基础设施。零售商、医疗保健提供商、市政当局、学校和所有其他类型的组织都依赖安全系统来保障其人员和场所的安全。 随着安全技术能力的不断发展,许多组织正在以更广泛的视角看待他们的投资&am…...

门面(外观)模式
简介 门面模式(Facade Pattern)又叫作外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构型设计模式。 通用模板 创建子系统角色类…...

kotlin flow 使用
1 创建flow 方式1 通过携程扩展函数FlowKt中的flow扩展函数可以直接构建flow,只需要传递FlowCollector收集器实现类就可以了 private fun create1(){val intFlow createFlow()println("创建int flow: $intFlow")runBlocking {println("开始收集&…...

vue3 实现文本内容超过N行折叠并显示“...展开”组件
1. 实现效果 组件内文字样式取决与外侧定义 组件大小发生变化时,文本仍可以省略到指定行数 文本不超过时, 无展开,收起按钮 传入文本发生改变后, 组件展示新的文本 2. 代码 文件名TextEllipsis.vue <template><div ref"compRef" class"wq-text-ellip…...

根据源码解析Vue2中对于对象的变化侦测
UI render(state) VUE的特点是数据驱动视图,在这里可以把数据理解为状态,而视图就是用户可以看到的页面,页面是动态变化的,而他的变化或是用户操作引起,或是后端数据变化引起,这些都可以说是数据的状态变…...

爬虫技术深潜:探究 JsonPath 与 XPath 的语法海洋与实战岛屿
Python爬虫中JSON与XML字符串的XPath和JsonPath过滤语法区别对比 在信息爆炸的互联网时代,数据抓取成为了获取宝贵信息的关键技能。对于技术爱好者,特别是Python程序员来说,熟练掌握JSON和XML数据解析方法至关重要。本文旨在深入探讨这两种格…...

纠删码参数自适应匹配问题ECP-AMP实验方案(一)
摘要 关键词:动态参数;多属性决策;critic权重法;DBSCA聚类分析 引言 云服务存储系统是一种基于互联网的数据存储服务,它可以为用户提供大规模、低成本、高可靠的数据存储空间。云服务存储系统的核心技术之一是数据容…...

五、人物持有武器攻击
一、手部添加预制体(武器) 1、骨骼(手) 由于人物模型有骨骼和动画,在添加预制体后,会抓握武器 建一个预制体在手部位置 二、添加武器拖尾 下载拖尾特效 赋值特效中的代码,直接使用 清空里面…...