0
点赞
收藏
分享

微信扫一扫

计算机网络--应用层(http)

狗啃月亮_Rachel 2022-10-18 阅读 105

                                

       今天我们正式来介绍计算机网络协议的第一层--应用层(http协议)。当然,针对https协议博主在下一篇博客再来讲解,毕竟https是针对网络安全工程师来说是比较重要的,博主是后端开发的,所以今天着重介绍http协议。

目录

再谈OSI七层协议中上三层

认识URL

urlencode和urldecode

HTTP协议格式

Http.cc

Sock.hpp

​编辑 HTTP的方法

在Linux终端访问

Http代码验证GET方法 

stat函数

Http.cc

Sock.hpp

./WWWROOT/index.html

Http代码验证POST方法 

Http.cc

Sock.hpp

./WWWROOT/index.html

GET和POST方法总结

第一批结论:概念问题

第二批结论:区别

第三批结论:如何选择

HTTP的状态码

301永久重定向

Location

Http.cc

 302/307临时重定向

 HTTP常见的Header

Connect中长链接&&短连接

cookie && session

单纯使用cookie带来的问题 

session


再谈OSI七层协议中上三层

还记得博主上一篇博客写的CS模式的网络在线版本计算器吗,本质就是一个应用层网络服务。

认识URL

        我们请求的图片,html,css,js,视频,音频,标签,文档等这些都称之为"资源"。在服务器后台,是用Linux做的。

       我们也知道,IP + Port唯一的确定一个进程,但是无法唯一的确认一个资源!公网IP地址是唯一确认一台主机的,而我们所谓的网络"资源",都一定是存在于网络中的一台Linux机器上!Linux或者传统的操作系统,保存资源的方式,都是以文件的方式保存的。单Linux系统,标识一个唯一资源的方式,是通过路径!

接下来,我们来认识一下URL。

        所以URL就是我们平常所说的网址,通过URL,那么就可以IP+Linux路径,来确认网络中唯一的一个资源。

urlencode和urldecode

举个栗子:

同时对于解码,我们可以使用网页在线解码工具:

HTTP协议格式

博主画个图来形象展示一下:

思考1:http如何解包,如何封装,如何分用?

思考2:http请求或者响应,是如何被读取的?http请求是如何被发送的?

思考3:如何理解普通用户的上网行为?是为了简单吗?

我们来写个代码来看一下Http的响应格式:

Http.cc

#include "Sock.hpp"
#include<pthread.h>
#include<unistd.h>

using namespace std;

void* HandlerHttpRequest(void* args)
{
    //Htttp协议,如果自己写的话,本质是我们根据协议内容,来进行文本分析
    int sock = *(int*)args;
    delete (int*)args;
    pthread_detach(pthread_self());

#define SIZE 1024*10
    char buffer[SIZE];
    memset(buffer, 0, sizeof(buffer));

    ssize_t s = recv(sock, buffer, sizeof(buffer)-1, 0);
    if(s > 0)
    {
        buffer[s] = 0;
        cout << buffer;//查看http的请求格式!

        //要构建响应,必须有协议!
        string http_response = "http/1.0 200 OK\n"; //协议版本 + 状态码 + 状态码描述
        http_response += "Content-Type: text/plain\n"; //text/plain 正文是普通的文本
        http_response += "\n"; //传说中的空行
        http_response += "hello world, hello cyq!"; //正文

        send(sock, http_response.c_str(), http_response.size(), 0);
    }
    
    close(sock);
    return nullptr;
}
void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
// ./Http port
int main(int args, char* argv[])
{
    if(args != 2)
    {
        Usage(argv[0]);
        return -1;
    }
    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)
        {
            pthread_t tid;
            int* parm = new int(sock);
            pthread_create(&tid, nullptr, HandlerHttpRequest, (void*)parm);
        }
    }
    return 0;
}

Sock.hpp

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

using namespace std;

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

    static void Bind(int sock, uint16_t port)
    {
        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; //服务端 ip地址

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

    static void Listen(int sock)
    {
        if(listen(sock, 5) < 0)
        {
            cerr << "listen error" << 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, string ip, uint16_t port)
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));

        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(ip.c_str()); //字符串转整型

        if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
        {
            cout << "Connect Success" << endl;
        }
        else
        {
            cout << "Connect failed" << endl;
            exit(5);
        }
    }
};

实验结果:

我们看一下服务端给客户端的响应:

我们来看一下客户端的请求:

 HTTP的方法

在Linux终端访问

如图所示:

Http代码验证GET方法 

stat函数

在这里我们使用这个系统调用来计算一个文本的大小。

Http.cc

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

using namespace std;

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


void* HandlerHttpRequest(void* args)
{
    //Htttp协议,如果自己写的话,本质是我们根据协议内容,来进行文本分析
    int sock = *(int*)args;
    delete (int*)args;
    pthread_detach(pthread_self());

#define SIZE 1024*10
    char buffer[SIZE];
    memset(buffer, 0, sizeof(buffer));

    ssize_t s = recv(sock, buffer, sizeof(buffer)-1, 0);
    if(s > 0)
    {
        buffer[s] = 0; //打印客户端请求
        cout << buffer;

        string html_file = WWWROOT;
        html_file += HOME_PAGE;

        struct stat st;
        stat(html_file.c_str(), &st); //计算index.html文件大小

        //构建响应
        //返回的时候,不仅仅是返回正文网页信息,而且还要包括http的请求
        string http_response = "http/1.0 200 OK\n";
        //正文部分的数据类型
        http_response += "Content-Type: text/html; charset=utf8\n";
        http_response += "Content-Length: ";
        http_response += to_string(st.st_size); //取出结构体中的这个变量
        http_response += "\n";

        http_response += "\n"; //空行

        //接下来才是正文
        ifstream in(html_file);
        if(!in.is_open())
        {
            cerr << "open html error!" << endl;
        }
        else
        {
            string content;
            string line;
            while(getline(in, line)) //可以读\n
            {
                content += line;
            }
            http_response += content;
            in.close();
            send(sock, http_response.c_str(), http_response.size(), 0);
        }
    }

    close(sock);
    return nullptr;
}
void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
// ./Http port
int main(int args, char* argv[])
{
    if(args != 2)
    {
        Usage(argv[0]);
        return -1;
    }
    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)
        {
            pthread_t tid;
            int* parm = new int(sock);
            pthread_create(&tid, nullptr, HandlerHttpRequest, (void*)parm);
        }
    }
    return 0;
}

Sock.hpp

./WWWROOT/index.html

<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <!-- <h3>hello world, hello wmm!</h3> -->
        <h2>hello 我是首页!</h2>

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

        <!-- /a/b/handler_from该路径并不存在,也不处理-->
        <form action="/a/b/handler_from" method="GET">
            姓名: <input type="text" name="name"><br/>
            密码: <input type="password" name="passwd"><br/>
            <!-- value表示按钮上写的什么 -->
            <input type="submit" value="登陆">
        </form>
    </body>
</html>

运行结果&&现象:

Http代码验证POST方法 

Http.cc

Sock.hpp

./WWWROOT/index.html

<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <!-- <h3>hello world, hello wmm!</h3> -->
        <h2>hello 我是首页!</h2>

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

        <!-- /a/b/handler_from该路径并不存在,也不处理-->
        <form action="/a/b/handler_from" method="POST">
            姓名: <input type="text" name="name"><br/>
            密码: <input type="password" name="passwd"><br/>
            <!-- value表示按钮上写的什么 -->
            <input type="submit" value="登陆">
        </form>
    </body>
</html>

运行结果&&现象:

GET和POST方法总结

第一批结论:概念问题

第二批结论:区别

第三批结论:如何选择

总之,GET方法既可以获取参数,也可以提交参数。POST方法用来提交参数。

HTTP的状态码

针对上面的状态码,我们着重讲解3XX形式的状态码。

301永久重定向

Location

我们写一段代码来演示一下:

Http.cc

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

using namespace std;

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


void* HandlerHttpRequest(void* args)
{
    //Htttp协议,如果自己写的话,本质是我们根据协议内容,来进行文本分析
    int sock = *(int*)args;
    delete (int*)args;
    pthread_detach(pthread_self());

#define SIZE 1024*10
    char buffer[SIZE];
    memset(buffer, 0, sizeof(buffer));

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

        string response = "http/1.1 301 Permanently moved\n"; //状态行
        response += "Location: https://www.qq.com/\n"; //server告诉浏览器去新的地址
        response += "\n";
        send(sock, response.c_str(), response.size(), 0);
    }

    close(sock);
    return nullptr;
}
void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
// ./Http port
int main(int args, char* argv[])
{
    if(args != 2)
    {
        Usage(argv[0]);
        return -1;
    }
    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)
        {
            pthread_t tid;
            int* parm = new int(sock);
            pthread_create(&tid, nullptr, HandlerHttpRequest, (void*)parm);
        }
    }
    return 0;
}

其他部分代码是不改变的,原来打开的网页内容时表单内容,现在我们给它重定向到qq官网中去。

演示结果:

 302/307临时重定向

 对于Http.cc模块,我们只修改部分代码:

 HTTP常见的Header

Connect中长链接&&短连接

短链接的过程:

长链接的过程:

cookie && session

单纯使用cookie带来的问题 

        所以又引入了session技术,注意,有session并不代表我们不用cookie,实际中,这两个技术是相辅相成的。

session

         但是,我们还有cookie文件被泄露的风险啊!!是的!!但是这是无法避免的,就好比这个世界上没有绝对的黑与白一样。网络安全工程师和黑客之间就是在不断地博弈过程中,相互进步。虽然被泄露的风险还存在,但是也有衍生出一些防御方案。

        例如:现在的许多网站或者qq、微信登录会自动检测当前IP,如果是异地登录,那么服务端就会清除session中对应的ID文件,并让用户再次认证。随着移动设备--手机的出现,出现了短信认证、新设备登录认证等等。所以,防止cookie文件不被盗取,就尽量不要点击不明链接,因为我们既然自己可以找到自己的cookie文件,那么别人通过恶意脚本程序也是可以找到的,这也是不安全的~

看到这里,给博主点个赞吧~

 

举报

相关推荐

0 条评论