网络协议——HTTP协议

目录

​编辑

一,HTTP协议基本认识

二,认识URL

三,http协议的格式

 1,发送格式

2,回应格式 

四,服务端代码

五,http报文细节

1,Post与Get方法

2,Content_lenth

 3,状态码

4,Location

5,content_type

6,cookie

​编辑


一,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://www.mzph.cn/news/797946.shtml

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

相关文章

运维:Linux操作系统

一、Linux基础命令 1、ls命令 功能&#xff1a;列出文件夹信息 语法&#xff1a;ls [-l -h -a] [参数] 参数&#xff1a;被查看的文件夹&#xff0c;不提供参数&#xff0c;表示查看当前工作目录 -l&#xff0c;以列表形式查看 -h&#xff0c;配合-l&#xff0c;以更加人性…

【前端webpack5高级优化】提升打包构建速度几种优化方案

HotModuleReplacement&#xff08;HMR/热模块替换&#xff09; 开发时我们修改了其中一个模块代码&#xff0c;Webpack 默认会将所有模块全部重新打包编译&#xff0c;速度很慢 所以我们需要做到修改某个模块代码&#xff0c;就只有这个模块代码需要重新打包编译&#xff0c;…

老王讲IT:函数基础

IT老王&#xff1a;函数基础 目标 函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数 01. 函数的快速体验 1.1 快速体验 所谓函数&#xff0c;就是把 具有独立功能的代码块 组织为一个小模块&#xff0c;在需要的时候 调用 函…

html加载后端数据较慢问题记载

标题: html加载后端数据较慢问题记载 日期: 2024-04-06 22:29:00 标签: [html, flask] 分类: [Python, Flask] 网站页面最近加载很慢&#xff0c;不知道为什么&#xff0c;这里记录一下&#xff0c;一步一步查问题的思路。 说下环境 python3.8 flask2.3.3 mysql5.7 问题 刷…

爬虫实战一、Scrapy开发环境(Win10+Anaconda3)搭建

#前言 在这儿推荐使用Anaconda进行安装&#xff0c;并不推荐大家用pythonpip安装&#xff0c;因为pythonpip的坑实在是太多了。 #一、环境中准备&#xff1a; Win10&#xff08;企业版&#xff09;Anaconda3-5.0.1-Windows-x86_64&#xff0c;下载地址&#xff0c;如果打不开…

每日OJ题_两个数组dp①_力扣1143. 最长公共子序列

目录 力扣1143. 最长公共子序列 解析代码 力扣1143. 最长公共子序列 1143. 最长公共子序列 难度 中等 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样…

尚硅谷html5+css3(2)CSS5基本知识

1.网页分为三个部分&#xff1a; 结构&#xff1a;HTML 表现&#xff1a;CSS 行为JavaScript CSS:层叠样式表&#xff0c;网页实际上是一个多层结构&#xff0c;通过CSS可以分别为网页的每一个层来设置样式&#xff0c;最终用户只看最上面的一层&#xff0c;总之&#xff0…

蚁群优化算法(Ant Colony Optimization Algorithm)

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 算法引言 蚁群算法&#xff0c;是一种模拟蚂蚁觅食行为的优化算法。想象一下&#xff0c;当你在野餐时&#xff0c;不小心洒了一些糖在地上。一…

AcWing26. 二进制中1的个数。三种解法Java

输入一个 3232 位整数&#xff0c;输出该数二进制表示中 11 的个数。 注意&#xff1a; 负数在计算机中用其绝对值的补码来表示。 数据范围 −100≤ 输入整数 ≤100 样例1 输入&#xff1a;9 输出&#xff1a;2 解释&#xff1a;9的二进制表示是1001&#xff0c;一共有2个…

免疫检查点信号转导和癌症免疫治疗(文献)

目录 基础 介绍 免疫检查点的表面调控&#xff08;细胞膜层面&#xff09; ​编辑 PD-1调节 PD-L1调节 CTLA-4 调节 检查点信号通路 关于靶点研究 展望 Immune checkpoint signaling and cancer immunotherapy - PubMed (nih.gov) 基础 【中英字幕】肿瘤免疫疗法之免…

分享10个免费高可用的GPT3.5和4.0网站并做功能测试【第一个】

1.介绍 网址&#xff1a;直接点&#xff1a;aicnn 或者 www.aicnn.cn 基于ChatGPT可以实现智能聊天、绘画生成、高清文本转语音、论文润色等多种功能&#xff0c;基于sd和mj实现的绘画功能&#xff0c;下面是功能测试&#xff1a; 博主从 1.GPT3.5是否完全免费/是否限制频率、…

MySQL学习笔记------事务

事务 事务是一组操作的集合&#xff0c;他是一个不可分割的单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败 事务操作 create table account(id int comment ID,name varchar(10) …

如何成为一名嵌入式C语言高手?

如何成为一名嵌入式C语言高手? 嵌入式系统是当今科技领域的核心&#xff0c;而C语言则是嵌入式系统开发中最常用的编程语言之一。成为一名嵌入式C语言高手需要长时间的学习和实践。下面将介绍一些方法和技巧&#xff0c;帮助你在嵌入式C语言领域获得突出的技能和能力。 一、打…

Spring 中类似 aBbb 单字母单词序列化与反序列问题

文章目录 前言代码准备问题排查lombok自定义生成 get、set 结合源码解析使用 lombok使用 lombok 自定义生成 user 对象 get、set 方法 如何解决使用注解 JsonProperty("aTest")自定义实现符合 Spring 规范的 get set 方法 个人简介 前言 最近在使用 spring boot mvc…

Linux下Qt生成程序崩溃文件

文章目录 1.背景2.Qt编译生成程序2.1.profile模式的本质 3.执行程序&#xff0c;得到core文件4.代码定位4.1.直接使用gdb4.2.使用QtCreator 5.总结6.题外话6.1.profile模式和debug模式的区别 1.背景 在使用Qt时&#xff0c;假如在windows&#xff0c;当软件崩溃时&#xff0c;…

【整数二分】难题选讲

对应洛谷题单里面【整数二分】几道同学们不太好理解的题目&#xff0c;写个解析 A-1数对题目链接 实际上本题方法很多&#xff0c;我先说个好做的办法 我们可以用map标记所有数的个数&#xff0c;枚举数字A[i],在map里面查找C-A[i]数字的个数&#xff0c;统计答案即可&#xff…

C++11右值引用

传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 左值与左值引用 左值是一个表示数据的表达式(如变量名或解引用的指针)&…

Qt-线程1-Run

0. 写在前面 一个应用程序一般只有一个线程&#xff0c;一个线程内的操作是顺序执行的&#xff0c;如果有某个比较耗时间的计算或操作&#xff0c;比如图像处理大数据图像、网络通信中的文件传输&#xff1b;在一个线程内操作时&#xff0c;用户界面就能冻结而不能及时响应。这…

34-2 SSRF(服务器端请求伪造)漏洞 - SSRF相关函数

简介 file_get_contents(): 功能: 该函数用于将整个文件读取到一个字符串中,可以用于获取本地或远程文件的内容。风险: 如果未经验证的用户输入作为参数传递给该函数,并且允许读取远程文件,则可能导致SSRF漏洞,攻击者可以利用该漏洞读取服务器内部的文件或者执行未经授权…