IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【Linux从0到1】第十四篇:HTTP&&HTTPS -> 正文阅读

[网络协议]【Linux从0到1】第十四篇:HTTP&&HTTPS


一、网络版计算器

约定方案一:

  • 客户端发送一个形如"1+1"的字符串;
  • 这个字符串中有两个操作数, 都是整形;
  • 两个数字之间会有一个字符是运算符, 运算符只能是 + ;
  • 数字和运算符之间没有空格

约定方案二:

  • 定义结构体来表示我们需要交互的信息;
  • 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
  • 这个过程叫做 “序列化” 和 “反序列化”

Protocol.hpp

#pragma once
#include <iostream>
#include <jsoncpp/json/json.h>

// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议
typedef struct Request
{
    int _x;
    int _y;
    char _op;   // "+-*/%"
}request_t;

typedef struct Response
{
    int code; /// server运算完毕的计算状态: code(0:success), code(-1: div 0) ...
    int _result; // 计算结果
}response_t;

// 序列化过程
std::string serializationRequest(request_t& req)
{
    Json::Value root;
    root["datax"] = req._x;
    root["datay"] = req._y;
    root["operator"] = req._op;

    Json::FastWriter writer;
    std::string json_string = writer.write(root);
    return json_string;
}

//反序列化
void DeserializationRequest(const std::string& json_string, request_t& out)
{
    Json::Reader reader;
    Json::Value root;
    reader.parse(json_string, root);
    out._x = root["datax"].asInt();
    out._y = root["datay"].asInt();
    out._op = (char)root["operator"].asUInt();
}

std::string serializationResponse(response_t& resp)
{
    Json::Value root;
    root["code"] = resp.code;
    root["result"] = resp._result;
    Json::FastWriter writer;
    std::string json_string = writer.write(root);
    return json_string;
}

void DeserializationResponse(const std::string& json_string, response_t& out)
{
    Json::Reader reader;
    Json::Value root;
    reader.parse(json_string, root);
    out.code = root["code"].asInt();
    out._result = root["result"].asInt();
}

sock.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>

class Sock
{
public:
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if(sock < 0)
        {
            std::cerr << "Socket error" << errno << std::endl;
            exit(2);
        }
        return sock;
    }

    static void Bind(int sock, u_int16_t port)
    {
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(port);

        if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            std::cerr << "Bind error" << errno << std::endl;
            exit(3);
        }
    }

    static void Listen(int sock)
    {
        if(listen(sock, 5) < 0)
        {
            std::cerr << "Listen error" << errno << std::endl;
            exit(4);
        }
    }

    static int Accept(int sock)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int fd = accept(sock, (struct sockaddr*)&peer, &len);
        if(fd >= 0)
        {
            return fd;
        }
        return -1;
    }

    static void Connect(int sock, std::string server_ip, uint16_t port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(sockaddr_in));
        peer.sin_family = AF_INET;
        peer.sin_addr.s_addr = inet_addr(server_ip.c_str());
        peer.sin_port = htons(port);
        
        if(connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0)
        {
            std::cerr << "connet failed" << errno << std::endl;
            exit(5);
        }
    }
};

cal_client.cc

#include "Protocol.hpp"
#include "Sock.hpp"
#include <unistd.h>
#include <cstring>

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " server_ip server_port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }

    std::string ip = argv[1];
    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));
    int sock = Sock::Socket();
    Sock::Connect(sock, ip, port);
    std::cout << "connect success!" << std::endl;

    request_t req;
    std::cout << "please input data one#" << std::endl;
    std::cin >> req._x;
    std::cout << "please input data two#" << std::endl;
    std::cin >> req._y;
    std::cout << "please input operator#" << std::endl;
    std::cin >> req._op;
    // 序列化
    std::string json_string = serializationRequest(req);
    write(sock, json_string.c_str(), json_string.size());
    // write(sock, &req, sizeof(req));
    char buffer[1024];
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        buffer[s] = 0;
        response_t resp;
        std::string str = buffer;
        DeserializationResponse(str, resp);
        std::cout << "code[0:success]:" << resp.code << std::endl;
        std::cout << "result:" << resp._result << std::endl;
    }

    // response_t resp;
    // memset(&resp, 0, sizeof(resp));

    // read(sock, &resp, sizeof(resp));

    // std::cout << "code[0:success]:" << resp.code << std::endl;
    // std::cout << "result:" << resp._result << std::endl;
    return 0;
}

cal_server.cc

#include "Protocol.hpp"
#include "Sock.hpp"
#include <string>
#include <thread>
#include <unistd.h>

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " server_port" << std::endl;
}

void HandlerRequest(int *args)
{
    int sock = *args;
    delete args;
    //业务逻辑, 做一个短服务 request-> 分析处理->构建response-> sent(response) -> colse(sock)
    // 1. 读取请求
    request_t req;
    char buffer[1024];
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if(s > 0)
    {
        buffer[s] = 0;
        std::string str = buffer;
        DeserializationRequest(str, req);

    //ssize_t s = read(sock, &req, sizeof(req));
    // if (s == sizeof(req))
    // {
        // 分析请求 && 计算结果
        response_t resp = {0, 0};

        switch (req._op)
        {
        case '+':
            resp._result = req._x + req._y;
            break;
        case '-':
            resp._result = req._x - req._y;
            break;
        case '*':
            resp._result = req._x * req._y;
            break;
        case '/':
            if (req._y == 0) // code = -1 除0错误
            {
                resp.code = -1;
            }
            else
            {
                resp._result = req._x / req._y;
            }
            break;
        case '%':
            if (req._y == 0) // code = -2 模0错误
            {
                resp.code = -2;
            }
            else
            {
                resp._result = req._x % req._y;
            }
            break;
        default: // code = -3 请求方法异常
            resp.code = -3;
            break;
        }
        // 构建链接,并且返回
        std::string json_str = serializationResponse(resp);
        write(sock, json_str.c_str(), json_str.size());
        // write(sock, &resp, sizeof(resp));
        std::cout << "服务结束!" << std::endl;
    // }
    }
    // 关闭套接字
    close(sock);
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    u_int16_t port = static_cast<u_int16_t>(atoi(argv[1]));
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    for (;;)
    {
        int new_sock = Sock::Accept(listen_sock);
        if (new_sock >= 0)
        {
            int *pram = new int(new_sock);
            std::thread t(HandlerRequest, pram);
            t.detach();
        }
    }
    return 0;
}

无论我们采用方案一, 还是方案二, 还是其他的方案, 只要保证, 一端发送时构造的数据, 在另一端能够正确的进行解析, 就是ok的. 这种约定, 就是 应用层协议

二、HTTP协议

虽然我们说, 应用层协议是我们程序猿自己定的.
但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)就是其中之一

2.1 认识URL

平时我们俗称的 “网址” 其实就是说的 URL
在这里插入图片描述
urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义

转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式
在这里插入图片描述
“+” 被转义成了 “%2B”
urldecode就是urlencode的逆过程;

urldecode解码工具

2.2 HTTP协议格式

在这里插入图片描述
Sock.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>

class Sock
{
public:
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if(sock < 0)
        {
            std::cerr << "Socket error" << errno << std::endl;
            exit(2);
        }
        return sock;
    }

    static void Bind(int sock, u_int16_t port)
    {
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(port);

        if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            std::cerr << "Bind error" << errno << std::endl;
            exit(3);
        }
    }

    static void Listen(int sock)
    {
        if(listen(sock, 5) < 0)
        {
            std::cerr << "Listen error" << errno << std::endl;
            exit(4);
        }
    }

    static int Accept(int sock)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int fd = accept(sock, (struct sockaddr*)&peer, &len);
        if(fd >= 0)
        {
            return fd;
        }
        return -1;
    }

    static void Connect(int sock, std::string server_ip, uint16_t port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(sockaddr_in));
        peer.sin_family = AF_INET;
        peer.sin_addr.s_addr = inet_addr(server_ip.c_str());
        peer.sin_port = htons(port);
        
        if(connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0)
        {
            std::cerr << "connet failed" << errno << std::endl;
            exit(5);
        }
    }
};

Http.cc

#include "Sock.hpp"
#include <string>
#include <thread>
#include <unistd.h>

void Usage(std::string proc)
{
    std::cout << "Usage\n\t" << proc << " prot" << std::endl;
}

void Handler(int *pSock)
{
    int sock = *pSock;
    delete pSock;
#define MAX_SIZE 1024 * 10
    char buffer[MAX_SIZE];
    memset(buffer, 0, sizeof(buffer));

    ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
    if (s > 0)
    {
        buffer[s] = 0;
        std::cout << buffer;

        std::string ret = "http/1.0 200 OK\n";
        ret += "Content-Type: text/plain\n"; //text/plain,正文是普通的文本
        ret += "\n";
        ret += "hello world";

        send(sock, ret.c_str(), ret.size(), 0);
    }
    close(sock);
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    //服务
    for (;;)
    {
        int new_sock = Sock::Accept(listen_sock);
        if (new_sock > 0)
        {
            int *pnew_sock = new int(new_sock);
            std::thread t(Handler, pnew_sock);
            t.detach();
        }
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

HTTP请求
在这里插入图片描述

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

HTTP响应
在这里插入图片描述

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

2.3 HTTP的方法

在这里插入图片描述
其中最常用的就是GET方法和POST方法
在这里插入图片描述
Http.cc

#include "Sock.hpp"
#include <string>
#include <thread>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fstream>

// wwwwroot下就是web根目录,wwwroot目录下放置的内容,都叫做资源
// wwwroot 目录下的index.html就叫做网站的首页

#define WWWROOT "./wwwroot/"
#define HOME_PAGE "index.html"

void Usage(std::string proc)
{
    std::cout << "Usage\n\t" << proc << " prot" << std::endl;
}

void Handler(int *pSock)
{
    int sock = *pSock;
    delete pSock;
#define MAX_SIZE 1024 * 10
    char buffer[MAX_SIZE];
    memset(buffer, 0, sizeof(buffer));

    ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
    if (s > 0)
    {
        buffer[s] = 0;
        std::cout << buffer;

    //     std::string ret = "http/1.0 200 OK\n";
    //     ret += "Content-Type: text/plain\n"; //text/plain,正文是普通的文本
    //     ret += "\n";
    //     ret += "hello world";

    //     send(sock, ret.c_str(), ret.size(), 0);
        std::string html_file = WWWROOT;
        html_file += HOME_PAGE;
        // 返回的时候不仅仅返回正文网页信息,还要包括http的请求
        std::string http_response = "http/1.0 200 OK\n";
        http_response += "Content-type: text/html; charset=utf-8\n";
        http_response += "Content-Lengt: ";
        struct stat st;
        stat(html_file.c_str(), &st);
        http_response += std::to_string(st.st_size);
        http_response += "\n";
        http_response += "\n";
        // 正文
        std::ifstream in(html_file);
        if(!in.is_open())
        {
            std::cerr << "open html_file error" << std::endl;
        }
        else
        {
            std::string content;
            std::string line;
            while(std::getline(in, line))
            {
                content += line;
            }
            http_response += content;
            in.close();
            send(sock, http_response.c_str(), http_response.size(), 0);
        }
    }
    close(sock);
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    //服务
    for (;;)
    {
        int new_sock = Sock::Accept(listen_sock);
        if (new_sock > 0)
        {
            int *pnew_sock = new int(new_sock);
            std::thread t(Handler, pnew_sock);
            t.detach();
        }
    }
    return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4 HTTP的状态码

在这里插入图片描述
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

  • 301:永久重定向
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 302 or 307 临时重定向
    在这里插入图片描述

重定向是浏览器给我们提供支持的浏览器必须识别301,302,307
server告诉浏览器我应该再去哪里;Location:新的地址

2.5 HTTP常见Header

  • Content-Type: 数据类型(text/html等)
  • Content-Length: Body的长度
  • Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
  • User-Agent: 声明用户的操作系统和浏览器版本信息;
  • referer: 当前页面是从哪个页面跳转过来的;
  • location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
  • Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;

Connection:keep-alive
在这里插入图片描述
Cookie
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
Session:
在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。有关使用Session 对象的详细信息,请参阅“ASP应用程序”部分的“管理会话”。注意会话状态仅在支持cookie的浏览器中保留。
在这里插入图片描述

三、HTTPS

http理解思路图
在这里插入图片描述
假设采取对称加密:
在这里插入图片描述

假设采取非对称加密
在这里插入图片描述
最终方案:对称加加密+非对称加密

在这里插入图片描述
但是这样也会不安全
在这里插入图片描述
上面的本质问题是了client无法判断发来的密匙协商报文是不是合法的服务方发来的,所以就会有CA证书机构,来证明这个服务方是合法的。
在这里插入图片描述
在这里插入图片描述

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:50:39  更:2022-07-21 21:52:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:27:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码