【Linux】HTTP协议1

news2024/11/15 15:53:27

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析

在这里插入图片描述


目录

  • 👉🏻http概念初识
    • http协议格式
  • 👉🏻URL
  • 👉🏻简单实现http协议(版本一)
    • HttpProtocol.hpp
  • 👉🏻http协议(版本二,增设解析请求行)
    • HttpProtocol.hpp
    • Main.cc
    • 补充知识
      • rz命令
      • ifstream的seekg和tellg

👉🏻http概念初识

HTTP(Hypertext Transfer Protocol)是一种用于传输超文本数据(如HTML、CSS、JavaScript等)的应用层协议。它是万维网的基础,用于在客户端和服务器之间传输信息。以下是关于HTTP协议的一些重要信息:

特点:

  1. 无连接性(Connectionless)每个请求/响应对都是独立的,服务器在处理完一个请求后断开连接,等待下一个请求的到来。

  2. 无状态性(Stateless):每个请求之间没有关联,服务器不会保留之前请求的状态信息,每个请求都是独立处理的。

  3. 基于文本(Text-based):HTTP的请求和响应都是以纯文本的形式进行传输,易于阅读和调试。

  4. 灵活性(Flexibility):HTTP支持多种数据类型和方法,可以传输各种类型的数据,并支持多种应用场景。

工作原理:

  1. 客户端-服务器模型:HTTP是一种客户端-服务器模型的协议,客户端发送请求,服务器返回响应。

  2. 请求-响应模式:客户端向服务器发送请求,请求包括方法(GET、POST等)、URL、HTTP版本、请求头部等信息;服务器接收到请求后,返回相应的响应,包括状态码、响应头部、响应体等信息。

http底层的发送和传输协议就是依靠TCP协议实现的,http说白了就是在TCP协议的基础上增设和规范了数据的结构性。

请求方法:

  1. GET:请求指定的资源。
  2. POST:向指定资源提交数据进行处理请求。
  3. PUT:上传指定的URI,作为其表示。
  4. DELETE:请求服务器删除指定的资源。
  5. PATCH:对资源进行部分修改。
  6. HEAD:类似于GET请求,只不过服务器在响应中只返回首部,不返回实体的主体部分。

状态码:

  1. 1xx:信息性状态码。
  2. 2xx:成功状态码。
  3. 3xx:重定向状态码。
  4. 4xx:客户端错误状态码。
  5. 5xx:服务器错误状态码。

协议版本:

  1. HTTP/1.0:最初的版本,每次请求/响应建立一个新的连接。
  2. HTTP/1.1:持久连接,复用连接,支持管道化请求。
  3. HTTP/2.0:二进制协议,多路复用,头部压缩,服务器推送等特性。

安全性

  1. HTTPS:HTTP的安全版本,使用SSL/TLS协议进行加密通信,保护数据的安全性和完整性。

HTTP协议在互联网中扮演着重要的角色,它的简单性和灵活性使得它成为了Web开发中不可或缺的一部分。

http协议格式

http协议主要分两个部分:请求和响应,

📃请求

  • 首行: [方法] + [url] + [版本]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Heade中会有一个 Content-Length属性来标识Body的长度
    在这里插入图片描述

📃响应

  • 首行: [版本号] + [状态码] + [状态码解释]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.
    在这里插入图片描述

👉🏻URL

URL(Uniform Resource Locator)是统一资源定位符的缩写,是用于标识互联网上资源位置的一种标识符。它由多个部分组成,用于定位资源的地址以及指定访问该资源的方式。

💦 URL的结构:

一个标准的URL通常由以下几个部分组成:

  1. 协议(Protocol):指定访问资源所使用的协议或者规则,如 HTTP、HTTPS、FTP 等。

http://表示的是协议名称,表示请求时需要使用的协议,通常使用的是HTTP协议或安全协议HTTPS。HTTPS是以安全为目标的HTTP通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性

  1. 主机名(Host):指定资源所在的主机或者服务器的域名或者IP地址。

  2. 端口号(Port):指定访问资源所使用的端口号。通常,如果使用默认端口号,则可以省略。

  3. 路径(Path):指定服务器上资源的路径。它是资源在服务器上的具体位置。

  4. 查询字符串(Query String):用于传递参数给服务器。它以 ? 开始,多个参数之间用 & 分隔。

  5. 片段标识符(Fragment Identifier):标识资源中的特定部分。它以 # 开始,用于定位文档中的特定位置。
    在这里插入图片描述

💦示例:

考虑以下示例URL:

https://www.example.com:8080/path/to/resource?param1=value1&param2=value2#section3
  • 协议(Protocol):HTTPS

  • 主机名(Host):www.example.com(服务器地址)

  • 端口号(Port):8080

  • 路径(Path):/path/to/resource(在服务器下的资源路径)

  • 查询字符串(Query String):param1=value1&param2=value2

  • 片段标识符(Fragment Identifier):section3


域名 www.example.com 可以分为以下三个部分:

  1. 子域名(Subdomain):在这个示例中,“www” 是子域名。它位于主域名之前,用于表示特定的服务、部门或者功能。通常,“www” 是用于指示该域名用于网站服务的子域名,但也可以使用其他的子域名,比如 “blog”、“shop” 等。

  2. 主域名(Domain):在示例中,“example.com” 是主域名。它是一个在互联网上唯一的名称,用于表示一个网站或者服务的整体身份。主域名通常由注册商注册,并且需要进行域名解析以映射到相应的IP地址。

  3. 顶级域名(Top-Level Domain,TLD):在示例中,“.com” 是顶级域名。顶级域名是域名体系中的最高层次,用于表示域名的类型或者所属的组织、地理位置等信息。常见的顶级域名包括 “.com”、“.net”、“.org” 等通用顶级域名,以及各个国家/地区的国家顶级域名(如 “.cn”、“.uk” 等)。

综上所述,www.example.com 这个域名可以分为 “www”(子域名)、“example”(主域名)和 “.com”(顶级域名)三个部分。

💦用途:

URL是互联网上资源的唯一标识符,用于在Web浏览器中定位和访问网页、图像、文件、视频等资源。通过URL,用户可以直接访问并获取到网络上的各种资源。

总之,URL是互联网上资源的地址标识符,它由多个部分组成,用于指定资源的位置和访问方式。

👉🏻简单实现http协议(版本一)

HttpProtocol.hpp

#pragma once

#include<iostream>
#include<string>
#include<vector>

using namespace std;
const string HttpSep = "\r\n";

class HttpRequest 
{
public:
    HttpRequest():
    _req_blank(HttpSep)
    {

    }
    
    bool Getline(string& str,string* line)//获取请求行的内容
    {
        //1.找到内容停止符的位置
        auto pos = str.find(HttpSep);
        if(pos==string::npos) return false;

        //2.截取正文
        *line = str.substr(0,pos);
        //3.读取完后,将str中已提取的内容清空
        str.erase(0,pos+HttpSep.size());
        return true;
        
    }
	//反序列化
    void Deserialize(string& request)
    {
        //1.先读请求行
        string line;
        bool ok = Getline(request,&line);
        if(!ok) return;//如果读不到\r\n则退出
        _req_line = line;

        //2.再读报文每一行的内容
        while(true)
        {
            bool ok = Getline(request,&line);//再接着往下读取
            if(ok&&line.empty())
            {
                //如果读到了,但是内容为空
                //则剩下的内容为
                _req_content = request;

            }
            else if(ok&&!line.empty())
            {
                _req_header.push_back(line);
            }
            else
                break;
        }
    }
     void DebugHttp()
    {
        std::cout << "_req_line" << _req_line << std::endl;
        for(auto &line : _req_header)
        {
            std::cout << "---> " << line << std::endl;
        }
        std::cout << "_req_blank: " << _req_blank << std::endl;
        std::cout << "_req_content: " << _req_content << std::endl;
    }
private:
    string _req_line;//请求行
    vector<string> _req_header;//用来存储请求数据中所有的请求行数据
    string _req_content;//报文内容
    string _req_blank;//空行

};

最后去网页访问我们的服务器,就可以得到这个
在这里插入图片描述

👉🏻http协议(版本二,增设解析请求行)

此次版本,我们主要给Http协议中增设解析请求行的功能,
将请求行解析为三部分:

  • method:请求方法
  • URL:资源地址
  • http_version:http的版本

而后我们还要再对URL地址进行解析,可以得到

  • path:资源的路径
  • suffix:资源的后缀名

文件目录如下:
在这里插入图片描述

HttpProtocol.hpp

#pragma once

#include <iostream>
#include <string>
#include <vector>

#include <sstream>

using namespace std;
const string HttpSep = "\r\n";

const string homepage = "index.html";
const string wwwroot = "./wwwroot"; // 根目录

class HttpRequest
{
public:
    HttpRequest() : _req_blank(HttpSep), _path(wwwroot)
    {
    }
    ~HttpRequest()
    {
    }
    bool Getline(string &str, string *line) // 获取请求行的内容
    {
        // 1.找到内容停止符的位置
        auto pos = str.find(HttpSep);
        if (pos == string::npos)
            return false;

        // 2.截取正文
        *line = str.substr(0, pos);
        // 3.读取完后,将str中已提取的内容清空
        str.erase(0, pos + HttpSep.size());
        return true;
    }

    void Deserialize(string &request)
    {
        // 1.先读请求行
        string line;
        bool ok = Getline(request, &line);
        if (!ok)
            return; // 如果读不到\r\n则退出
        _req_line = line;

        // 2.再读报文每一行的内容
        while (true)
        {
            bool ok = Getline(request, &line); // 再接着往下读取
            if (ok && line.empty())
            {
                // 如果读到了,但是内容为空
                // 则剩下的内容为
                _req_content = request;
            }
            else if (ok && !line.empty())
            {
                _req_header.push_back(line);
            }
            else
                break;
        }
    }
    void ParseReqLine()
    {
        stringstream ss(_req_line);

        ss >> _method >> _url >> _http_version;

        if (_url == "/")
        {
            // 如果是在当前下目录,则直接令它的路径资源终点为index.html
            _path += _url;
            _path += homepage;
        }
        else
        {
            _path += _url;
        }
    }
    void ParseSuffix()
    {
        auto pos = _path.rfind(".");
        if (pos == string::npos)
            _suffix = ".html";
        else
            _suffix = _path.substr(pos);
    }
    void Parse()
    {
        // 1.解析请求行
        ParseReqLine();

        // 2.解析资源后缀
        ParseSuffix();
    }
    void DebugHttp()
    {
        std::cout << "_req_line" << _req_line << std::endl;
        for (auto &line : _req_header)
        {
            std::cout << "---> " << line << std::endl;
        }
        std::cout << "_req_blank: " << _req_blank << std::endl;
        std::cout << "_req_content: " << _req_content << std::endl;

        std::cout << "Method: " << _method << std::endl;
        std::cout << "url: " << _url << std::endl;
        std::cout << "http_version: " << _http_version << std::endl;
    }

    // 接下来再整几个返回解析后内容的函数接口
    string Url()
    {
        return _url;
    }
    string Path()
    {
        return _path;
    }
    string Suffix()
    {
        return _suffix;
    }

private:
    string _req_line;           // 请求行
    vector<string> _req_header; // 用来存储请求数据中所有的请求行数据
    string _req_content;        // 报文内容
    string _req_blank;          // 空行

    // 解析之后的内容
    string _method;       // 请求方法
    string _url;          // 资源路径
    string _http_version; // http版本
    string _path;         // 资源的具体文件路径位置,path包含在url中,所以需要从url中解析
    string _suffix;       // 请求资源的后缀
};

Main.cc

#include <iostream>
#include <memory>
#include <string>
#include <unistd.h>
#include <fstream>
#include "TcpServer.hpp"
#include "HttpProtocol.hpp"
string SuffixToType(const std::string &suffix) // 返回后缀名对应的http-type
{
    if (suffix == ".html" || suffix == ".html")
        return "text/html";
    else if (suffix == ".png")
        return "image/png";
    else if (suffix == ".jpg")
        return "image/jpeg";
    else
    {
        return "text/html";
    }
}
string GetFileContent(const string &path)
{
    // 为了中间不出现字符解码出现问题
    // 我们读取文件内容,而是以二进制形式读取
    ifstream in(path, std::ios::binary); // 二进制读写
    if (!in.is_open())
        return "";

    in.seekg(0, in.end);       // 位置移到文件末尾
    int filesize = in.tellg(); // 读取文件当前位置之前的大小
    in.seekg(0, in.beg);       // 读完大小后回到开头

    string content; // 存储读到的二进制内容
    content.resize(filesize);
    in.read((char *)content.c_str(), filesize);

    in.close();

    return content;
}
// request: 我们认为我们已经读到了一个完整的请求了
std::string HandlerHttpRequest(std::string &request)
{
    HttpRequest req;
    // 1.反序列化+解析http信息+打印http信息
    req.Deserialize(request);

    req.Parse();

    req.DebugHttp();

    // 2.获取请求URL中的文件资源,即文件的内容
    string content = GetFileContent(req.Path());
    // 3.打印看看解析后,资源文件的后缀和对应的http格式类型
    std::cout << "suffix: " << req.Suffix() << " Type: " << SuffixToType(req.Suffix()) << std::endl;

    if (!content.empty())
    {
        //返回http状态+请求到的资源信息+Content-Type
        std::string httpstatusline = "Http/1.0 200 OK\r\n";
        std::string httpheader = "Content-Length: " + std::to_string(content.size()) + "\r\n";
        httpheader += "Content-Type: " + SuffixToType(req.Suffix()) + "\r\n"; // 正文的类型
        httpheader += "\r\n";

        std::string httpresponse = httpstatusline + httpheader + content;
        
        return httpresponse;
    }

    return "";
}

// ./server port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "Usage : " << argv[0] << " port" << std::endl;
        return 0;
    }
    uint16_t localport = std::stoi(argv[1]);

    std::unique_ptr<TcpServer> svr(new TcpServer(localport, HandlerHttpRequest));

    svr->Loop();

    return 0;
}

实现效果如下:
在这里插入图片描述
在这里插入图片描述

补充知识

rz命令

rz 命令是用于在 Linux 系统中接收文件的命令,通常用于在终端下通过串口或者 SSH 连接从另一台计算机发送文件到本地计算机。这个命令通常与 sz 命令一起使用,sz 用于发送文件,rz 用于接收文件。

使用 rz 命令时,通常会通过串口或者 SSH 连接到远程计算机,然后在远程计算机上执行 sz 命令发送文件,最后在本地计算机上执行 rz 命令来接收文件。

要使用 rz 命令接收文件,你需要先确保你的 Linux 系统上已经安装了支持该命令的终端软件,例如 minicomscreenpicocom 等,以及 szrz 命令所在的软件包。

在使用 rz 命令接收文件时,默认情况下文件会保存到当前工作目录下。如果你想要控制 rz 命令将接收的文件保存到特定的目录下,你可以在执行 rz 命令之前先切换到你想要保存文件的目录。

ifstream的seekg和tellg

在 C++ 中,ifstream 是用于从文件中读取数据的输入流类,seekgtellgifstream 类的成员函数,用于在文件中定位和查询当前读取位置的偏移量。

  • seekg: seekg 函数用于在文件中定位读取位置。它有多个重载形式,可以设置文件读取位置的绝对位置或相对位置。通常情况下,seekg 函数的参数为一个 streampos 类型的偏移量,表示从文件开始位置的偏移量,但也可以使用 ios_base::seekdir 类型的参数指定相对于当前位置或者文件末尾的偏移量。

  • tellg: tellg 函数用于查询当前读取位置的偏移量。它返回一个 streampos 类型的值,表示当前读取位置相对于文件开始位置的偏移量。

这两个函数通常用于实现文件的随机访问,可以在文件中自由移动读取位置。下面是一个简单的示例:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream file("example.txt", std::ios::binary); // 打开一个二进制文件用于读取

    // 获取当前读取位置的偏移量
    std::streampos currentPosition = file.tellg();
    std::cout << "Current position: " << currentPosition << std::endl;

    // 移动读取位置到文件末尾
    file.seekg(0, std::ios::end);
    
    // 获取文件末尾的偏移量
    std::streampos endPosition = file.tellg();
    std::cout << "End position: " << endPosition << std::endl;

    // 将读取位置移动回文件开头
    file.seekg(0, std::ios::beg);
    
    // 读取文件内容
    // ...

    return 0;
}

这段代码演示了如何使用 seekgtellg 函数在文件中定位和查询读取位置的偏移量。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

5G前传光纤传输的25G光模块晶振SG2016CAN

一款适用于5G前传光纤传输网络中的25G光模块的5G晶振SG2016CAN。随着5G时代的到来&#xff0c;5G晶振的重要性也不言而喻&#xff0c;小体积宽温晶振SG2016CAN可以用于5G前传的25G光模块&#xff0c;具有高稳定性、小体积、宽温等优势。在5G前传光纤传输网络中&#xff0c;25G光…

java.sql.SQLDataException: Cannot determine value type from string 报错

报错 "org.springframework.dao.DataIntegrityViolationException: Error attempting to get column title from result set. Cause: java.sql.SQLDataException: Cannot determine value type from string 标题\n; Cannot determine value type from string 标题; neste…

ssm089理发店会员管理系统的设计和实现+vue

理发店会员管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和…

Redis入门到通关之数据结构解析-SkipList

文章目录 ☃️概述☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开发者。 博客特色&…

Python_AI库 Pandas在商业环境中的实际用途

Python_AI库 Pandas在商业环境中的实际用途 在前文中我们介绍了Pandas的各种常见操作&#xff0c;由于Pandas提供了高效、灵活且易于使用的数据结构&#xff0c;使得数据处理、清洗、分析和可视化变得更为简单&#xff0c;因此它广泛应用于各种商业应用中。 我们学习一个工具…

如何在Windows 10上刷新DNS?这里提供详细步骤

电脑的DNS缓存出现问题可能会导致连接到互联网时出现问题。如果你尝试过清除浏览器缓存和cookie等常见技巧&#xff0c;刷新Windows 10的DNS可能会解决你的问题。 DNS缓存的作用是什么 域网络系统&#xff08;DNS&#xff09;服务器将熟悉的域名转换为计算机用来相互连接的IP…

Stable Diffusion中的embedding

Stable Diffusion中的embedding 嵌入&#xff0c;也称为文本反转&#xff0c;是在 Stable Diffusion 中控制图像样式的另一种方法。在这篇文章中&#xff0c;我们将学习什么是嵌入&#xff0c;在哪里可以找到它们&#xff0c;以及如何使用它们。 什么是嵌入embedding&#xf…

linux的压缩与备份

一、打包 格式&#xff1a;tar -参数 <打包文件名> <打包的目标> 作用&#xff1a;将文件或者目录打包 重要参数&#xff1a;-f 使用归档文件&#xff0c;一定要加上这个参数 -c 新建打包文件 -x 解包文件 -t 可以不用解包就能查看包文件内容 -v 打包和解包时显…

远程桌面连接不上个别服务器的问题分析与解决方案

在日常的IT运维工作中&#xff0c;远程桌面连接&#xff08;RDP&#xff0c;Remote Desktop Protocol&#xff09;是我们经常使用的工具之一&#xff0c;用于管理和维护远程服务器。然而&#xff0c;有时我们可能会遇到无法连接到个别服务器的情况。针对这一问题&#xff0c;我…

TCP-模拟BS架构通信

简介 bs是通过浏览器进行访问的每次访问都会开启一个短期的socket用来访问服务器的资源 响应报文的格式 服务端 bs架构中的b是浏览器&#xff0c;不需要我们书写&#xff0c;我们只需要书写服务端即可 服务端 public class Server {public static void main(String[] args) {S…

Centos7 RPM包离线安装Nginx

查看是否安装nginx #使用命令 rpm -qa|grep 列出需要卸载的软件包 rpm -qa | grep nginx 卸载nginx #使用rpm -e 加包名删除 rpm -e nginx-release-centos-7-0.el7.ngx.noarch nginx-1.14.1-1.el7_4.ngx.x86_64 rpm -e nginx 安装nginx 其他版本步骤一样 下载rpm包In…

stm32的GPIO基本结构

1.带FT标号的引脚能容忍5V 2.GPIO系统架构 stm32的所有GPIO都是挂载在APB2总线上的 3.GPIO的基本结构 在上图中&#xff0c;左边就是寄存器&#xff0c;右边就是驱动器了 保护二极管的作用&#xff1a;VDD表示3.3V&#xff0c;如果输入的电压的值大于3.3V&#xff0c;那么这个…

融入本土文化:Kompas.ai助力跨境品牌本地化营销

在全球化的商业环境中&#xff0c;跨境品牌面临着将其产品和服务适应不同文化和市场的挑战。本地化营销是品牌国际化战略的关键组成部分&#xff0c;它要求品牌不仅要传递其核心价值&#xff0c;还要尊重并融入目标市场的文化特色。本文将深入探讨跨境品牌在本地化营销中面临的…

知网怎么查重 知网查重的详细步骤

知网查重八个步骤&#xff1a;1. 访问官网&#xff0c;注册账号。2. 上传待查文档。3. 选择查重规则。4. 选择相似来源库。5. 提交查重任务。6. 等待查重结果。7. 获取查重报告。8. 下载查重报告。 知网查重的详细步骤 第一步&#xff1a;进入知网查重系统 打开浏览器&#x…

数据污染对大型语言模型的潜在影响

大型语言模型&#xff08;LLMs&#xff09;中存在的数据污染是一个重要问题&#xff0c;可能会影响它们在各种任务中的表现。这指的是LLMs的训练数据中包含了来自下游任务的测试数据。解决数据污染问题至关重要&#xff0c;因为它可能导致结果偏倚&#xff0c;并影响LLMs在其他…

【EI会议|投稿优惠】2024年机械应用与能源动力国际会议(ICMAEP 2024)

2024 International Conference on Mechanical Applications and Energy Power 一、大会信息 会议名称&#xff1a;2024年机械应用与能源动力国际会议 会议简称&#xff1a;ICMAEP 2024 收录检索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等 会议官网&#xff1a;…

应用实战 | 别踩白块小游戏,邀请大家来PK挑战~

“踩白块会输”是一个简单的微信小程序游戏&#xff0c;灵感来自当年火热的别踩白块游戏&#xff0c;程序内分成三个模块&#xff1a;手残模式、经典模式和极速模式&#xff0c;分别对应由易到难的三种玩法&#xff0c;可以查看游戏排名。动画效果采用JS实现&#xff0c;小程序…

CDP客户数据平台:构建S2B2C智能名片商城的核心引擎

在数字化浪潮席卷之下&#xff0c;企业对于客户数据的整合与利用已不再是单纯的竞争优势&#xff0c;而是关乎生存与发展的必要条件。CDP&#xff08;Customer Data Platform&#xff09;客户数据平台&#xff0c;以其统一且持续可访问的特性&#xff0c;正成为S2B2C AI智能名片…

JAVA实现EasyExcel导出excel

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。快速、简洁、解决大文件内存溢出的java处理Excel工具 快速&#xff1a;快速的读取excel中的数据。简洁&#xff1a;映射excel和实体类&#xff0c;让代码变的更加简…

(二十一)C++自制植物大战僵尸游戏僵尸游戏关卡结束数据处理

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs 文件位置 代码实现的文件在Class\Scenes\GameScene文件夹中,如下图所示。 GameEndLayer.h class GSGameEndLayer :public LayerColor { public:CREATE_FUNC(GSGameEndLayer);void successfullEntry();void brea…