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

目录

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.mfbz.cn/a/1986.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三月份跳槽了,历经字节测开岗4轮面试,不出意外,被刷了...

大多数情况下&#xff0c;测试员的个人技能成长速度&#xff0c;远远大于公司规模或业务的成长速度。所以&#xff0c;跳槽成为了这个行业里最常见的一个词汇。 前几天&#xff0c;我看到有朋友留言说&#xff0c;他在面试字节的测试开发工程师的时候&#xff0c;灵魂拷问三小…

【Shell】脚本

Shell脚本脚本格式第一个Shell脚本&#xff1a;hello.sh脚本常用执行方式1. bash或sh脚本的相对路径或绝对路径2. 输入脚本的绝对路径或相对路径3. 在脚本的路径前加上.或者source脚本格式 脚本以#!/bin/bash开头&#xff08;指定解析器&#xff09; #! 是一个约定的标记&…

让 new bing 使用 GPT-4 编写一个令人满意的程序全过程赏析

让 new bing 使用 GPT-4 编写一个令人满意的程序全过程赏析 标签&#xff1a;new bing、GPT-4 文章目录让 new bing 使用 GPT-4 编写一个令人满意的程序全过程赏析前言1 让 bing 编写一个画螺旋线的程序1.1 我的要求&#xff08;1&#xff09;1.2 bing 的回答全文&#xff08;…

p81 红蓝对抗-AWD 监控不死马垃圾包资源库

数据来源 注意&#xff1a;一下写的东西是在p80 红蓝对抗-AWD 模式&准备&攻防&监控&批量这篇文章的基础上进行的 演示案例&#xff1a; 防守-流量监控-实时获取访问数据包流量 攻击-权限维持-不死脚本后门生成及查杀 其他-恶意操作-搅屎棍发包回首掏共权限…

WPF 认识WPF

什么是WPF?WPF是Windows Presentation Foundation(Windows展示基础)简称&#xff0c;顾名思义是专门编写表示层的技术。WPF绚丽界面如下&#xff1a;GUI发展及WPF历史&#xff1f;Windows系统平台上从事图形用户界面GUI(Graphic User Interface)已经经历了多次换代&#xff0c…

web前端开发和后端开发哪个难度大?

前言 因为涉及到的具体的应用的领域不同&#xff0c;所以说不能简单地说哪一个难&#xff0c;对于前端而言你会感觉到入门会非常的简单&#xff0c;这也是会给许多人一种错觉&#xff0c;前端很简单&#xff0c;但是只能说是在入门理解上是有利于新手的&#xff0c;前端在主要…

二叉树系统刷题1

文章目录**BM26** **求二叉树的层序遍历****BM27** **按之字形顺序打印二叉树****BM28** **二叉树的最大深度****BM29** **二叉树中和为某一值的路径(一)****BM30** **二叉搜索树与双向链表****BM31** **对称的二叉树****BM32** **合并二叉树****BM34** **判断是不是二叉搜索树…

【数据结构】KMP算法细节详解

KMP算法细节详解前言一、字符串匹配问题1.BF算法2.KMP算法二、next数组三、手写nex思想四、机算next思想五、next数组细节理解六、nextVal数组七、KMP算法代码实现八、nextVal数组代码实现完结前言 KMP算法是为了字符串匹配问题而被研究出来的&#xff0c;字符串匹配问题就是查…

真实的软件测试日常工作是咋样的?

最近很多粉丝问我&#xff0c;小姐姐&#xff0c;现在大环境不景气&#xff0c;传统行业不好做了&#xff0c;想转行软件测试&#xff0c;想知道软件测试日常工作是咋样的&#xff1f;平常的工作内容是什么&#xff1f; 别急&#xff0c;今天跟大家细细说一下一个合格的软件测…

【LeetCode每日一题】——面试题17.21.直方图的水量

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【时间频度】八【代码实现】九【提交结果】一【题目类别】 双指针 二【题目难度】 困难 三【题目编号】 面试题17.21.直方图的水量 四【题目描述】 给定一个直方图(也称…

Android Studio 中使用 Gradle 配置多渠道打包 配置不同的渠道名称 配置不同的App名称 配置不同的Logo

废话三种操作都是可以混合一起用的&#xff0c;本来也不是很难的事情&#xff0c;为了方便分别理解&#xff0c;这里我就分开处理了。如果需要将打包出来的apk的名称自动命名成指定格式&#xff0c;也可以进行配置&#xff0c;我这里没这个需求&#xff0c;所以这里就不讨论了。…

晶晨S905D3切换到外部phy方法

文章目录 前言一、s905d3的以太网驱动的理解二、修改设备树注意前言 随着芯片的国产化推荐,越来越多的国产芯片被大家重视起来,但是国产的一些稍微高性能的芯片资料太少,这里把调实phy的流程记录一下,不做太多的理论分析 一、s905d3的以太网驱动的理解 如果拿到sdk后,默…

ESP32设备驱动-ADXL335加速计驱动

ADXL335加速计驱动 文章目录 ADXL335加速计驱动1、ADXL335介绍2、硬件准备3、软件准备4、驱动实现1、ADXL335介绍 ADXL335 是一款小型、薄型、低功耗、完整的 3 轴加速度计,具有信号调理电压输出。 该产品以 3 g 的最小满量程测量加速度。它可以测量倾斜传感应用中的静态重力…

JAVASE/封装、继承、多态

博客制作不易&#xff0c;欢迎各位点赞&#x1f44d;收藏⭐关注前言在学习面向对象编程语言时&#xff0c;封装、继承、多态则是我们必须学习和使用的三大特征。本文通过举例&#xff0c;说明了该三大特征的基本权限特点。一、访问限定符范围private默认权限protectedpublic同一…

springcloud3 nacos,sentinel,ribbon,openfegin等整合案例4[fallback+blockhandler完美整合]

一 说明 1.1 结论 SentinelResource(value "fb",fallback "handlerFallback") //fallback只负责业务异常 SentinelResource(value "fb",blockHandler "blockHandler") //blockHandler只负责sentinel控制台配置违规 假设fallbac…

国内版的ChatGPT弯道超车的机会在哪里?

前言 从去年11月最后一天ChatGPT诞生&#xff0c;截至目前&#xff0c;ChatGPT的热度可谓是爆了。众所周知&#xff0c;ChatGPT是美国“开放人工智能研究中心”研发的聊天机器人程序&#xff0c;它是一个人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过学习和理解人…

Android性能优化的底层逻辑

前言 性能优化仿佛成了每个程序员开发的必经之路&#xff0c;要想出人头地&#xff0c;与众不同&#xff0c;你还真需要下点功夫去研究Android的性能优化&#xff0c;比如说&#xff0c;从优化应用启动、UI加载、再到内存、CPU、GPU、IO、还有耗电等等&#xff0c;当你展开一个…

Docker部署springcloud项目(清晰明了)

概述 最近在想做个cloud项目,gitee上找了个模板项目&#xff0c;后端使用到 Nacos、Gateway、Security等技术&#xff0c;需要到 Docker 容器部署&#xff0c;在此总结一下&#xff0c;若有不足之处&#xff0c;望大佬们可以指出。 什么是 Docker Docker 使用 Google 公司推…

【8】核心易中期刊推荐——人工智能与机器人

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

【C++】Google编码风格学习

Google规范线上地址&#xff1a;https://zh-google-styleguide.readthedocs.io/en/latest/ 文章目录1. 头文件2. 作用域3. 类4. 函数5. 其他C特性6. 命名约定7. 注释8. 格式1. 头文件 每个cpp/cc文件都对应一个h头文件&#xff0c;除单元测试代码和只包含main()的文件外。 所…
最新文章