网络协议——HTTP协议
目录
编辑
一,HTTP协议基本认识
二,认识URL
三,http协议的格式
1,发送格式
2,回应格式
四,服务端代码
五,http报文细节
1,Post与Get方法
2,Content_lenth
3,状态码
4,Location
5,content_type
编辑
一,HTTP协议基本认识
在我之前写的文章中,我实现过自定义协议。但是,在实际的网络编程中我们是不太需要定制协议的。因为前辈早就定制好了。而HTTP协议就是其中的一种。 http协议又被叫做超文本传输协议,这是因为http的本质其实就是按照http协议从服务端拿文件资源。而http协议能够拿走所有的文件资源,所以http协议又被叫做超文本传输协议。
二,认识URL
URL:Uniform Resource Locator,中文名叫统一资源定位符。
URL的样子如下:
http://www.example.com/path/to/resource?query=string#fragment
http://是协议部分,表示这是一个使用 HTTP 协议的 URL。www.example.com是域名部分,表示资源所在的服务器的地址。/path/to/resource是路径部分,表示服务器上资源的具体位置。?query=string是查询字符串部分,用于传递参数给服务器。#fragment是片段标识符部分,用于指定资源的某一部分,通常用于网页的导航。
域名其实就是ip地址,那为什么只要有ip地址就可以访问到主机上的唯一资源呢?
因为http的端口号是默认绑定的,是统一的不需要我们再来绑定。
urlencode与urldecode
urlencode:在url中有些符号是被url默认使用了的。比如:?://。当我们的用户输入的url中带有这些字符时这些字符就会被encode。encode的规则是:将对应的字符的ASSCALL码转化为16进制,然后在前面加上%。urldecode就是反过来。当然,这个过程并不需要我们来做,有相应的工具可以帮我们完成:urlencode&&urldecode工具
三,http协议的格式
1,发送格式
当我们以http为协议向服务端发送请求时,我们发送的数据会包含如下数据:
1,请求行
2,请求报文
3,一个空行
4,请求正文
在请求行,包含如下数据:
1,method 2,url 3,http version 4,"\r\n"
图示如下:

2,回应格式
当服务器在将数据发送回客户端时,会依照如下何时将数据返回:
1,状态行
2,响应报文
3,空行
4,响应正文
在状态行内,包含如下数据:
1,http version 2,状态码 3,状态码描述 4,“"\r\n"
图示如下:

四,服务端代码
在了解了http发送消息和响应消息的数据发送格式以后,我们便可以来动手写上一个能够按照http协议的格式进行响应的服务端。
实现如下:
1,为了能够更方便的使用创建套接字的接口,我对创建套接字的接口做如下封装
#pragma once
#include<iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<cstring>
#include<arpa/inet.h>
#include<unistd.h>//定义一些变量
#define blog 10
#define defaultport 8080class Socket
{public://构造函数Socket(): sockfd_(0){}public://创建套接字bool Sock(){sockfd_ = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字if (sockfd_ < 0){std::cerr << "创建套接字失败" << std::endl;return false;}int opt = 1;setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));return true; // 将创建好的套接字返回}//bind,服务端只要绑定端口号bool Bind(int port = defaultport){sockaddr_in server_addr;memset(&server_addr, 0, sizeof (server_addr));//清空数据server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);server_addr.sin_addr.s_addr = INADDR_ANY;int r1 = bind(sockfd_,(sockaddr*)&server_addr,sizeof server_addr);if(r1<0){std::cerr << "bind err" << std::endl;return false;}return true;}//监听bool Listen(){int r2 = listen(sockfd_, blog);if(r2<0){std::cerr << "listen err" << std::endl;return 0;}return true;}//接收int Accept(std::string* ip,int* port){sockaddr_in cli_addr;socklen_t len = sizeof(cli_addr);int sockfd = accept(sockfd_, (sockaddr *)&cli_addr, &len);if(sockfd<0){std::cerr << "accept err" << std::endl;return -1;}char buff[64]={0};inet_ntop(AF_INET, &cli_addr, buff, sizeof(buff));*ip = buff;*port = ntohs(cli_addr.sin_port);return sockfd;}//连接bool Connect(std::string& ip,int16_t port){sockaddr_in addr_;addr_.sin_family = AF_INET;addr_.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(addr_.sin_addr));int r = connect(sockfd_, (sockaddr *)&addr_, sizeof (addr_));if(r<0){std::cerr << "connect err" << std::endl;return false;}return true;}//关闭void Close(){close(sockfd_);}public://成员int sockfd_;
};
2,服务端代码
在做好上述封装工作以后便可以来构建服务端了,服务端的创建步骤如下:
1,类成员
1,listensockfd_: Sock类成员。主要是为了调用创建套接字的接口。
2,port_:端口号,主要是为了绑定时使用。
2,构造函数
初始化port_
3,Init函数
创建套接字 bind套接字 监听套接字
4,Start函数
1,循环接收服务端发来的连接请求,然后创建线程执行相应的任务。
2,读出客户端发来的请求。
3,然后根据http协议将请求处理出来到req中。
4,根据url判断客户端想要请求的资源是服务器那个资源。
5,读出资源发送回客户端。
在理清上面创建服务端的过程以后,便可以写出如下代码:
struct ThreadData
{int sockfd;
};class HttpServer
{public:HttpServer(int port):port_(port){} void Init(){listensocket_.Sock();//创建套接字listensocket_.Bind(port_);//绑定套接字端口listensocket_.Listen();//监听套接字}bool Start(){while (true){// 建立连接std::string ip;int port;int sockfd = listensocket_.Accept(&ip, &port_);lg(Debug, "link->%s:%d sucess,sockfd:%d", ip.c_str(), port_,sockfd);// 创建线程执行任务pthread_t td;ThreadData ts;ts.sockfd = sockfd;pthread_create(&td, nullptr, ThreadRun, &ts);}return true;}static void* ThreadRun(void*args){pthread_detach(pthread_self());//与主线程分离ThreadData* ts = static_cast<ThreadData*>(args);Helper(ts->sockfd);//处理客户端发来的消息delete ts;return nullptr;}private:Socket listensocket_;int port_;};
如何处理客户端发来的消息?
代码如下:
static void Helper(int sockfd){// 接收消息并打印char buff[1024] = {0};int n = recv(sockfd, buff, sizeof(buff), 0);if (n > 0){ //显示读出来的消息buff[n] = 0;std::cout << buff << std::endl;//读出来的消息进行解析处理,得到客户端想要访问什么资源Request req;req.Deserialize(buff);req.prase(buff);std::string path = req.Select_path();path = rootpath + path;std::cout << "debug: " << std::endl;req.Debugprint();//根据http协议的方式将资源发送会给客户端std::string text = Readrequest(path);std::string response;std::string sep = "\r\n";std::string line = "HTTP 1.1 1 OK";line += sep;std::string head = "contentlenth:";std::string len = std::to_string(text.size());head += len;head += sep;head += sep;response+=line;//状态行response += head;//报头response += text;//正文send(sockfd, response.c_str(), response.size(), 0);}}
如何对客户端的发来的消息进行处理呢?
创建一个Request类,这个类里面含有如下成员:
1, std::vector<std::string> arr :对内容进行分割
2, std::string text :将正文提取出来
######################################
3,std::string method :接收方法
4,std::string url :显示网址
5,std::string http_version :显示http的版本
6, std::string path :表示路径
类方法如下:
1,Deseralize: 对从客户端接收到的消息里的请求行和报文进行打散,打散到我的arr数组里面。并将正文提取出来。
2,prase:打散后,arr[0]便代表着请求行,包含着三个信息:method url http version。通过prase函数将这些消息获取出来。
3,DebugPrint:显示相应的消息。用于debug。
4,select_path:选择路径。
实现如下:
class Request
{public:void Deserialize(std::string message){while (true)//循环读取{int pos = message.find(sep);if(pos == std::string ::npos){break;}std::string str = message.substr(0, pos);if(str.empty())break;arr.push_back(str);message.erase(0, pos + 1);//读一行消一行}text = message;}void prase(std::string message)//将状态行的信息散开{std::stringstream s (message);s >> method >> url >> http_version;}void Debugprint(){for(auto e:arr){std::cout << e <<" "<<std::endl ;}std::cout << "method:" << method << std::endl;std::cout << "url:" << url << std::endl;std::cout<< "http_version:" << http_version << std::endl;}std::string Select_path(){if(url == "/"||url == "/html.index"){path = rootpath+"/";}else {path = url;}return path;}private:std::vector<std::string> arr;std::string text;std::string method;std::string url;std::string http_version;std::string path;
};
返回消息:
当我们处理了客户端发来的消息以后,便可以得到客户端想要的资源在那个路径下。于是我们便可以将该路径对应的文件的内容读出来并返回给客户端显示。
读取文件的函数如下:
std::string Readrequest(std::string path)//从文件内读取
{ std::ifstream in(path);if(!in.is_open())return "404";std::string line;std::string content;while(getline(in,line)){content += line;}return content;//这个消息会拼接到正文,也就是text
}
五,http报文细节
1,Post与Get方法
在http的请求报头当中,经常使用的请求方法有如下两个:1,Post 2,Get
Post:
Post方法在提取form表单的参数时,通常会将参数放到正文部分来提交参数。
Get:
Get方法在提取表单的参数时,通常会将参数放到url的后面来提交参数。
相对于Get方法,Post方法提交参数的方式比较隐蔽。但是两种提交参数的方式都是不安全的。因为通过抓包都可以将报文抓取然后获取报文的所有内容。如果想要安全就得加密,加密协议就是https协议。
2,Content_lenth
在相应报文的报头里面有一个Content_lenth的字段,代表着正文的长度。报头与正文之间通过一个空行来分隔,分割以后,正文内容的大小由Content_lenth来指定,进而进行读取。
3,状态码
在http报文当中,相应报文内的状态行中会有一串数字表示响应的状态。

比如,200就代表正常,状态码描述便是ok。
通常,状态码对应的消息如下
1xx:信息型状态码。
2xx:代表请求ok,如200。
3xx:代表重定向,如301(永久重定向) 302(临时重定向)。
4xx:代表客户端请求错误,如404。
5xx:代表服务端内部错误,如501。
4,Location
Location字段一般都是和重定向状态3xx搭配使用的。当我的客户端申请访问我们已经移动后的资源时,服务端的Location字段便会指引客户端去访问移动后的资源。
5,content_type
content_type标识的是正文的文件类型。这个,content_type放在报头,指明文件类型,进而让客户端以正确的类型收取文件资源显示文件资源。
6,cookie
cookie字段的作用是储存少量信息,用于搭建临时会话。
在我们的浏览器上,其实每次访问一个资源都是需要认证的。比如你在一个浏览器上第一次访问b站,我们是不是要进行登录认证呢?是的吧,那我们看b站上的视频需不需要进行认证呢?其实也是需要的,但是cookie已经帮你吧认证信息存储起来了,所以会帮你自动登录认证。
分类:
cookie文件一般分为内存文件和硬盘文件。为了安全,一般都会设置为内存文件(会定时清理)。比如在edge 浏览器上便是内存文件。
更完整细节:
HTTP方法:

相关文章:
网络协议——HTTP协议
目录 编辑 一,HTTP协议基本认识 二,认识URL 三,http协议的格式 1,发送格式 2,回应格式 四,服务端代码 五,http报文细节 1,Post与Get方法 2,Content_lenth 3&…...
八股面试——数据库——索引
索引的概念 B树的概念: 索引的作用 聚簇索引与非聚簇索引 聚簇索引就是主键值,在B树上,通过主键大小(数据在B树叶子节点按主键顺序排序)寻找对应的叶子节点,叶子节点保存的一整条记录。 非聚簇索引&#x…...
【二分查找】Leetcode 二分查找
题目解析 二分查找在数组有序可以使用,也可以在数组无序的时候使用(只要数组中的一些规律适用于二分即可) 704. 二分查找 算法讲解 当left > right的时候,我们循环结束,但是当left和right缩成一个点的时候&#x…...
Python+Vuecil笔记
Nginx 进入目录: C:\nginx-1.20.2\nginx-1.20.2 start nginx 开始 nginx -s stop 停止 nginx -s quit 退出CSS 通过标签去写css 循环展示数据 JS 点击时执行事件 Django 配置media 在seetings里面修改 STATIC_URL /static/ MEDIA_URL /upload/ MEDIA_ROOT os.pat…...
C语言关于随机数知识点的总结
在C语言中,随机数的生成通常依赖于特定的库函数,最常用的是 <stdlib.h> 头文件中的 rand() 函数。以下是对随机数知识点的总结、举例和分析: 随机数知识点总结 1.随机数种子:rand() 函数生成的随机数是伪随机数࿰…...
网络应用层和传输层
网络中有很多协议这些协议的不同导致了分层这一现象,不同层的主要功能不一样。 应用层:应用程序。数据具体如何使用 传输层:关注起点和终点 网络层:关注路径规划 数据链路层:关注相邻节点的转发 物理层࿱…...
Vue3:优化-从响应式数据中获取纯数据
一、情景说明 我们知道,Vue3中,创建变量时,常用ref、reactive来包裹,这样,这个变量就是响应式数据 然而,有时候,我们只需要纯数据 例如,我们在调用后端接口的时候,我们只…...
C#.手术麻醉系统源码 手麻系统如何与医院信息系统进行集成?
C#.手术麻醉系统源码 手麻系统如何与医院信息系统进行集成? 手术麻醉系统与医院信息系统的集成是一个关键步骤,它有助于实现信息的共享和流程的协同,从而提高医疗服务的效率和质量。手麻系统与lis、his、pacs等系统的对接是医院信息化建设的重…...
学习CSS Flexbox 玩flexboxfroggy flexboxfroggy1-24关详解
欢迎来到Flexbox Froggy,这是一个通过编写CSS代码来帮助Froggy和朋友的游戏! justify-content 和 align-items 是两个用于控制 CSS Flexbox 布局的属性。 justify-content:该属性用于控制 Flexbox 容器中子项目在主轴(水平方向)…...
springboot项目如何配置跨域?
在Spring Boot项目中配置跨域(CORS,Cross-Origin Resource Sharing)主要是为了允许来自不同源(不同的协议、域名或端口)的前端应用能够访问后端API。Spring Boot提供了多种方式来配置跨域支持。 1. 使用CrossOrigin注…...
实现第一个动态链接库 游戏插件 成功在主程序中运行 dll 中定义的类
devc 5.11编译环境 dll编译环境设置参考 Dev c C语言实现第一个 dll 动态链接库 创建与调用-CSDN博客 插件 DLL代码和主程序代码如下 注意 dll 代码中的class 类名需要 和主程序 相同 其中使用了函数指针和强制类型转换 函数指针教程参考 以动态库链接库 .dll 探索结构体…...
算法第三十九天-验证二叉树的前序序列化
验证二叉树的前序序列化 题目要求 解题思路 方法一:栈 栈的思路是「自底向上」的想法。下面要结合本题是「前序遍历」这个重要特点。 我们知道「前序遍历」是按照「根节点-左子树-右子树」的顺序遍历的,只有当根节点的所有左子树遍历完成之后…...
Rust---复合数据类型之字符串与切片(2)
目录 字符串操作删除 (Delete)连接 (Concatenate)字符串转义前情回顾: Rust—复合数据类型之字符串(1) 字符串操作 删除 (Delete) 删除方法仅适用于 String 类型,分别是: pop(),remove(),truncate(),clear(),此外还有drain() 方法。 pop 方法:pop() 方法返回一个 O…...
iOS 应用内网络请求设置代理
主要通过URLSessionConfiguration 的connectionProxyDictionary 属性 为了方便其他同学使用,我们可以通过界面来进行设定(是否开启代理、服务端、端口),从而达到类似系统上的设定 具体链接参考:为 iOS 网络请求设置代理…...
什么是MariaDB
2024年4月6日,周六晚上 今晚在Debian12上安装mysql时,运行后却发现是MariaDB MariaDB是一个开源的关系型数据库管理系统(RDBMS),它是MySQL的一个分支和替代品。MariaDB由MySQL的原始开发者之一Michael "Monty&qu…...
【面试八股总结】传输控制协议TCP(三)
参考资料 :小林Coding、阿秀、代码随想录 一、TCP拥塞控制⭐ 1. 慢启动 – Slow Start 慢启动是指TCP连接刚建立,一点一点地提速,试探一下网络的承受能力,以免直接扰乱了网络通道的秩序。 慢启动算法: 初始拥塞窗口…...
今年过去了多少天?(switch)
//今年已经过去了几天? #include <stdio.h> int monthday(int year,int month){switch(month){case 1:return 31;case 2:if ((year % 4 0 && year % 100 ! 0)||year % 400 0){return 29;}else{return 28;}break;case 3:return 31;case 4:return 30;…...
提升团队工程交付能力,从“看见”工程活动和研发模式开始
作者:张裕、雅纯 理想中的研发团队应当具有以下特征: 总是工作在最高优先级的事项上 理想的研发团队能够识别并始终集中精力在当前最紧迫和最有价值的任务上。这需要团队具备出色的项目管理能力和决策能力,以便能够正确评估优先级࿰…...
前端学习之DOM编程案例:全选反选案例
代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>全选反选</title> </head> <body><input type"checkbox" id"all">全选<ul><li><…...
golang map
1.底层实现 2.如何解决hash冲突 3.扩容机制 4.无序 5.非线程安全 6.不可寻址 runtime/map.go 1.底层实现 底层基于hash表实现,实现有2个结构体hmap,bmap,map由若干个桶存储,每个桶存8个元素,使用链地址解决hash冲突 …...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...


