【网络】HTTP协议(1)

简介: 【网络】HTTP协议

一、HTTP简介

HTTP协议(超文本传输协议HyperText Transfer Protocol),它是基于TCP协议的应用层传输协议,简单来说就是客户端和服务端进行数据传输的一种规则。

1、认识URL

URL(Uniform Resource Lacator)叫做统一资源定位符,也就是我们通常所说的网址,URL是对从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址,互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎处理它。

一个URL大致由如下几部分构成(其中有些部分是可以省略的):

解释 :

  • http://表示请求时需要使用的协议,通常使用的是HTTP协议或安全协议HTTPS,成熟的协议要和端口号一 一匹配,例如使用HTTP协议的服务器要采用80号端口提供服务,使用HTTPS协议的服务器要采用443号端口提供服务
  • usr:pass表示的是登录认证信息,包括登录用户的用户名和密码。虽然登录认证信息可以在URL中体现出来,但绝大多数URL的这个字段都是被省略的,因为登录信息可以通过其他方案交付给服务器。
  • www.example.jp表示的是服务器地址,也叫做域名,通过域名解析系统能够得到域名对应的IP地址。
    例如使用ping命令获得www.baidu.com域名解析后的IP地址为39.156.66.14

    我们可以在浏览器的地址栏使用这个IP直接访问百度的网站。
  • 80表示的是服务器端口号,这个字段一般是被省略的,因为协议和端口号是一一对应的。
  • /dir/index.htm表示的是要访问的资源所在的路径。访问服务器的目的是获取服务器上的某种资源,通过前面的域名和协议已经能够找到对应的服务器进程了,此时要做的就是指明该资源所在的路径。
    例如我们现在要访问百度的更多资源,我们就要指明路径(当然对于我们普通人,我们只需要点击就行,由百度的后端帮我们输入链接进行跳转)

  • uid=1表示的是请求时提供的额外的参数,这些参数是以键值对的形式,通过&符号分隔开的。
    比如我们在百度上面搜索你好世界,此时可以看到URL中有很多参数,而在这众多的参数当中有一个参数wd(word),表示的就是我们搜索时的搜索关键字wd=你好世界。
  • ch1表示的是片段标识符,是对资源的部分补充。

2、URL encode和URL decode

如果在搜索关键字当中出现了像/?:这样的字符,由于这些字符已经被URL当作特殊意义理解了,因此URL在呈现时会对这些特殊字符进行转义。

转义的规则如下:

  • 将需要转码的字符转为十六进制,然后从右到左,取4位(不足4位直接处理),每两位做一位,前面加上%,编码成%XY格式。

比如当我们搜索C++时,由于+加号在URL当中也是特殊符号,经过转义以后变成了%2B,因此一个+就会被编码成一个%2B


说明: URL当中会对这些特殊符号做编码,中文也属于特殊字符

我们浏览器看到的是中文,但是复制其url时,就会发现其被转义了。


  • URL编码(URL encode):是将URL中的特殊字符转换为%符号后跟着两个十六进制数的形式。
  • URL解码(URL decode):是将这种经过URL编码的字符串重新转换回原始的字符串。

3、HTTP协议格式

Ⅰ、请求格式

HTTP请求由以下四部分组成:

  • 请求行:[请求方法] + [url] + [http版本]
  • 请求报头:请求的属性,这些属性都是以key: value的形式按行陈列的。
  • 空行:遇到空行表示请求报头结束,HTTP就是根据空行来进行请求报头与有效载荷进行分离的!
  • 请求正文:请求正文允许为空字符串,如果请求正文存在,则在请求报头中会有一个Content-Length属性来标识请求正文的长度。

前面三部分是一般是由HTTP协议自行设置的,而请求正文一般是用户提交的相关信息或数据,如果用户在请求时没有信息要上传给服务器,此时请求正文就为空字符串。

为了证明上面的结论我们可以通过启动下面的代码将浏览器的发送的请求,打印出来。

// Sock.hpp
// 这是一个对socket相关接口的一个封装,大概了解其接口的作用就行
#pragma once
#include <iostream>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
class Sock
{
public:
    Sock()
        :_sock(-1)
    {}
    void Socket()
    {
        _sock = socket(AF_INET, SOCK_STREAM, 0);
    }
    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        socklen_t len = sizeof(local);
        memset(&local, 0, len);
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(port);
        bind(_sock, (struct sockaddr*)&local, len);
    }
    void Listen(int backlog = 32)
    {
        listen(_sock, backlog);
    }
    int Accept(std::string* client_ip, uint16_t* client_port)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        memset(&client, 0, len);
        int sockfd = accept(_sock, (struct sockaddr*)&client, &len);
        return sockfd;
    }
    int Connect(const std::string& server_ip, uint16_t server_port)
    {
        struct sockaddr_in server;
        socklen_t len = sizeof(server);
        memset(&server, 0, len);
        server.sin_family = AF_INET;
        inet_pton(AF_INET, server_ip.c_str(), &server.sin_addr);
        server.sin_port = htons(server_port);
        return connect(_sock, (struct sockaddr*)&server, len);
    }
    int getFd()
    {
        return _sock;
    }
    ~Sock()
    {
        if (_sock >= 0)
        {
            close(_sock);
        }
    }
private:
    int _sock;          // 对于服务端来说是监听套接字
};
// httpserver.hpp
// 这是一个对服务器相关接口的一个封装,大概了解其接口的作用就行。
// 注意这里使用了原生线程库pthread
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include  <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Sock.hpp"
class HttpServer
{
    using func_t = std::function<std::string(const std::string&)>;
public:
    HttpServer(func_t func, uint16_t server_port)
        :_func(func), _server_port(server_port)
    {}
    void Init()
    {
        _listen_fd.Socket();
        _listen_fd.Bind(_server_port);
        _listen_fd.Listen();
    }
    void Start()
    {
        std::string client_ip;
        uint16_t client_port;
        while (true)
        {
            // 接收连接
            int sockfd = _listen_fd.Accept(&client_ip, &client_port);
            if (sockfd < 0)
            {
                continue;
            }
            // 多线程并发处理任务
            pthread_t tid;
            ThreadData* ptd = new ThreadData(sockfd, this, client_port, client_ip);
            pthread_create(&tid, nullptr, ThreadRoutine, ptd);
        }
    }
private:
    struct ThreadData
    {
        ThreadData(int sockfd, HttpServer* is, uint16_t client_port, std::string client_ip)
            :_sockfd(sockfd), _is(is), _client_port(client_port), _client_ip(client_ip)
        {}
        ~ThreadData()
        {
            if (_sockfd >= 0)
            {
                // logMessage(Info, "通讯完成,文件描述符关闭");
                close(_sockfd);
            }
        }
    public:
        int _sockfd;                // 通信套接字
        HttpServer* _is;             // 传递过来的this指针
        uint16_t _client_port;      // 客户端端口号
        std::string _client_ip;     // 客户端ip
    };
    static void* ThreadRoutine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* ptd = static_cast<ThreadData*>(args);
        ptd->_is->HandleHttpRequest(ptd->_sockfd, ptd->_client_ip, ptd->_client_port);
        delete ptd;
        return nullptr;
    }
    // 这个必须是一个可重入函数
    void HandleHttpRequest(int sockfd, const std::string& client_ip, uint16_t client_port)
    {
        char buf[4096];
        // 1.假设一次能够读取完毕,并且只请求一次
        ssize_t n = recv(sockfd, buf, sizeof(buf) - 1, 0);
        buf[n] = '\0';
        std::string request = buf;
        // 2.业务处理
        std::string response = _func(request);
        // 3.结果返回
        send(sockfd, response.c_str(), response.size(), 0);
    }
private:
    func_t _func;               // 回调函数
    uint16_t _server_port;      // 服务器端口号
    Sock _listen_fd;            // 监听套接字
};
// httpserver.cpp
#include <iostream>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <functional>
#include "httpserver.hpp"
// 使用手册                                                                                                                                            
static void Usage(std::string proc)
{
    std::cout << "usage\n\t" << proc << " 端口" << std::endl;
}
// 处理http请求的函数,这里我们对于请求不作任何处理,只打印请求的内容
std::string Handler(const std::string& request)
{
    // 打印请求的内容
    std::cout << "------------------------------------" << std::endl;
    std::cout << request << std::endl;
    return "";
}
// 必须以 程序名 端口号 的方式启动
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t server_port = atoi(argv[1]);
    std::unique_ptr<HttpServer> up(new HttpServer(Handler, server_port));
    up->Init();
    up->Start();
    return 0;
}

编译运行我们的程序,这里我们的直接在浏览器的地址栏里面,输入我们的公网IP和端口号,格式:IP: 端口号

此时我们发现我们的服务器打印了下面的信息:

说明:

  • 浏览器向我们的服务器发起HTTP请求后,因为我们的服务器程序没有对进行响应,此时浏览器就会认为服务器没有收到,然后再不断发起新的HTTP请求,因此虽然我们只用浏览器访问了一次,但会受到多次HTTP请求。
  • 由于浏览器发起请求时默认用的就是HTTP协议,因此我们在浏览器的url框当中输入网址时可以不用指明HTTP协议。
  • url当中的/表示的是web根目录,这个web根目录可以是你的机器上的任何一个目录,由你自己指定的。
相关文章
|
1天前
|
负载均衡 网络协议 算法
|
3天前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
23 13
|
1天前
|
开发者
HTTP 协议请求方法的发展历程
【10月更文挑战第21天】
|
1天前
|
安全
HTTP 协议的请求方法
【10月更文挑战第21天】
|
1天前
|
缓存 安全 前端开发
HTTP 协议的请求方法在实际应用中有哪些注意事项?
【10月更文挑战第29天】HTTP协议的请求方法在实际应用中需要根据具体的业务场景和需求,合理选择和使用,并注意各种方法的特点和限制,以确保网络通信的安全、高效和数据的一致性。
|
3天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
4天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
4天前
|
网络协议 前端开发 API
HTTP 和 TCP 协议的应用场景有哪些不同
【10月更文挑战第25天】HTTP(超文本传输协议)和 TCP(传输控制协议)处于网络协议栈的不同层次,各自具有独特的功能和特点,因此它们的应用场景也存在明显的差异。
|
4天前
|
安全 前端开发 JavaScript
利用HTTP协议进行文件上传和下载的常见方法
【10月更文挑战第25天】可以利用HTTP协议方便地实现文件的上传和下载功能,满足不同应用场景下的需求。在实际应用中,还可以根据具体的业务需求和安全要求,对文件上传和下载的过程进行进一步的优化和安全处理。
|
4天前
|
网络协议 API 数据格式
HTTP 和 TCP 协议的主要区别
【10月更文挑战第25天】HTTP 和 TCP 在网络通信中扮演着不同的角色,各自具有独特的功能和特点,它们相互配合,共同为实现网络应用的各种需求提供了基础支持。