网络通信:http协议

news2024/10/5 18:23:32

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

认识URL

统一资源定位符(Uniform Resource Locator,缩写:URL),是对资源的引用和访问该资源的方法。俗称网址。

  • 主机名:表示IP地址的注册名称(域名)或IP地址,用于识别连接到网络设备的数字标识符
  • 端口:跟在域名后面,不是一个URL必须的部分,如果省略端口会使用默认端口。
80是http协议的默认端口,是在输入网站的时候其实浏览器(非IE)已经帮你输入协议了,所以你输入 http://baidu.com,其实是访问 http://baidu.com:80。而8080,一般用与webcahe,完全不一样的两个,比如linux服务器里 apache默认跑80端口,而apache-tomcat默认跑 8080端口,其实端口没有实际意义只是一个接口,主要是看服务的 监听端口。
  • 路径:表示服务器上资源的路径,过去这样的路径标记的是服务器上文件的物理路径,但是现在,路径表示的只是一个抽象地址,并不指代任何物理地址.
  • 参数:请求里提供的额外参数.这些参数是以键值对的形式,通过&符号分隔开来,服务器可以通过这些参数进行相应的个性化处理
  • :用于为页面上的标题提供快速链接,如锚点链接

 urlencode和urldecode

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

转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY 格式

leetcode中文C++   被转义成了   leetcode%E4%B8%AD%E6%96%87C%2B%2B

HTTP协议

HTTP的方法 

 

创建html文件,输入!并按tab键,可以得到一个简单的模板。

传输正文时中英文会乱码:

在html文件的头标签中加入<meta charset="UTF-8">

添加一个报头  Content-type: text/html

要获取文件信息可以使用stat接口:

 

//以文件尺寸为例
struct stat st;
int n = stat(path.c_str(), &st);
if(n == 0) size = st.st_size;
else size = -1;  

 网页教程详情见:w3cschool官网 - 编程狮,随时随地学编程

在Linux中可以使用

wget  [域名]  

获取网页数据。

一个简单的网站服务器

Util.hpp

#pragma once

#include <iostream>
#include <string>
#include<fstream>
using namespace std;

class Util
{
public:
    // XXXX XXX XXX\r\nYYYYY
    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;
    }

    static bool ReadFile(const string& name, string& body)
    {
        auto pos = name.find(".");
        string typeofFile = name.substr(pos);
        ifstream fs(name, fstream::in | fstream::binary);
        char c;
        while(fs.get(c))
        {
            body += c;
        }

        fs.close();

        return true;

    }
};

Protocal.hpp

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include "Util.hpp"

const std::string sep = "\r\n";
const std::string default_root = "./wwwroot";
const std::string home_page = "index.html";

class HttpRequest
{
public:
    HttpRequest(){}
    ~HttpRequest(){}
    void parse()
    {
        // 1. 从inbuffer中拿到第一行,分隔符\r\n
        std::string line = Util::getOneLine(inbuffer, sep);
        if(line.empty()) return;
        // 2. 从请求行中提取三个字段
        // std::cout << "line: " << line << std::endl;
        std::stringstream ss(line);
        ss >> method >> url >> httpversion;

        // 3. 添加web默认路径
        path = default_root; // ./wwwroot, 
        path += url; //./wwwroot/a/b/c.html, ./wwwroot/
        if(path[path.size()-1] == '/') path += home_page;

        // 4. 获取path对应的资源后缀
        // ./wwwroot/index.html
        // ./wwwroot/test/a.html
        // ./wwwroot/image/1.jpg
        auto pos = path.rfind(".");
        if (pos == std::string::npos)
            suffix = ".html";
        else
            suffix = path.substr(pos);
    }
public:
    std::string inbuffer;
    // std::string reqline;
    // std::vector<std::string> reqheader;
    // std::string body;

    std::string method;
    std::string url;
    std::string httpversion;
    std::string path;
    std::string suffix;
};


class HttpResponse
{
public:
    std::string outbuffer;
};

httpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "Protocol.hpp"

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 = std::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) // 第二个参数backlog后面在填这个坑
            {
                exit(LISTEN_ERR);
            }
        }
        void HandlerHttp(int sock)
        {
            // 1. 读到完整的http请求
            // 2. 反序列化
            // 3. httprequst, httpresponse, _func(req, resp)
            // 4. resp序列化
            // 5. send
            char buffer[4096];
            HttpRequest req;
            HttpResponse resp;
            size_t n = recv(sock, buffer, sizeof(buffer)-1, 0); // 大概率我们直接就能读取到完整的http请求
            if(n > 0)
            {
                buffer[n] = 0;
                req.inbuffer = buffer;
                req.parse();
                _func(req, resp); // req -> resp
                send(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);
            }
        }
        void start()
        {
            for (;;)
            {
                // 4. server 获取新链接
                // sock, 和client进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    continue;
                }

                // version 2 多进程版(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;
    };

} // namespace server

httpServer.cc

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

using namespace std;
using namespace server;
       
void Usage(std::string proc)
{
    cerr << "Usage:\n\t" << proc << " port\r\n\r\n";
}
// 1. 服务器和网页分离,html
// 2. url -> / : web根目录
bool Get(const HttpRequest &req, HttpResponse &resp)
{
    // for test
    cout << "----------------------http start---------------------------" << endl;
    std::cout << "method: " << req.method << std::endl;
    std::cout << "url: " << req.url << std::endl;
    std::cout << "httpversion: " << req.httpversion << std::endl;
    std::cout << "path: " << req.path << std::endl;

    cout << req.inbuffer << std::endl;
    cout << "----------------------http end---------------------------" << endl;

    std::string respline = "HTTP/1.1 200 OK\r\n";
    std::string respheader = "Content-Type: ";
    if (req.suffix == ".html")
        respheader += "text/html";
    else if (req.suffix == ".jpg")
        respheader += "application/x-jpg;image/jpeg";
    respheader += "\r\n";

    std::string respblank = "\r\n";

    std::string body;
    if(!Util::ReadFile(req.path, body))
    {
        Util::ReadFile("./wwwroot/404.html", body);
    }
    resp.outbuffer += respline;
    resp.outbuffer += respheader;
    resp.outbuffer += respblank;
    resp.outbuffer += body;

    return true;
}

// ./httpServer 8080
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<HttpServer> httpsvr(new HttpServer(Get, port));
    httpsvr->initServer();
    httpsvr->start();

    return 0;
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>main tittle</title>
</head>
<body>
    <h1>每天都刷leetcode</h1>
    <img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F83b66036-a374-424f-ab45-4a02aaf45a4f%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1688195268&t=19cab953a51d9bd85e9b057dc82d7345" alt="测试图片">
    <img src="/image/1.jpg" alt="本地图片">
    <img src="/image/2.jpg" alt="石榴花">
    <a href="/test/a.html">新闻</a>
    <a href="/test/c.html">电商</a>
</body>
</html>

3开头的状态——重定向

重定向流程图:

实现一个简单的临时重定向:

1.将服务器返回的状态行改为

std::string respline = "HTTP/1.1 307 Temporary Redirect\r\n";

 2.在响应报头中加入重定向的域名

respheader += "Location: https://www.qq.com/\r\n";

浏览器在访问我们的服务器时会自动跳转到QQ。

表单

<form action="/a/b/c.py" method="POST">

姓名:<br> <input type="text" name="xname"> <br>

密码:<br> <input type="password" name="ypwd"> <br>

<br> <input type="submit" value="登陆">

</form>

 使用GET方法和POST方法的区别:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/598774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot + vue3实现视频播放Demo(video.js Vue3-video-play视频播放器)

文章目录 学习链接前言ffmpeg安装ffmpeg配置环境变量分割视频文件 后台配置WebConfig 前端代码video.js示例安装依赖视频播放组件效果 Vue3-video-play示例安装依赖main.js中使用视频播放组件效果 学习链接 ffmpeg官网 长时长视频java存储及vue播放解决方法 【 攻城略地 】vue…

AKStream部署1:ZLMediaKit流媒体服务器(win)

环境准备&#xff1a; windows10/11 visual stadio 2022(.net 6) cmake 3.22.0 git bash 没讲究直接下最新的 ffmpeg &#xff1a; ffmpeg-5.1.2-full_build VLC播放器&#xff1a;VLC media player ZLMediaKit&#xff1a;gitee地址 1、安装上述软件 例ffmpeg下载解压至某自定…

图片转pdf怎么转?一分钟解决

在日常生活中&#xff0c;我们常常需要将一些图片转换成PDF格式&#xff0c;以便于在电子设备上查看和分享。今天&#xff0c;我们将分享一些关于如何将图片转换成PDF的方法。 以下是三种常用的将图片转换成PDF的方法&#xff1a; 我们可以借助转换工具将图片转成PDF 如PDF转…

用esp8266连网的一些互联网知识

参考太极创客的课程资料添加链接描述 TCP/IP协议簇 在网络系统中&#xff0c;为了保证通信设备之间能正确地进行通信&#xff0c;必须使用一种双方都能够理解的语言&#xff0c;这种语言被称为“协议”。 TCP/IP协议簇是Internet的基础&#xff0c;也是当今最流行的组网形式…

数据结构学习记录——树习题-Complete Binary Search Tree(题目描述、输入输出示例、数据结构的选择、核心算法、计算左子树的规模)

目录 题目描述 输入示例 输出示例 数据结构的选择 核心算法 计算左子树的规模 思路 总结 题目描述 现给定一系列不同的非负整数键&#xff0c;如果要求构造出一颗完全二叉树&#xff0c;则可以构造唯一的二叉搜索树。输出此二叉搜索树的层序遍历序列。 完全二叉树 有n…

复习之linux系统中的权限管理

1.权限的查看及读取 &#xff08;1&#xff09;权限的查看 # ls -l file : 查看文件的权限 # ls -ld dir :查看目录权限 &#xff08;2&#xff09;权限的读取 文件的属性叫做文件的元数据。 元数据&#xff08;Metadata&#xff09;&#xff0c;又称中介数据、中继…

chatgpt赋能python:Python为什么运行不出来?

Python为什么运行不出来&#xff1f; Python是一门高级编程语言&#xff0c;被广泛应用于科学计算、机器学习、Web开发等领域。但是&#xff0c;有时候我们在编写Python程序的过程中会遇到各种各样的问题&#xff0c;其中之一就是程序无法运行。那么&#xff0c;Python为什么会…

网页投票系统怎么做如何制作网页投票链接如何投票链接

用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。 而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务&#xff0c;很多用户都很喜欢“活动星投票”这款软件。 “活动星投票”小程序在…

mysql版本升级导致的时区问题

背景 公司各个项目组用的mysql 版本不一样&#xff0c;有的是5.x稳定守旧版&#xff0c;有的最近喜欢用8.x高级迎新版&#xff0c;结果出了问题。 现象&#xff0c;数据库中保存的时间与java读取的时间字段不一样。 最开始数据库mysql版本为5.7.28&#xff0c;驱动为5.1 反正…

【C++】queue和priority_queue的用法及模拟实现

目录 一、queue的简介及其使用1、queue的简介2、queue的使用2、queue的模拟实现 二、priority_queue的简介及其使用1、priority_queue的简介2、priority_queue的使用3、priority_queue的模拟实现 一、queue的简介及其使用 1、queue的简介 queue是一种容器适配器&#xff0c;专…

SpringBoot 学习笔记之 “异常处理”

&#x1f34f;&#x1f350;&#x1f34a;&#x1f351;&#x1f352;&#x1f353;&#x1fad0;&#x1f951;&#x1f34b;&#x1f349;&#x1f95d; 异常处理 文章目录 &#x1f34f; 错误处理1、默认规则2、定制错误处理逻辑3、异常处理自动配置原理4、异常处…

用Python发送通知到企业微信,实现消息推送

Hi&#xff0c;大家好&#xff0c;今天就介绍如何实现自动推送消息到企业微信&#xff0c;适合告警类型通知&#xff0c;非常方便。 在华为工作了10年的大佬出的Web自动化测试教程&#xff0c;华为现用技术教程&#xff01;_哔哩哔哩_bilibili在华为工作了10年的大佬出的Web自…

C++服务器框架开发6——日志系统LogFormatter/size_t学习

该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见&#xff1a;[C高级教程]从零开始开发服务器框架(sylar) 上一篇&#xff1a;C服务器框架开发5——日志系统LogAppender/IO类“3种stream”/双感叹号 C服务器框架开发6——日志系统logFormatter/size_t学…

【Python】深度理解Class类、Object类、Type元类;

详解Class类、Object类、Type元类 1.Class类、Object类、Type元类的表面关系2.Class、Object、Type解释1.1关系详解 2.Object类的默认方法作用详解2.2.1 init2.2.2 new2.2.3 repr2.2.4 提要&#xff1a;作为普通的Python开发者来讲&#xff0c;深入理解object、type不是必要的&…

ESP32设备驱动-MSA301加速计传感器驱动

MSA301加速计传感器驱动 文章目录 MSA301加速计传感器驱动1、MSA301介绍2、硬件准备3、软件准备4、驱动实现1、MSA301介绍 MSA301 是一款具有 I2C 数字输出的三轴低 g 加速度计,适用于消费类应用。 它具有 2g/4g/8g/16g 的动态用户可选满量程范围,并允许在 1Hz 至 500Hz 的…

Lombok 的正确使用姿势

文章目录 1.Lombok 是什么2.安装 Lombok3.Spring Boot 集成 Lombok4.使用 Lombok4.1 注解一览表4.2 部分使用介绍Getter(lazytrue)ValueBuilderSuperBuilderSneakyThrowsSlf4jCleanupNonNullWithSynchronizedval 在 Java 开发领域中&#xff0c;Lombok 插件已经成为一个非常流行…

OpenGL蓝宝书第八章学习笔记:基元处理之几何着色器

前言 本篇在讲什么 OpenGL蓝宝书第八章学习笔记之几何着色器 本篇适合什么 适合初学OpenGL的小白 本篇需要什么 对C语法有简单认知 对OpenGL有简单认知 最好是有OpenGL超级宝典蓝宝书 依赖Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重实践&#xff0c…

使用scikit-learn和pandas学习线性回归

对于想深入了解线性回归的童鞋&#xff0c;这里给出一个完整的例子&#xff0c;详细学完这个例子&#xff0c;对用scikit-learn来运行线性回归&#xff0c;评估模型不会有什么问题了。 1. 获取数据&#xff0c;定义问题 没有数据&#xff0c;当然没法研究机器学习啦。这里我们用…

buuctf re入门题目解析

目录 1.easyre 2.reverse1 3.reverse2 4.内涵的软件 1.easyre 将exe文件放入ida&#xff0c;在主函数main中找到flag&#xff0c;此题结束 2.reverse1 打开主函数main&#xff0c;发现有一个跳转函数&#xff0c;双击打开 这句命令是将str1和str2的内容比较&#xff0c;当…

「C/C++」C/C++空指针void*

✨博客主页&#xff1a;何曾参静谧的博客 &#x1f4cc;文章专栏&#xff1a;「C/C」C/C程序设计 相关术语 void指针&#xff1a;是一种通用指针类型&#xff0c;可以指向任何类型的数据或对象。它不关心指向的数据或对象的类型&#xff0c;只关心指针本身的地址。因此&#xf…