[ 网络 ] 应用层协议 —— HTTP协议

news2024/11/15 9:55:22

目录

1.HTTP协议

1.1URL

urlencode和urldecode

2. HTTP协议格式

HTTP请求

HTTP响应

3.告知服务器意图的HTTP方法

GET:获取资源

POST:传输实体主体

GET和POST的区别

使用Cookie的状态管理

4.返回结果的HTTP状态码

状态码告知从服务器端返回的请求结果

2XX成功

3XX重定向

4XX客户端错误

5XX服务器错误

 5.HTTP的缺点


1.HTTP协议

应用层协议已经有大佬定义了一些现成的,有非常好用的应用层协议,我们可以直接参考使用。例如本篇所提到的HTTP(超文本传输协议)就是其中之一。

1.1URL

URL(统一资源定位符)就是我们俗称的"网址"

我们所常见到的网址:例如 https://www.baidu.com/ 是域名,这种字符串风格的域名,具有更好的字描述性。域名在解析时必须被转换成为IP地址,要访问网络服务,又必须具有port.

协议方案名和服务器端口号是强绑定的:

比如httpserver --- 80 ; httpsServer  --- 443 ; sshd --- 22

HTTP协议的本质是要获得某种"资源",比如我们请求百度的官网时,我们所获取的资源是百度首页的网页信息。我们可以理解为HTTP是 获取网页资源的(视频,音乐等)。HTTP是向特定的服务器向特定端口申请特定的"资源"的,获取到本地进行展示或者某种展示的。而对应服务器上,你所要的资源所在的位置就是URL中带层次的文件路径。

实际上,上网的大部分行为,都在进行这进程间通信。既然是通信,就是获取信息和发送信息。所以我们对应到生活中,大部分的上网行为,无非两种:

  1. 把服务器上面的资源数据拿到本地(短视频,小说等等)
  2. 把本都的数据推送到服务器(搜索,注册,登录,下单等)

urlencode和urldecode

在URL中,像 / ? 等这样的字符已经被URL当做特殊意义理解了。因此这些字符不能随意出现。

 比如:某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义。

转义规则:将需要转码的字符转为16进制,然后从右到做,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。

我们可以看到 "C++" 中 "+"被转义成了 "%2B" 我们可以使用urlencode工具验证上述过程

UrlEncode编码/UrlDecode解码  |  urldecode就是urlencode的逆过程。

2. HTTP协议格式

HTTP请求

  •  首行:【方法】+【URL】+【版本】
  • Header:请求的属性,冒号分割的键值对;每组属性之间使用\n分割;遇到空行表示Header部分结束
  • Body:空行后面的内容都是Body.Body允许为空字符串.如果Body存在,则在Header中会有一个Content-Length字段用来表示Body的长度

常规情况下,HTTP(HTTPS)底层使用的传输层协议是TCP.

我们通过一段tcp套接字编程来查看HTTP请求格式

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>

using namespace std;
int main()
{
    int listen_sock = socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock < 0){
        std::cout<<"socket error" <<std::endl;
        return 1;
    }

    struct sockaddr_in local;
    memset(&local,0,sizeof(local));

    local.sin_family = AF_INET;
    local.sin_port = htons(8082);
    local.sin_addr.s_addr = INADDR_ANY;

    if(bind(listen_sock,(struct sockaddr*)&local, sizeof(local))<0){
        std::cout<<"bind error" << std::endl;
        return 2;
    }
    if(listen(listen_sock,5) < 0){
        std::cout<<"listen error" << std::endl;
        return 3;
    }

    struct sockaddr_in peer;
    for(;;){
        socklen_t len = sizeof(peer);
        int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
        if(sock < 0){
            std::cout<<"accept error "<<std::endl;
            continue;
        }

        
        if(fork() == 0){
            if(fork() > 0) exit(0);
            close(listen_sock);

            char buffer[1024];
            recv(sock,buffer,sizeof(buffer),0);
            std::cout<<"###################HTTP request begin####################"<<std::endl;
            std::cout<< buffer << std::endl;
            std::cout<<"###################HTTP request end####################"<<std::endl;
            exit(0);
        }

        close(sock);
        waitpid(-1,nullptr,0);
    }
}

我们在直接打印出请求的格式

第一部分 首行:请求方法 请求url HTTP协议的版本

常用的请求方法:GET和POST (后面详解)

刚刚我们请求的是 /  . " / " 是Web根目录不是系统根目录。那我们也可以请求 /a/b/c/d.html 我们再次看看请求报文:

 

第二部分是一组Key:value的请求报头

请求报头是一堆Key: Value 请求属性,包括是否需要长链接,浏览器的编码类型,数据类型,我们想发送给服务器的相关信息等等.....通常存在多行,是一堆的Key: Value值

服务器端可以按行循环读取,一直读到\n (空行)就证明已经把报头读完了

第三部分:空行 

是报头和有效载荷的分离符,为了就是将报头和有效载荷进行分离

 前三部分都必须是按行方式陈列的

第四部分:请求正文(有效载荷)  ——非必须 | 可以没有

根据我们的需求,有时候我们需要登录账号和密码,个人信息,音乐,视频等等一般都是用户的相关信息或者数据。

以上就是HTTP协议的请求(HTTP request)。

HTTP响应

  • 首行: 【版本号】 + 【状态码】 + 【状态码解释】
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.

HTTP响应也是由4部分组成,其中响应正文也是可以被省略的。客户端如何判断已经将response报头读取完毕呢,仍然是客户端可以循环按行读取,知道读取到空行。

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fstream>

using namespace std;
int main()
{
    int listen_sock = socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock < 0){
        std::cout<<"socket error" <<std::endl;
        return 1;
    }

    struct sockaddr_in local;
    memset(&local,0,sizeof(local));

    local.sin_family = AF_INET;
    local.sin_port = htons(8083);
    local.sin_addr.s_addr = INADDR_ANY;

    if(bind(listen_sock,(struct sockaddr*)&local, sizeof(local))<0){
        std::cout<<"bind error" << std::endl;
        return 2;
    }
    if(listen(listen_sock,5) < 0){
        std::cout<<"listen error" << std::endl;
        return 3;
    }

    struct sockaddr_in peer;
    for(;;){
        socklen_t len = sizeof(peer);
        int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
        if(sock < 0){
            std::cout<<"accept error "<<std::endl;
            continue;
        }

        
        if(fork() == 0){
            if(fork() > 0) exit(0);
            close(listen_sock);

            char buffer[1024];
            recv(sock,buffer,sizeof(buffer),0);
            // std::cout<<"###################HTTP request begin####################"<<std::endl;
            // std::cout<< buffer << std::endl;
            // std::cout<<"###################HTTP request end####################"<<std::endl;

#define PAGE "./wwwroot/index.html"
            
            std::ifstream in(PAGE);
            if(in.is_open()){
                in.seekg(0,std::ios::end);
                size_t len = in.tellg();
                in.seekg(0,std::ios::beg);

                char *file = new char[len];
                in.read(file,len);
                in.close();

                std::string status_line = "http/1.0 200 OK\n";
                std::string response_header = "Content-Length: "+std::to_string(len);
                response_header+="\n";
                std::string blank = "\n";

                send(sock,status_line.c_str(),status_line.size(),0);
                send(sock,response_header.c_str(),response_header.size(),0);
                send(sock,blank.c_str(),blank.size(),0);

                send(sock,file,len,0);
                delete[] file;
            }
            close(sock);
            exit(0);
        }

        close(sock);
        waitpid(-1,nullptr,0);
    }
}

  

3.告知服务器意图的HTTP方法

在众多的HTTP方法中最常用的是GET和POST方法,因此在此我们对GET和POST进行详细了解

GET:获取资源

GET方法是用来请求访问已被URL识别的资源。指定的资源经服务器端解析后返回响应内容。

我们也可以使用Postman工具抓取HTTP请求

POST:传输实体主体

POST方法是用来传输实体的主体

虽然用GET方法也可以传输实体的主体,但是一般不用GET方法进行传输,而是用POST方法。虽然POST的功能和GET很相似,但是POST的主要目的并不是获取响应的主体内容。

GET和POST的区别

  • GET方法可以带参,参数在URL " ?"的后面
  • POST方法通过正文传参
  • GET方法传参不私密
  • POST方法因为通过正文传参,所以相对私密一些

GET通过url传参,POST通过正文传参,所以一般一些大的内容都是通过POST传参。

使用Cookie的状态管理

HTTP是无状态协议,它不对之前发生过的请求和响应的状态进行管理。也就是说,无法根据之前的状态进行本次的请求管理。那么我们在日常上网的过程中,假设要求登录认证的Web页面本身无法进行状态的管理(不记录已登录的状态),那么每次跳转新页面的时候都要再次登录,或者每次请求报文中附加参数来管理登录状态。那么这对我们用户是非常不友好的,就相当于我们每次登录C站我们都要进行登录认证。因此Cookie技术就是通过在请求和响应报文中写入Cookie信息来控制客户端的状态。

Cookie会根据从服务器端发送的响应报文内的一个叫Set-Cookie的首部字段信息,通知客户端保存Cookie。当下次客户端再往服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送出去。服务器端发现过来的Cookie后,会检查究竟是从哪一个客户端的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。

举例:我们用B站来进行举例。

我只要登录过B站之后,再之后再次登录B站时会自动登录我的账号, 点击网址左边的锁,就会看到Cookie,点进去就会看到当前页面下的Cookie信息,我们全部进行删除后点击完成,再次刷新该页面,就发现无法找到之前的登录信息了。登录后再次查询发现Cookie信息被重新填写上了。

 

 

4.返回结果的HTTP状态码

HTTP状态码负责表示客户端HTTP请求的返回结果,标记服务器端的处理是否正常,通知出现的错误等工作。

状态码告知从服务器端返回的请求结果

状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求还是出现了错误。

状态码的类别

2XX成功

2XX的响应结果表明请求被正常处理了。

例如:

  1. 200 OK表示从客户端发来的请求在服务器端被正常处理了。
  2. 204 No Content 表示服务器接受的请求已成功处理,但在返回的响应报文中不含有实体的主体部分,也就是说请求处理成功但是没有资源可以返回。因此返回204响应后浏览器的显示页面不会发生更新。

3XX重定向

  1. 301 Moved Permanently 永久重定向。该状态码表示请求的资源已被分配了新的URL,以后应使用现在所指的URL。
  2. 302 Found 临时重定向。该状态码表示请求的资源已被分配了新的URL,希望用户本次能使用新的URL访问。
    1. 302和301状态码相似,但是302状态码代表的资源不是永久移动,只是临时性质的。换句话说,302的资源对应的URL将来还有可能发生变。
        if(fork() == 0){
            if(fork() > 0) exit(0);
            close(listen_sock);

            char buffer[1024];
            recv(sock,buffer,sizeof(buffer),0);
            //重定向到腾讯网
            std::string response = "HTTP/1.1 301 Permanently Moved\r\n";
            response += "Location: https://www.qq.com/\r\n"; 
            response += "\r\n";
            send(sock, response.c_str(), response.size(), 0);
            close(sock);
            exit(0);
        }

当服务器启动之后在浏览器输入ip:port后按下回车发现URL自动跳转到了腾讯网

  

4XX客户端错误

  1. 403 Forbidden 表示请求资源的访问被服务器拒绝了。服务器端没有必要给出拒绝的详细理由。
    1. 发生403的原因:未获得文件系统的访问授权,访问权限出现某些问题(从未授权的发送源IP地址试图访问)
  2. 404 Not Found 表示服务器上无法找到请求的资源。除此之外,也可能在服务器端拒绝请求且不想说明理由时使用。

5XX服务器错误

5XX的响应结果表名服务器本身发生错误

  1. 500 Internal Server Error 表示服务器端在执行请求时发生了错误。也可能是Web应用存在的Bug或某些临时的故障。

 5.HTTP的缺点

HTTP主要有如下不足之处:

  1. 通信使用明文(不加密),内容可能会被窃听
  2. 不验证通信方的身份,因此有可能遭遇伪装
  3. 无法证明报文的完整性,所以有可能已经遭到篡改

因此解决如上三个不足之处正是HTTPS的主要功能,因此HTTPS = HTTP+加密+认证+完整性保护。具体3个功能的实现细节,将单独整理成一篇博客HTTPS

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

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

相关文章

十连胜!实在智能×浙江省十家农商行,数字科技赋能普惠金融

近日&#xff0c;中共中央、国务院印发了《数字中国建设整体布局规划》&#xff08;以下简称《规划》&#xff09;。《规划》指出&#xff0c;建设数字中国是数字时代推进中国式现代化的重要引擎&#xff0c;是构筑国家竞争新优势的有力支撑。全面提升数字中国建设的整体性、系…

C语言-基础了解-23-C预处理器

C预处理器 一、C预处理器 C 预处理器不是编译器的组成部分&#xff0c;但是它是编译过程中一个单独的步骤。简言之&#xff0c;C 预处理器只不过是一个文本替换工具而已&#xff0c;它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器&#xff08;C Prepr…

spring-security2

参考b站up主&#xff1a;传送门 前沿 &#xff1a; 回顾上章&#xff1a;最顶层就是一个过滤器 SpringSecurity流程&#xff1a;FilterChainProxy->根据请求决定一个 SecurityFilterChain (挨个调用其matches方法 匹配请求能否处理)-> 执行SecurityFilterChain中的一系列…

Java简单认识泛型——图文详解

写在开头:想必大家和博主一样&#xff0c;在以往学习JavaSE的语法中&#xff0c;遇到了一个陌生的词——泛型&#xff0c;博主当时很好奇&#xff0c;什么是泛型呢&#xff1f;即使是学完了JavaSE&#xff0c;这个问题都没有解决&#xff0c;只能在百度查阅了解关于泛型的一些皮…

Maven依赖的基本概念

目录 1.依赖的基本配置 2.依赖范围 3.传递性依赖 1.依赖的基本配置 根元素project下的dependencies可以包含多个 dependence元素&#xff0c;以声明多个依赖。每个依赖都应该包含以下元素&#xff1a; 1. groupId, artifactId, version : 依赖的基本坐标&#xff0c; 对于任…

【C和C++】输出100内能够被13整除的数,取模判断方法

目录 前言基础概念重温整除例子小知识点收尾前言 在软件行业已经有快十年,技术虽然一般般,但是足够应付和解决编程入门的相关问题! 都说十年磨一剑,积累到一定经验,是时候发挥自己的价值,给予入门的同行些许的帮助! 为什么要写收费专栏,其实原因很简单,时间就是金钱(…

聚观早报 |拼多多跨境电商业务正式登陆澳洲;中国加快6G网络研发

今日要闻&#xff1a;拼多多跨境电商业务正式登陆澳洲&#xff1b;全球自动驾驶公司排名特斯拉垫底&#xff1b;中国将加快 6G 网络研发&#xff1b;B站再次“崩”上热搜&#xff01;已闪电修复&#xff1b;微软将必应AI聊天每次对话上限增加至8条拼多多跨境电商业务正式登陆澳…

大神教你在 Linux 中查看你的时区

在这篇短文中&#xff0c;我们将向你简单介绍几种 Linux 下查看系统时区的简单方法。在 Linux 机器中&#xff0c;尤其是生产服务器上的时间管理技能&#xff0c;是在系统管理中一个极其重要的方面。Linux 包含多种可用的时间管理工具&#xff0c;比如 date 或 timedatectlcomm…

自动化测试——执行javaScript脚本

文章目录一、点击元素(对应的click())二、input标签对应的值&#xff08;对应的send_keys()&#xff09;修改时间控件的属性值&#xff1a;三、元素的文本属性四、js脚本滚动操作一、点击元素(对应的click()) 使用场景&#xff1a;当使用显性等待不能解决问题时 代码中实现点击…

[神经网络]DETR目标检测网络

一、概述 相较于传统目标检测&#xff0c;DETR是一种纯端到端的网络。它不再需要NMS(非极大值抑制&#xff0c;用于去除多余的预测框)和生成anchor。 DETR提出了一个新的目标函数&#xff08;二分图匹配&#xff09;&#xff0c;这个函数可以强制网络输出一个独一无二的预测值&…

【Unity大气渲染】Unity Shader中实现大气散射(半成品)

写在前面 这是之前在做天空盒的时候同步写的分析博客&#xff0c;结果后面写到一半就忘了继续了&#xff0c;这里先贴出当时写的半成品&#xff0c;有小伙伴问我怎么做的&#xff0c;这里只能尽力把之前的半成品先放出来了&#xff08;写得很乱&#xff0c;勿怪orz&#xff09…

用C语言实现一个任意类型的队列

下面是一个简单的无类型队列的实现&#xff1a; #include <stdio.h> #include <stdlib.h>typedef struct Node {void *data;struct Node *next; } Node;typedef struct Queue {Node *front;Node *rear;int size; } Queue;void enqueue(Queue *queue, void *data);…

[蓝桥杯] 枚举、模拟和排列问题

文章目录 一、连号区间数 1、1 题目描述 1、2 题解关键思路与解答 二、递增三元组 2、1 题目描述 2、2 题解关键思路与解答 三、错误票据 3、1 题目描述 3、2 题解关键思路与解答 四、回文日期 4、1 题目描述 4、2 题解关键思路与解答 五、归并排序 标题&#xff1a;蓝桥杯——…

windows和linux出现timewait过多的解决方法

一、timewait出现在客户端还是服务端以及什么情况下出现 我是做性能测试的。在压测过程中遇到了timewait过多的情况&#xff0c;下面来看一下timewait产生的原因及解决办法&#xff0c;我自己在服务器起了一个很简单的springboot应用来验证自己的猜想及解决办法。 说到产生原…

基于docker部署prometheus

1、prometheus架构 Prometheus Server: 收集指标和存储时间序列数据&#xff0c;并提供查询接口 ClientLibrary:客户端库 Push Gateway: 短期存储指标数据。主要用于临时性的任务 Exporters:采集已有的第三方服务监控指标并暴露 metrics Alertmanager:告警 Web UI :简单的…

VS插件CodeRush全新发布v22.2.4——改进对VS 17.5的支持

CodeRush是一个强大的Visual Studio .NET 插件&#xff0c;它利用整合技术&#xff0c;通过促进开发者和团队效率来提升开发者体验。CodeRush能帮助你以极高的效率创建和维护源代码。Consume-first 申明&#xff0c;强大的模板&#xff0c;智能的选择工具&#xff0c;智能代码分…

【C++升级之路】第九篇:vector

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【C学习与应用】 ✒️✒️本篇内容&#xff1a;vector的基本概念、vector的使用&#xff08;构造&#xff0c;迭代器&#xff0c;空间增长&#xff0c;增删…

小樽C++ 单章③ 一维数组

目录 一、一维数组认识与使用 1.4 数组的输入与输出 1.5 最大值与最小值的求解 二、一维数组的操作用法 2.1 数组的插入与删除 2.2 数组某个值的查找 2.3多个数组的合并 2.4多个数组的有序合并 三、一维数组的应用 3.1 斐波那契数列 3.2 淘淘摘苹果 3.3 翻纸牌游戏…

RTOS中互斥量的原理以及应用

互斥量的原理 RTOS中的互斥量是一种同步机制&#xff0c;用于保护共享资源&#xff0c;防止多个任务同时访问该资源&#xff0c;从而避免数据竞争和不一致性。 互斥量的原理是通过对共享资源进行加锁和解锁操作来实现的。 在RTOS中&#xff0c;互斥量通常是一个数据结构&…

为什么说要慎用BeanUtils,因为性能真的拉跨

1 背景之前在专栏中讲过“不推荐使用属性拷贝工具”&#xff0c;推荐直接定义转换类和方法使用 IDEA 插件自动填充 get / set 函数。不推荐的主要理由是&#xff1a;有些属性拷贝工具性能有点差有些属性拷贝工具有“BUG”使用属性拷贝工具容易存在一些隐患&#xff08;后面例子…