Http详解

网络版计算器

例如 , 我们需要实现一个服务器版的加法器 . 我们需要客户端把要计算的两个加数发过去 , 然后由服务器进行计算 , 最后再把结果返回给客户端.
约定方案一 :
客户端发送一个形如 "1+1" 的字符串 ;
这个字符串中有两个操作数 , 都是整形 ;
两个数字之间会有一个字符是运算符 , 运算符只能是 + ;
数字和运算符之间没有空格;
约定方案二 :
定义结构体来表示我们需要交互的信息 ;
发送数据时将这个结构体按照一个规则转换成字符串 , 接收到数据的时候再按照相同的规则把字符串转 化回结构体;
这个过程叫做 " 序列化 " " 反序列化 "

我们用方案二来实现,方案一没有啥意义。需要安装第三方库jsoncpp

sudo yum install -y jsoncpp-devel

json,xml,protcolbuff都是用来序列化的组件。
NetServer.cc
#include <pthread.h>
#include "Protocol.hpp"
#include "Sock.hpp"
using namespace std;
#include <unistd.h>
#include <functional>
#include <map>
static void Usage(string proc)
{
    cout << "Usage: " << proc << " port" << endl;
    exit(1);
}

void *HandlerRequest(void *args)
{
    int sock = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    // version1 原生方法,没有明显的序列化和反序列化的过程
    // 业务逻辑, 做一个短服务
    // request -> 分析处理 -> 构建response -> sent(response)->close(sock)
    // 1. 读取请求
   std::map<char, std::function<std::pair<int, int>(int, int)>> mp;
	mp['+'] = [](int a, int b)->std::pair<int,int>{return make_pair(0, a + b); };
	mp['-'] = [](int a, int b)->std::pair<int, int>{return make_pair(0, a - b); };
	mp['*'] = [](int a, int b)->std::pair<int, int>{return make_pair(0, a * b); };
	mp['%'] = [](int a, int b)->std::pair<int, int>{if (b == 0)  return make_pair(-1, 0);
                                                    else  return make_pair(0, a % b);};
	mp['/'] = [](int a, int b)->std::pair<int, int>{if (b == 0) return make_pair(-2, 0);
                                                    else  return make_pair(0, a / b); };
    char buffer[1024];
    request_t req;
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        buffer[s] = 0;
        cout << "get a new request: " << buffer << endl;
        std::string str = buffer;
        DeserializeRequest(str, req); //反序列化请求

        // request_t req;
        // ssize_t s = read(sock, &req, sizeof(req));
        // if (s == sizeof(req))
        // {
        //读取到了完整的请求,待定
        // req.x , req.y, req.op
        // 2. 分析请求 && 3. 计算结果
        // 4. 构建响应,并进行返回
        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)
        //         resp.code = -1; //代表除0
        //     else
        //         resp.result = req.x / req.y;
        //     break;
        // case '%':
        //     if (req.y == 0)
        //         resp.code = -2; //代表模0
        //     else
        //         resp.result = req.x % req.y;
        //     break;
        // default:
        //     resp.code = -3; //代表请求方法异常
        //     break;
        // }
        if(mp.find(req.op) != mp.end())
        {
            resp.result = mp[req.op](req.x,req.y).second;
            resp.code = mp[req.op](req.x,req.y).first;
        }
        else
        {
            resp.code = -3;
        }
        cout << "request: " << req.x << req.op << req.y << endl;
        // write(sock, &resp, sizeof(resp));
        std::string send_string = SerializeResponse(resp);   //序列化之后的字符串
        write(sock, send_string.c_str(),send_string.size());     
        cout << "服务结束: " << send_string << endl;
        // }
    }

    // 5. 关闭链接
    close(sock);
}

// ./CalServer port
int main(int argc, char *argv[])
{
    if (argc != 2)
        Usage(argv[0]);
    uint16_t port = atoi(argv[1]);

    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        if (sock >= 0)
        {
            cout << "get a new client..." << endl;
            int *pram = new int(sock);
            pthread_t tid;
            pthread_create(&tid, nullptr, HandlerRequest, pram);
        }
    }

    return 0;
}

NetClient.cc

#include "Protocol.hpp"
#include "Sock.hpp"
using namespace std;
#include<unistd.h>

void Usage(string proc)
{
    cout << "Usage: " << proc << " server_ip server_port" << endl;
}
// ./CalClient server_ip server_port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    int sock = Sock::Socket();
    Sock::Connect(sock, argv[1], atoi(argv[2]));

    // while (true)
    // {
    // 业务逻辑
    request_t req;
    memset(&req, 0, sizeof(req));
    cout << "Please Enter Data One# ";
    cin >> req.x;
    cout << "Please Enter Data Two# ";
    cin >> req.y;
    cout << "Please Enter operator# ";
    cin >> req.op;

    std::string json_string = SerializeRequest(req);

    // ssize_t s = write(sock, &req, sizeof(req));
    ssize_t s = write(sock, json_string.c_str(), json_string.size());

    char buffer[1024];
    s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        response_t resp;
        buffer[s] = 0;
        std::string str = buffer;
        DeserializeResponse(str, resp);

        cout << "code[0:success]: " << resp.code << endl;
        cout << "result: " << resp.result << std::endl;
    }
    // response_t resp;
    // s = read(sock, &resp, sizeof(resp));
    // if (s == sizeof(resp))
    // {
    //     cout << "code[0:success]: " << resp.code << endl;
    //     cout << "result: " << resp.result << std::endl;
    // }
    //     else{
    //         break;
    //     }
    // }

    return 0;
}

Protocol.hpp

#include<jsoncpp/json/json.h>
typedef struct request
{
    int x;
    int y;
    char op;
}request_t;

typedef struct response
{
    int code;
    int result;
}response_t;


std::string SerializeRequest(request_t &req)  //序列化请求
{
    Json::Value root;   //可以承载任何对象  
    root["datax"] = req.x;
    root["datay"] = req.y;
    root["op"] = req.op;

    //序列化请求
    Json::FastWriter writer;
    std::string json_string = writer.write(root);
    return json_string;
}
void DeserializeRequest(std::string &json_string,request_t &out)  //反序列化请求
{
    Json::Reader reader;
    Json::Value root;

    reader.parse(json_string,root);
    out.op = root["op"].asInt();
    out.x = root["datax"].asInt();
    out.y = root["datay"].asInt();
}
std::string SerializeResponse(response_t &res)  //序列化响应
{
     Json::Value root;   //可以承载任何对象  
    root["code"] =res.code;
    root["result"] = res.result;

    //序列化响应
    Json::FastWriter writer;
    std::string json_string = writer.write(root);
    return json_string;
}
void DeserializeResponse(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.cc

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

class Sock
{
public:
    static int Socket()
    {
        int listen_sock = socket(AF_INET,SOCK_STREAM,0);
        if(listen_sock < 0)
        {
            std::cerr<<"listen_sock"<<listen_sock<<std::endl;
            exit(1);
        }    
        return listen_sock;
    }
    static void Bind(int sock,uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        if(bind(sock,(struct sockaddr*)(&local),sizeof(local)) < 0)
        {
            std::cerr<<"bind()"<<std::endl;
    
        }
    }
    static void Listen(int sock)
    {
        if(listen(sock,5) < 0)
        {
            std::cerr<<"listen()"<<std::endl;
          
        }
    }
    static int Accept(int sock)
    {
        struct sockaddr_in perr;
        memset(&perr,0,sizeof(perr));
        socklen_t len = sizeof(perr);

        int newsock = accept(sock,(struct sockaddr*)(&perr),&len);
        if(newsock >= 0)
            return newsock;
        return -1;
    }
    static void Connect(int sock,std::string ip,uint16_t port)
    {
        struct sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_addr.s_addr = inet_addr(ip.c_str());
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        if(connect(sock,(struct sockaddr*)(&server),sizeof(server)) == 0)
        {
            std::cerr<<"connect() is sucesss"<<std::endl;
        }
        else
        {
            std::cerr<<"connect() is fail"<<std::endl;
        }
    }
};

makefile

.PHONY:all
all:NetServer NetClient
NetServer:NetServer.cc
	g++ -o $@ $^ -std=c++11 -lpthread -ljsoncpp
NetClient:NetClient.cc
	g++ -o $@ $^ -std=c++11 -lpthread -ljsoncpp
.PHONY:clean
clean:
	rm -rf NetClient NetServer
一端发送时构造的数据 , 在另一端能够正确的进行解析, 就是 ok . 这种约定 , 就是 应用层协议。通过以上实现可以看OSI七层模式,表示层就是数据格式和网络标准数据的转换(序列化和反序列化),会话层 (负责通信连接)。

HTTP协议

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

认识URL

 

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

我们通常表示互联网上的唯一资源,是ip+路径。

ip通常是以域名的方式呈现的,路径是通过路径 + /确定的

urlencodeurldecode

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

 

url转码工具

Http的格式

Http的请求或者响应,基本上都是按行(\n)为单位来进行构建的请求和响应的!无论是请求还是响应都是由3到4部分组成的。

 Http的读取的发送都是按照字符串的方式。空行是一个特殊字符:用空行可以将长字符串一切为二。

接下来我们观察一下Http的报文

#include <iostream>
#include "Sock.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void Usage(std::string str)
{
    std::cout << str << " port " << std::endl;
}
void *HandlerResult(void *args)
{
    int scok = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    char buf[1024];
    ssize_t s = recv(scok, buf, sizeof(buf), 0);
    if(s > 0)
    {
        buf[s] = 0;
        std::cout << buf << std::endl;
    }
    close(scok);
    return nullptr;
}
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, atoi(argv[1]));
    Sock::Listen(listen_sock);
    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        pthread_t tid;
        int *pram = new int(sock);
        pthread_create(&tid, nullptr, HandlerResult, pram);
    }
}

用电脑连接服务器

用手机连接服务器

现在我们客服端网页没有任何响应,接写来我们响应网页内容

http Content-type 对照表

#include <iostream>
#include "Sock.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void Usage(std::string str)
{
    std::cout << str << " port " << std::endl;
}
void *HandlerResult(void *args)
{
    int scok = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    char buf[1024];
    ssize_t s = recv(scok, buf, sizeof(buf), 0);
    if(s > 0)
    {
        buf[s] = 0;
        std::cout << buf;


        std::string http_response = "http/1.0 200 OK\n";
        http_response += "Content-Type: text/plain\n"; //text/plain,正文是普通的文本
        http_response += "\n"; //传说中的空行
        
        http_response += "hello bit, hello 102!";
        send(scok, http_response.c_str(), http_response.size(), 0); //ok??

    }
    close(scok);
    return nullptr;
}
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, atoi(argv[1]));
    Sock::Listen(listen_sock);
    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        pthread_t tid;
        int *pram = new int(sock);
        pthread_create(&tid, nullptr, HandlerResult, pram);
    }
}

 

我们也可以手动构建一个请求

 下面一堆html文本就是百度首页的网页消息。

HTTP协议格式

HTTP 请求

 一个http请求如何保证报头数据全部读完,当一个http读到空行时,如果Content-Length属性有数据,那么请求正文的长度就是Content-Length的值,Content-Length就是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中

HTTP的方法

 其中最常用的就是GET方法和POST方法

测试GET和POST

在当前目录下创建一个文件夹wwwroot(wbe根目录)新建文件index.html,HTML基础教程

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h5>hello world</h5>
        <h5>hello world</h5>
        <h5>hello world</h5>
        <h5>hello world</h5>
        <h5>hello world</h5>
        <h5>hello world</h5>
    </body>
</html>

http.cc

#include <iostream>
#include "Sock.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string>
#include<fstream>
#define html_file "./wwwroot/index.html"

void Usage(std::string str)
{
    std::cout << str << " port " << std::endl;
}
void *HandlerResult(void *args)
{
    int scok = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    char buf[1024];
    ssize_t s = recv(scok, buf, sizeof(buf), 0);
    if(s > 0)
    {
        buf[s] = 0;
        std::cout << buf;


        // std::string http_response = "http/1.0 200 OK\n";
        // http_response += "Content-Type: text/plain\n"; //text/plain,正文是普通的文本
        // http_response += "\n"; //传说中的空行
        
        // http_response += "hello bit, hello 102!";
        // send(scok, http_response.c_str(), http_response.size(), 0); //ok??
        std::string http_response = "http/1.0 200 OK\n";
        http_response += "Content-Type: text/html;charset=utf8\n";
        http_response += "Content-Length: ";

        //获取文件大小
        struct stat st;
        stat(html_file,&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 is error"<<std::endl;
            exit(1);
        }
        else
        {
            std::string line;
            while(getline(in,line))
            {
                http_response += line;
            }
            in.close();
        }
        send(scok, http_response.c_str(), http_response.size(), 0); //ok??
    }
    close(scok);
    return nullptr;
}
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, atoi(argv[1]));
    Sock::Listen(listen_sock);
    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        pthread_t tid;
        int *pram = new int(sock);
        pthread_create(&tid, nullptr, HandlerResult, pram);
    }
}

通过telnet发送请求

 通过网页请求

 GET方法

index.html

<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h5>hello 我是首页!</h5>

        <h5>hello 我是表单!</h5>

        <!-- /a/b/handler_from并不存在,也不处理 -->
        <form action="/a/b/handler_from" method="GET">
            姓名: <input type="text" name="name"><br/>
            密码: <input type="password" name="passwd"><br/>
            <input type="submit" value="登陆">
        </form>
    </body>
</html>

 

 POST方法

将index.html的method改为POST方法

通过Fiddler抓包

 

 可以看出请求的数据在正文当中。

总结:

1.GET是最常用的方法默认一般获取所有网页,都是GET方法,同url拼接从而提交GET参数。POST是提交参数比较常用的方法,一般通过正文提交的

2.GET提交的参数在url中,POST提交的参数在正文中。

3.POST方法更私密(私密不等于安全)。不会回显在url中

4.通过GET传参大小是有限制的(url不可能太长,和具体游览器决定的),而POST传参没有限制。

如果提交的参数比较小且不敏感可以考虑用GET方法。

GET和POST是前后端交互的一个重要方式

HTTP的状态码

其实我觉得状态码没没有任何意思,不过是服务器程序反应给用户层的结果而已,但是还是说一下吧。 

4**(是客户端错误,客户端请求,服务端没有这个资源)

#include <iostream>
#include "Sock.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <fstream>
#define html_file "./wwwroot/index.html.bak" //故意给一个不存在的文件

void Usage(std::string str)
{
    std::cout << str << " port " << std::endl;
}
void *HandlerResult(void *args)
{
    int scok = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    char buf[1024];
    ssize_t s = recv(scok, buf, sizeof(buf), 0);
    if (s > 0)
    {
        buf[s] = 0;
        std::cout << buf;

        // std::string http_response = "http/1.0 200 OK\n";
        // http_response += "Content-Type: text/plain\n"; //text/plain,正文是普通的文本
        // http_response += "\n"; //传说中的空行

        // http_response += "hello bit, hello 102!";
        // send(scok, http_response.c_str(), http_response.size(), 0); //ok??
        //读取正文
        std::ifstream in(html_file);
        if (!in.is_open())
        {
            std::string http_response = "http/1.0 404 NOT FOUND\n";
            http_response += "Content-Type: text/html;charset=utf8\n";
            http_response += "\n";
            http_response += "<html><p>你访问的资源不存在</p></html>";
            //std::cerr << "open is error" << std::endl;
            // exit(1);
            send(scok, http_response.c_str(), http_response.size(), 0); // ok??
        }
        else
        {
           
            //获取文件大小
            struct stat st;
            stat(html_file, &st);
            std::string http_response = "http/1.0 200 OK\n";
            http_response += "Content-Type: text/html;charset=utf8\n";
            http_response += "Content-Length: ";
            http_response += std::to_string(st.st_size);
            http_response += "\n";
            http_response += "\n";
            std::string line;
            while (getline(in, line))
            {
                http_response += line;
            }
            in.close();
            send(scok, http_response.c_str(), http_response.size(), 0); // ok??
        }
       
    }
    close(scok);
    return nullptr;
}
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, atoi(argv[1]));
    Sock::Listen(listen_sock);
    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        pthread_t tid;
        int *pram = new int(sock);
        pthread_create(&tid, nullptr, HandlerResult, pram);
    }
}

 

5**(服务端错误)例如:客服端来了请求服务端创建线程,进程崩溃了,或者文件读取错误,进程异常了...

3**重定性 301永久重定向,302和307是临时重定向需要搭配报头中location属性使用

当我们输入150.158.146.173:8080时会跳转到腾讯官网

#include <iostream>
#include "Sock.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <fstream>
#define html_file "./wwwroot/index.html"

void Usage(std::string str)
{
    std::cout << str << " port " << std::endl;
}
void *HandlerResult(void *args)
{
    int scok = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    char buf[1024];
    ssize_t s = recv(scok, buf, sizeof(buf), 0);
    if (s > 0)
    {
        buf[s] = 0;
        std::cout << buf;

        std::string http_response = "http/1.1 301 Permanently moved!\n";
        http_response += "Location: https://www.qq.com/\n";
        http_response += "\n";
        send(scok, http_response.c_str(), http_response.size(), 0); // ok??
        // std::string http_response = "http/1.0 200 OK\n";
        // http_response += "Content-Type: text/plain\n"; //text/plain,正文是普通的文本
        // http_response += "\n"; //传说中的空行

        // http_response += "hello bit, hello 102!";
        // send(scok, http_response.c_str(), http_response.size(), 0); //ok??
        //读取正文
        // std::ifstream in(html_file);
        // if (!in.is_open())
        // {
        //     std::string http_response = "http/1.0 404 NOT FOUND\n";
        //     http_response += "Content-Type: text/html;charset=utf8\n";
        //     http_response += "\n";
        //     http_response += "<html><p>你访问的资源不存在</p></html>";
        //     //std::cerr << "open is error" << std::endl;
        //     // exit(1);
        //     send(scok, http_response.c_str(), http_response.size(), 0); // ok??
        // }
        // else
        // {
           
        //     //获取文件大小
        //     struct stat st;
        //     stat(html_file, &st);
        //     std::string http_response = "http/1.0 200 OK\n";
        //     http_response += "Content-Type: text/html;charset=utf8\n";
        //     http_response += "Content-Length: ";
        //     http_response += std::to_string(st.st_size);
        //     http_response += "\n";
        //     http_response += "\n";
        //     std::string line;
        //     while (getline(in, line))
        //     {
        //         http_response += line;
        //     }
        //     in.close();
        //     send(scok, http_response.c_str(), http_response.size(), 0); // ok??
       //  }
       
    }
    close(scok);
    return nullptr;
}
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, atoi(argv[1]));
    Sock::Listen(listen_sock);
    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        pthread_t tid;
        int *pram = new int(sock);
        pthread_create(&tid, nullptr, HandlerResult, pram);
    }
}

 

 

HTTP常见Header

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

 User-Agent里的历史故事

Connection:keep-alive是长链接。没写就是短链接。HTTP/1.0是短链接,HTTP1.1是长链接,长链接解决了频繁建立连接的过程。

cookie和session

http本身是无状态的协议。但是有时候我们在访问有些网站时能够认识用户,比如有些网站需要登陆,关闭掉网页再次打开网页就不需要登录。http协议本身不并不解决这个问题,而是提供技术支持来保证网站具有"会话保持“的功能。

cookie会话管理

1.cookie其实是一个文件,该文件里面保存的是我们用户的私密消息。

2.一旦网站有对应有cookie,在发起任何请求的时候,都会在http协议request中携带该cookie消息。 

 当我们在请求某个网页的时候在第一次登录过后会在客户端浏览器中生成对应的cookie文件,cookie文件中保存的是用户的私密信息(用户名,密码),在后续的http请求报文中都会携带cookie内容,来保证 网站认识用户。 

 

 

 cookie可能有文件版或者内存版。文件版一般都在浏览器的安装目录下可以找到,如果别人盗取了我们的cookie文件,就可以以我们的身份认证访问某些特定的资源,如果保存的是我们的用户名和密码,那么就是非常糟糕的。

总结:单纯使用cookie是具有一定的安全隐患的。所以现在大多数都是session和cookie配合使用。

session

核心思路就是:将用户的私密信息,保存在服务器中!

 客户端请求的时候会在服务端上构建一个session文件,响应给客户端的是保存在服务器上的session文件名,下次客户端请求来的时候,会去查看该客户的cookie文件中的会话id是否在服务端找到。找到就不需要再次认证。当前用户的session文件时唯一的。但是cookie文件时还是会有泄露的危险,目前没有办法解决。

总结:cookie+session本质时提高用户的体验。

HTTPS

宏观认识

 加密方式

1.对称加密

发送方数据用密钥(X)加密,接受方也要用密钥(X)解密。

2.非对称加密(RSA)

有一对密钥:公钥和私钥,可以用公钥加密但是只能有私钥解密,或者可以用私钥加密,只能用公钥解密,一般公钥时全世界公开的,私钥只能自己私有保存。

数字签名

 发送方在发送一段文本同时会发送该文本的数据签名,接收方在接受到文本内容的时候,通过相同的hash散列算法,形成数据摘要,在将发送方发送的数据签名进行解密形成数据摘要,对比两份数据摘要,就可以观察数据是否被篡改。如下图:

HTTPS形成过程

1.通信双方都用对称加密的方式

 

2.通信双方采用一对非对称加密

 3.通信双方采用两对非对称加密

 

采用两对非对称加密就可以保证数据安全吗?然而并非如此。依旧有被非法窃取的风险并且非对称算法非常费时间。

4.实际采用的是非对称加对称

但是这种方式任然有攻击的可能,类似于偷梁换柱,server和client交换密钥的时候中间将自己的密钥给clinet,client加密后数据又被截取到,中间人因为给clinet是自己的公钥,那么中间人就可以通过自己的公钥进行解密,让后在把数据发送给server,server和client在中间人眼里就是透明的。现在采用的是server向CA机构申请证书,CA机构会生成证书和数字签名人后在发送给client,中间人就算截取到这个内容也没有办法查看和篡改。只有修改任何一点内容,数字签名就会不被匹配。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值