HTTP协议

认识URL

平时我们俗称的 "网址" 其实就是说的 URL

举例说明

https://blog.csdn.net/m0_57249790?spm=1000.2115.3001.5343

各个字段解析

https : 协议方案名

blog.csdn.net :服务器地址/域名

/m0_57249790:带层次的文件路径

spm=1000.2115.3001.5343: 查询字符串

http的本质

通过http协议从服务器拿下来对应的资源。

什么是资源呢?

一切你在网络上看到的都是文件(视频、音乐、文章等等)。
这些文件存放在你的服务器上 -->因此需要Linux系统的路径结构来从
服务器上拿到这些资源。
因为文件资源种类特别多,http都能搞定,所以:http叫做超文本传输协议!

urlencode和urldecode

客户端的url中会对我们需要搜索的内容中特殊符号和汉字进行encode过程
服务器(软件)收到url请求就是自己对特殊符号和汉字进行decode的过程。

特殊字符的转义

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式

urlencode工具

UrlEncode编码/UrlDecode解码 - 站长工具

http请求的格式

状态码

例如:200/400/302/307/500/404

状态码描述

404 -> Not  Found    200 -> OK

1.请求和响应怎么保证应用层完整读取完毕了?

a.  我可以读取完整的一行
b. while(读取完整的一行) - 所有的请求行+请求报头全部读完 - 直到空行!
c. 我们能保证把报头读完,报头有一个属性:Content-Length:XXX正文长度

2.请求和响应是怎么做到序列化和反序列化的?

a. http自己实现的,第一行+请求/响应报头,只要按照\r\n将字符串1->n即可!
b. 正文不用做

实现简单的http服务器代码

服务端

#pragma once
#include <iostream>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include <functional>
#include <unordered_map>
#include "Protocol.hpp"
#include "Util.hpp"

using namespace std;

namespace server
{

    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };

    static const uint16_t gport = 8080;
    static const int gbacklog = 5;

    using func_t = function<bool(const HttpRequest &, HttpResponse &)>;

    class HttpServer
    {
    public:
        HttpServer(func_t func, const uint16_t &port = gport)
            : _func(func), _listensock(-1), _port(port)
        {
        }
        void initServer()
        {

            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                exit(SOCKET_ERR);
            }

            // 2.bind绑定自己的网络信息
            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(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                exit(BIND_ERR);
            }

            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0) // TODO
            {
                exit(LISTEN_ERR);
            }
        }
        // void registerCb(std::string servicename, func_t cb)
        // {
        //     funcs.insert(std::make_pair(servicename,cb));
        // }
        void HandlerHttp(int sock)
        {
            // 1. 读到完整的http请求
            // 2. 反序列化
            // 3. httprequest,httprespons,_func(req,resp)
            // 4. resp序列化
            // 5. send

            char buffer[4096];

            // 构建对象
            HttpRequest req;
            HttpResponse resp;

            // 大概率我们直接就能读取到完整的http请求
            ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
            // n>0说明读取成功
            if (n > 0)
            {
                buffer[n] = 0;
                req.inbuffer = buffer;
                // 对进行收到的字符串进行处理
                req.parse();
                // funcs[req.path](req,resp);
                _func(req, resp);
                
                send(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);
            }
        }

        void start()
        {

            for (;;)
            {
                // 4. server 获取新链接
                // sock 和客户端进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);

                if (sock < 0)
                {
                    cout << "sock: " << sock << endl;

                    continue;
                }

                // version 2
                pid_t id = fork();

                if (id == 0) // child
                {
                    close(_listensock); // 子进程中不需要监听因此关闭监听的文件描述符
                    if (fork() > 0)
                        exit(0);

                    HandlerHttp(sock);
                    close(sock);
                    exit(0);
                }
                close(sock);

                // father
                waitpid(id, nullptr, 0);
            }
        }

        ~HttpServer()
        {
        }

    private:
        int _listensock; // 不是用来进行数据通信的,它是用来监听链接到来,获取新链接的!
        uint16_t _port;
        func_t _func;
        // std::unordered_map<std::string, func_t> funcs;
    };
}

 客户端

#include "HttpServer.hpp"
#include <memory>

using namespace std;
using namespace server;

void Usage(string proc)
{
    cerr << "Usage:\n\t" << proc << " proc\r\n\r\n";
}

bool Get(const HttpRequest &req, HttpResponse &resp)
{
    
    cout << "------------------http start ------------------" << endl;
    cout << req.inbuffer << endl;
    cout << "------------------http end   ------------------" << endl;

    return true;
}



// ./httpServer 8080
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    // atoi 字符串转整型
    uint16_t port = atoi(argv[1]);

    unique_ptr<HttpServer> httpsvr(new HttpServer(Get, port));

    // httpsvr->registerCb("/",GET); // 功能路由
    // httpsvr->registerCb("/search",Search);
    // httpsvr->registerCb("/test.py",Other);
    httpsvr->initServer();
    httpsvr->start();

    return 0;
}

http协议的特点

由于http(超文本传输协议)  已经是一个非常成熟的协议了,因此我们直接使用网站(手机和电脑都可以)请求就可以让我们写的对应http服务端收到请求的消息。

硬编码网页回显

我们也可以硬编码一个网页,将该html代码发送给服务器,让服务器回显出来

std::string body = "<html lang=\"en\"><head><meta charset=\"UTF-8\"><title>for test</title><h1>hello world</h1></head><body><p>山河为碑,历史作证;硝烟已散,精神永存。如今,伟大抗战精神早已成为中华儿女的精神底色,薪火相传、历久弥新。</p></body></html>";

 分隔http请求

static std::string getOneLine(std::string &buffer, const std::string &sep)
{

    auto pos = buffer.find(sep);
    // 没找到标识符
    if (pos == std::string::npos)
        return "";

    // 截取到标识符之前的内容
    std::string sub = buffer.substr(0, pos);
    // buffer.erase(0, sub.size() + sep.size());

    // 返回截取到的标识符
    return sub;
}

POST和GET

我们进行数据提交的时候,本质前端要通过from表单提交,浏览器会自动将from表单中的内容转换成GET/POST方法。

区别

GET

GET通过url传递参数,具体:http://ip:port/XXX/YY?name=value&name2=value2

POST

POST提交参数通过http请求的正文提交参数!

私密性

POST方法通过正文提交参数,所以一般用户看不到,私密性更好, 私密性 != 安全性;
GET方法不私密;
无论是GET方法和POST方法,都不安全!要谈安全,必须加密!

GET通过URL传参,注定不能太大。
但是POST,通过正文,正文可以很大,甚至可以是其他的东西。

HTTP状态码

HTTP状态码是指在HTTP协议中,服务器向客户端返回的3位数字代码,用于表示请求的处理结果。常见的HTTP状态码有以下几种:

  • 1xx:信息类,表示请求已被接收,继续处理。
  • 2xx:成功类,表示请求已成功被服务器接收、理解和处理。
  • 3xx:重定向类,表示需要进一步操作以完成请求。
  • 4xx:客户端错误类,表示服务器无法处理请求。
  • 5xx:服务器错误类,表示服务器处理请求时发生了错误。

其中,一些常见的HTTP状态码包括:

  • 200 OK:请求成功。
  • 301 Moved Permanently:永久重定向。
  • 404 Not Found:资源未找到。
  • 500 Internal Server Error:服务器内部错误。

需要注意的是,HTTP状态码是由服务器返回给客户端的。

长连接

其实一张我们看到的网页,实际上可能由多种元素构成 ——一张完整的网页需要多次http请求;
http网页中可能包含多个元素,如果频繁发起http请求,http是基于tcp的,tcp面向链接的;
频繁创建链接的问题;

需要client和server都要支持,长连接,建立好一条链接,获取一大份资源的时候,通过一条链接完成。

长连接   ——   Connection: keep-alive
短连接  ——    Connection: close

会话保持

定位

会话保持严格意义不是http天然具备的,是后面使用发现需要的

什么叫做会话保持呢?

http协议是无状态的! 但是用户需要,因为用户查看新的网页是常规操作,如果发生网页跳转,那么新的页面也就无法识别是哪一个用户了,为了让用户一经登录,可以在整个网站,按照自己的身份进行随意访问,这就是会话保持。

cookie技术

把我们用户的输入信息:  用户名&&密码保存起来

往后只要访问同一个网站,浏览器会自动推送历史保留信息

这种技术叫做cookie技术。

内容

1.浏览器会自动记录客户信息
2.记录客户信息这个文件叫做cookie文件,当用户首次登录通过验证以后,用户的
账号密码信息可以直接被保存在cookie文件里面,在站内做跳转就不需要在填写用户账号密码了,
cookie分为文件级别和内存级别。

设置cookie

respheader += "Set-Cookie: name=1234567abcdefg; Max-Age=120\r\n";
name后面是我们需要保存的内容,Max-Age后面是我们所设置的时间(单位是秒)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁百万

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值