Linux:http协议初步认识

文章目录

  • OSI七层模型
  • http协议
    • 域名
    • 路径信息
    • 请求和响应
  • 编写一个httpserver

OSI七层模型

在结束了前面对于序列化反序列化等内容的学习后,重新回到对于OSI模型的部分

在这里插入图片描述
在这里插入图片描述
如上所示的是对于OSI接口的示意图,在这当中可以看到会话层的概念,会话层的作用主要是建立断开连接管理的,相当于是在网络服务器中在TCP server中获取链接的过程,如果不需要这个链接,就在TCP server中把对应的链接关掉,而下面的表示层中写的是固有的数据格式和网络数据的转换,比如有接受不同形式的信息,文字,图像,声音等等,这个表示层其实就是在前面的部分中定义的协议

这个协议表示的就是固有的数据格式,比如说是序列化和反序列化这些内容,再比如有说网络序列转换为主机序列,或者是在缓冲区中定义了各种视频的名称格式等等,这些都是属于自定义协议的范畴,因此这些内容都是属于是表示层当中的信息

最后是应用层,应用层中有针对于特定应用的协议,比如说有电子邮件远程传输的问题,再比如有传输数据解析的问题,这些其实都是在应用层需要解决的问题

所以说这个OSI的这些层次结构就是已经提前定制好了未来的发展方式,把网络中需要的各个功能和网络中每一层的能力就都定制好了,那在表示层和应用层层相对比较好理解一些,无非就是定制协议的过程,那对于会话层的理解该如何深入理解呢?

对于会话的概念并不陌生,在将Linux终端的时候就有提及过这个session的概念,而当客户端链接到服务端后,如果一直不给服务器发送数据,那么对应的服务端就会一直阻塞在这里,那未来可以设置对应的一些策略,比如说超过5秒钟部进行发送消息,就把对应的链接自动关闭

因此,在定制OSI模型的时候,实际上会话表示应用这三层是不能定义在内核当中的,原因就是无论是这些当中的那一层,最终都必须要根据使用场景的不同,由程序员自己定义出对应的协议信息,然后才能有对应的序列化和反序列化的方法,如果用操作系统来全部实现,自然会引起一定的问题,所以就都交给用户自己来进行实现

http协议

域名

在前面的认知中,对于访问网络服务的概念是,可以使用一个ip地址和端口号来访问唯一一台主机上的唯一的一个进程,所以我们才可以用ip加端口号的方式来进行访问,但是现在的问题是,平时使用访问的都是使用一个网址来进行访问,没有使用端口号,这该如何理解呢?

事实上,的确在技术上用ip地址和端口号就可以实现这样的功能,但是在日常生活中作为用户会直接使用一个域名的内容来进行访问网站,因为域名的名字相比起来更加的好记忆,尤其是对于一些大型的网站都是直接用拼音来进行标注,方便用户来进行访问信息

所以就诞生了域名的概念,域名实际上在背后会被解析为对应的服务,这个技术上叫做是域名解析的概念,通过这个技术就能实现把域名转换为ip地址,因此当浏览器访问了特定的域名的时候,浏览器内部会内置一些地址,就能访问对应的域名解析的服务,这样就能转换为ip地址,我们以下面的这个例子为例

当访问百度一下这个网址的时候,都是直接访问这个www.baidu.com这个网址,而当在浏览器中访问的时候,其实在背后已经把这个域名转换成ip地址了,那这个怎么看呢?

在命令行中可以使用这样的命令来进行查看

在这里插入图片描述
由此我们就可以根据这个信息来进行访问百度了

在这里插入图片描述
https协议默认的端口号是80,所以本质上访问的是39.156.66.18:80/,关于这个问题后续再进行详谈

路径信息

对于每一个网址来说,其实是有固定的规则的:

在这里插入图片描述
未来会对带层次的文件路径进行解析,其他的字段不进行额外的讲解

请求和响应

下一步要谈论的主题是,关于http中的请求和响应的问题

http请求

http的请求是有固定格式的,下图画出http请求的具体格式:

在这里插入图片描述

下图展示的是http的一个请求:

在这里插入图片描述
如图所示的是http的一个请求,第一行表示的是POST方法,后面带有对应的URL网址信息,还有http协议版本,还有换行分割符这些信息,后续的内容都是对应的是请求报头,其中包含有对应的Key和Value信息,在结束之后是一个空行,也是作为一个分割的作用,最后一行的信息表示的是请求的正文,在这里表示的是一个用户名和密码的信息,后续会实现这个内容

这里对于这个\r\n进行一个理解:这两个表示的是,对于请求来说都是按行读取,这样可以读取到完整的一行,读取到完整的一行之后就能对于报头和有效载荷进行分离,进而进行后续的一些操作,那么现在的问题是在对于读取网络请求的时候,如何保证读取到的是一个完整的http请求呢?具体的做法是分析它的长度,并进行提取,这是我们之前的做法,而我们之前的做法本质上是一个自定义协议,而在对应的http协议如何保证?答案是保证不了,但是因为有这个\r和\n的存在,所以可以保证的是可以完整的读取到一个http的报头,之后就能进行解析,而在解析内容中就包含有长度这样的字段,所以说想要实现一个http的请求正确读取是一个非常繁琐的过程

http响应

那http响应该如何理解呢?在http的响应和请求几乎是完全一样的:

在这里插入图片描述

所以说此时对于请求和响应已经有了一个基本的视线了,那么下一步就是要进行序列化和反序列化,然后再进行对应的分割就可以,这都是可以解决的问题,所以在未来进行使用http实现网络通信的时候,就是一个发送请求,进行响应,再发送请求,再进行响应的一个过程

编写一个httpserver

那么下一步就直接进行编写一个httpserver来进行测试,这里我就直接用一份已经完备的httpserver来进行测试,主要想法是根据实验现象来理解http背后的细节原理

// socket.hpp
#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"enum
{SocketErr = 2,BindErr,ListenErr,
};// TODO
const int backlog = 10;class Sock
{
public:Sock(){}~Sock(){}public:void Socket(){sockfd_ = socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ < 0){lg(Fatal, "socker error, %s: %d", strerror(errno), errno);exit(SocketErr);}int opt = 1;setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));}void Bind(uint16_t port){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(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind error, %s: %d", strerror(errno), errno);exit(BindErr);}}void Listen(){if (listen(sockfd_, backlog) < 0){lg(Fatal, "listen error, %s: %d", strerror(errno), errno);exit(ListenErr);}}int Accept(std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;socklen_t len = sizeof(peer);int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);if(newfd < 0){lg(Warning, "accept error, %s: %d", strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));*clientip = ipstr;*clientport = ntohs(peer.sin_port);return newfd;}bool Connect(const std::string &ip, const uint16_t &port){struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));if(n == -1) {std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;return false;}return true;}void Close(){close(sockfd_);}int Fd(){return sockfd_;}private:int sockfd_;
};
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map>
#include "Socket.hpp"
#include "Log.hpp"const std::string wwwroot = "./wwwroot"; // web 根目录
const std::string sep = "\r\n";
const std::string homepage = "index.html";static const int defaultport = 8082;class HttpServer;class ThreadData
{
public:ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s){}public:int sockfd;HttpServer *svr;
};// 定义一个http的请求信息
class HttpRequest
{
public:// 对信息进行反序列化void Deserialize(std::string req){while (true){std::size_t pos = req.find(sep);if (pos == std::string::npos)break;std::string temp = req.substr(0, pos);if (temp.empty())break;req_header.push_back(temp);req.erase(0, pos + sep.size());}// 存储请求体的信息text = req;}// .png:image/png// 对信息进行解析void Parse(){// 解析请求的方法,URL,http版本std::stringstream ss(req_header[0]);ss >> method >> url >> http_version;// 设置默认寻找的路径是wwwroot这个目录file_path = wwwroot; // ./wwwroot// 对于URL文件的路径进行一定的拼接if (url == "/" || url == "/index.html"){file_path += "/";file_path += homepage; // ./wwwroot/index.html}elsefile_path += url; // /a/b/c/d.html->./wwwroot/a/b/c/d.html// 查找文件的后缀,默认是html,否则就进行合适的拼接auto pos = file_path.rfind(".");if (pos == std::string::npos)suffix = ".html";elsesuffix = file_path.substr(pos);}// 打印调试信息void DebugPrint(){for (auto &line : req_header){std::cout << "--------------------------------" << std::endl;std::cout << line << "\n\n";}// 打印当前识别出来的请求信息等等std::cout << "method: " << method << std::endl;std::cout << "url: " << url << std::endl;std::cout << "http_version: " << http_version << std::endl;std::cout << "file_path: " << file_path << std::endl;std::cout << text << std::endl;}public:// 请求信息中至少要包含下面的内容std::vector<std::string> req_header;std::string text;// 解析之后的结果std::string method;std::string url;std::string http_version;std::string file_path; // ./wwwroot/a/b/c.html 2.png// 文件后缀std::string suffix;
};// http服务端
class HttpServer
{
public:HttpServer(uint16_t port = defaultport) : port_(port){content_type.insert({".html", "text/html"});content_type.insert({".png", "image/png"});}bool Start(){// 进行套接字的创建,绑定,设置监听listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();for (;;){std::string clientip;uint16_t clientport;// 从监听信息中提起对应的ip和端口号信息,当有用户连接的时候会进行接受对应的信息int sockfd = listensock_.Accept(&clientip, &clientport);if (sockfd < 0)continue;lg(Info, "get a new connect, sockfd: %d", sockfd);pthread_t tid;ThreadData *td = new ThreadData(sockfd, this);// 创建线程去执行对应的handle响应方法pthread_create(&tid, nullptr, ThreadRun, td);}}// 读取html的内容static std::string ReadHtmlContent(const std::string &htmlpath){std::ifstream in(htmlpath, std::ios::binary);if (!in.is_open())return "";in.seekg(0, std::ios_base::end);auto len = in.tellg();in.seekg(0, std::ios_base::beg);std::string content;content.resize(len);in.read((char *)content.c_str(), content.size());in.close();return content;}// 将后缀名替换为描述信息std::string SuffixToDesc(const std::string &suffix){auto iter = content_type.find(suffix);if (iter == content_type.end())return content_type[".html"];elsereturn content_type[suffix];}// 处理http的请求信息void HandlerHttp(int sockfd){char buffer[10240];// 进行收消息的过程ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // bugif (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;// 将接受到的请求信息进行反序列化HttpRequest req;req.Deserialize(buffer);// 对于反序列化后的内容进行解析,解析为当前所需要的信息内容req.Parse();// 返回响应的过程std::string text;bool ok = true;text = ReadHtmlContent(req.file_path); // 失败?if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/";err_html += "err.html";text = ReadHtmlContent(err_html);}// 设置响应状态行,默认失败是404状态std::string response_line;if (ok)response_line = "HTTP/1.0 200 OK\r\n";elseresponse_line = "HTTP/1.0 404 Not Found\r\n";// 进行字符串的拼接// response_line = "HTTP/1.0 302 Found\r\n";std::string response_header = "Content-Length: ";response_header += std::to_string(text.size()); // Content-Length: 11response_header += "\r\n";response_header += "Content-Type: ";response_header += SuffixToDesc(req.suffix);response_header += "\r\n";response_header += "Set-Cookie: name=haha&&passwd=12345";response_header += "\r\n";// response_header += "Location: https://www.qq.com\r\n";std::string blank_line = "\r\n"; // \nstd::string response = response_line;response += response_header;response += blank_line;response += text;// 把拼接好的字符串响应再发送回去,这样就完成了一次通信send(sockfd, response.c_str(), response.size(), 0);}close(sockfd);}static void *ThreadRun(void *args){// 对该线程进行设置,使得主线程不必关心这个线程pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);// 去执行对应的handle方法td->svr->HandlerHttp(td->sockfd);delete td;return nullptr;}~HttpServer(){}private:Sock listensock_;uint16_t port_;std::unordered_map<std::string, std::string> content_type;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/763786.shtml

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

相关文章

QPainter绘图和QChart图表和QCustomplot绘曲线图

一&#xff0c;QPainter绘图 Qt里的所有绘图&#xff0c;比如一个按钮和一个Label的显示&#xff0c;都有绘图系统来执行。绘图系统基于 QPainter、QPaintDevice和QPainEngine类。QPainter是可以直接用来操作绘图的类&#xff0c;而 QPaintDevice和QPainEngine都比QPainter更底…

FinalShell下载安装 + FinallShell远程连接Linux 教程

图形化&#xff0c;命令行 : windows下的图形化和命令行 : Linux下的图形化和命令行 : 在Linux下&#xff0c;普遍使用命令行操作 ; FinalShell 下载安装: 下载安装地址 : 这里给出windows的下载地址 : install 双击打开就好 : 然后点击下一步 : 选择下载地址&…

vue学习日记14:工程化开发脚手架Vue CLI

一、概念 二、安装 1.全局安装&查看版本 注意启动cmd输入命令 要以管理员运行哦 安装了一次就行以后不用再创建了 yarn global addvue/cli vue --version 显示了版本号即可 2.创建项目架子 创建项目的路径在哪 项目就在哪 项目名字不能用中文 vue create project-n…

搜索测试题题解(3月19号总结)

目录 1.Dungeon Master 2.Oil Deposits 3.Find a way 1.Dungeon Master Sample InputcopyOutputcopy 3 4 5 S.... .###. .##.. ###.###### ##### ##.## ##...##### ##### #.### ####E1 3 3 S## #E# ###0 0 0Escaped in 11 minute(s). Trapped! 这道题与普通的bfs模板题就是…

线性顺序表算法库

list.cpp 具体函数实现 #include <stdio.h> #include "list.h" #include <malloc.h>/************************************************** ①函数名: CreateList 功 能: 用数组构建顺序表 参 数: ①SqList *&L:传入的线性表 ②ElemType a[]:使用…

Iterator对象功能学习

package config;import java.util.Iterator; import java.util.Properties; import java.util.Set;/*** 这个类演示了如何使用Properties类来存储和访问键值对。* Properties类继承自Hashtable&#xff0c;因此它可以用来存储键值对数据&#xff0c;且支持同步。*/ public clas…

代码随想录算法训练营第十七天|110、平衡二叉树

110. 平衡二叉树 已解答 简单 相关标签 相关企业 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&am…

计算机二级Python基础操作题

题目来源&#xff1a;计算机二级Python半个月抱佛脚大法&#xff08;内呈上真题版&#xff09; - 知乎 第4&#xff0c;5&#xff0c;6&#xff0c;7&#xff0c;9&#xff0c;10&#xff0c;11套 1. 基础题1 sinput() print("{:\"^30x}".format(eval(s))) b …

安科瑞ADL逆流检测多功能电表

ADL400 导轨式多功能电能表&#xff0c;是主要针对电力系统&#xff0c;工矿企业&#xff0c;公用设施的电能统计、管理需求而设计的一款智能仪表&#xff0c;产品具有精度高、体积小、安装方便等优点。集成常见电力参数测量及电能计量及考核管理&#xff0c;提供上48月的各类电…

2024年企业上云补贴,免费领取腾讯云优惠代金券

腾讯云代金券领取渠道有哪些&#xff1f;腾讯云官网可以领取、官方媒体账号可以领取代金券、完成任务可以领取代金券&#xff0c;大家也可以在腾讯云百科蹲守代金券&#xff0c;因为腾讯云代金券领取渠道比较分散&#xff0c;腾讯云百科txybk.com专注汇总优惠代金券领取页面&am…

springboot实现文件上传

SpringBoot默认静态资源访问方式 首先想到的就是可以通过SpringBoot通常访问静态资源的方式&#xff0c;当访问&#xff1a;项目根路径 / 静态文件名时&#xff0c;SpringBoot会依次去类路径下的四个静态资源目录下查找&#xff08;默认配置&#xff09;。 在资源文件resour…

3·15日,上海飞北京,东航全球首架C919亲测初体验

引言&#xff1a;“望闻问切”亲测 感受C919机型的航班 【阿明观察 &#xff5c; 科技热点关注】 赶巧了&#xff01;2024年3月15日消费者权益日这天&#xff0c;上海飞北京&#xff0c;我选择了采用C919的东方航空公司航班。 真赶巧了&#xff01;上了飞机后我才知道&…

Spring Boot项目中使用MyBatis连接达梦数据库6

在开发中&#xff0c;使用Spring Boot框架结合MyBatis来操作数据库是一种常见的做法。本篇博客将介绍如何在Spring Boot项目中配置MyBatis来连接达梦数据库6&#xff0c;并提供一个简单的示例供参考。(达梦六不仅分表还分模式.) 我拿SYSTEM表的LPS模式下面Student表做案例。 1.…

GPT中使用的Gaussian Error Linear Unit (GELU)

GPT中使用的Gaussian Error Linear Unit (GELU) flyfish 高斯误差线性单元&#xff08;GELU&#xff09; Gaussian Error Linear Unit g e l u ( x ) x P ( X ≤ x ) X ∼ N ( 0 , 1 ) gelu(x) x P(X \leq x) \qquad X \sim \mathcal{N}(0, 1) gelu(x)xP(X≤x)X∼N(0,1) G…

2024年【山东省安全员C证】免费试题及山东省安全员C证作业考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 山东省安全员C证免费试题根据新山东省安全员C证考试大纲要求&#xff0c;安全生产模拟考试一点通将山东省安全员C证模拟考试试题进行汇编&#xff0c;组成一套山东省安全员C证全真模拟考试试题&#xff0c;学员可通过…

【Qt】使用Qt实现Web服务器(六):QtWebApp用户名密码登录

1、示例 1)演示 2)登录 3)显示 2、源码 示例源码Demo1->LoginController void LoginController::service(HttpRequest& request, HttpResponse& response) {

【Linux】/proc文件系统

&#x1f525;博客主页&#xff1a;PannLZ &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 文章目录 /proc文件系统1.获取与进程相关的信息:/proc/ID2./proc 目录下的系统信息3. 访问/proc 文件4.动态创建/proc文件系统4.1创建目录4.2创建proc…

双点双向路由引入实验

双点双向路由引入实验 1、OSPF和ISIS路由协议的优先级分别是什么&#xff1a;OSPF&#xff1a;10&#xff0c;150&#xff0c;ISIS&#xff1a;15&#xff0c;15 2、加表原则&#xff1a;当不同的路由协议学习到相同的目的网络&#xff0c;比较优先级&#xff0c;优先级数值小…

普发Pfeiffer分子泵TMH-U1001PC-1601PC安装使用维护说明

普发Pfeiffer分子泵TMH-U1001PC-1601PC安装使用维护说明

2024年【化工自动化控制仪表】考试试卷及化工自动化控制仪表模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 化工自动化控制仪表考试试卷是安全生产模拟考试一点通总题库中生成的一套化工自动化控制仪表模拟考试题&#xff0c;安全生产模拟考试一点通上化工自动化控制仪表作业手机同步练习。2024年【化工自动化控制仪表】考试…