Linux网络——HTTP

news2024/12/26 11:26:50

一.应用层

我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层.

我们上一次写的网络版本计算器就是一个应用层的网络程序。

我们约定了数据的读取,一端发送时构造的数据, 在另一端能够正确的进行解析, 就是ok的. 这种约定, 就是应用层协议。

在应用层我们只负责将数据怎么读取,和怎么构造数据,然后将构造好的数据交给应用层的协议层传输层,再由传输层何其一下的网络协议栈来帮我们完成数据在网络中发送,至于数据在这之间是怎么发送的,什么时候发送的,我们不知道,不清楚,也不关心。

二.认识URL

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

1.域名

服务器地址可以是一个IP地址,但是IP地址是点分十进制的字符串,为了方便记忆就有了和IP地址一 一对应的域名。

例如:

  • 112.29.213.131 —— www.JD.com
  • 36.155.132.55   —— www.baidu.com

2.urlencode和urldecode

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

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

 "+" 被转义成了 "%2B"。
urldecode就是urlencode的逆过程。

三.HTTP协议格式 

1.请求格式http

见一见http请求:

部分测试代码:

编写一个服务器,服务器多线程接受网络数据,使用浏览器在url栏框中输入服务端车程序的IP和端口,然后回车。此时浏览器就会构建一个http请求发送给我们的服务端程序,服务端程序直接将收到的数据直接以字符串的形式输出到终端。

    void serverIO(int fd)
    {
        string message;
        char buff[102400];
        // 读取一个完整的http请求报文
        int n = recv(fd, buff, sizeof(buff), 0);
        message = buff;
        // 直接将该请求以字符串的形式输出
        cout << "得到一个HTTP请求" << endl;
        cout << message << endl;
        close(fd);
    }

得到的http请求:

2.响应格式

见一见http响应:

使用telnet 向百度模拟发送一个http请求,接受返回的http响应。

HTTP协议的格式大致有四部分组成:

  1. 首行:HTTP请求——[ 方法 ]+[ url ]+[ 版本 ];HTTP响应——[ 版本 ]+[ 状态码 ]+[ 描述 ];其中请求中url实际上就是服务器上的请求的资源路径。
  2. Header:请求/响应 的属性,冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束。例如:Content-Length:8899,标识 请求/响应 的有效载荷长度,有效载荷的长度是8899。
  3. 空行:用来分隔Header和Body。
  4. Body:就是有效载荷部分,空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;

四.HTTP响应状态码

状态码类别描述
1XX

lnformational(信息性状态码)

接收的请求正在处理

2XX

Success(成功状态码)

请求正常处理完毕

3XX

Redirection(重定向状态码)

需要进行附加操作以完成请求

4XX

Client Error(客户端错误状态码)

服务器无法处理请求

5XX

Server Error(服务器错误状态码)

服务器处理请求出错

说明:

Client Error:

  • 客户端连接的端口错了。
  • 客户端连接的域名或者IP地址错误。
  • 如果客户端使用了域名连接,域名可能指向了错误的服务器。
  • 当客户端发送了一个无效请求时,服务器可能会因为无法处理而返回 Client  Error。

lnformational:

  • 主要作用是告知客户端请求已经被接受 。

Redirection:

  • 是服务器在处理客户端请求时,为了保证流程的顺利进行而发出的一种信号。
  • 当客户端发送一个请求到服务器,服务器会根据需要将请求重定向到另一个URL。

 Success:

  • 表示客户端与服务器之间的通信已经成功完成,并且服务器已经处理了客户端的请求。

 Server Error:

  • 表示服务器在处理客户端请求时发生了错误。
  • 服务器遇到了一个未知的错误,无法完成请求的处理。
  • 服务器当前无法处理请求,因为过载或维护。

五.HTTP常见Header

Header是以K/V形式存储的字符串,主要是一些协议的属性说明等。

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

 介绍Content-Type:表明此次http报文的body传输的是什么内容。

对于服务器上的每一种资源,都是以文件的形式存在的,文件都会有自己的类型,例如(网页文件).html,(音频文件).mp3,(图片).jpg/.png,(视频).mp4。

每一种文件类型,都会有自己对应的Content-Type类型:

  • text/html:HTML 文档
  • text/css:CSS 样式表
  • text/javascript:JavaScript 脚本
  • image/jpeg:JPEG 图像
  • image/png:PNG 图像
  • image/gif:GIF 图像

Content-Type 对照表

六.简单的HTTP服务器

编写一个服务器,多线程处理请求,对于请求的处理,解析一个http请求反序列化,根据http的请求的资源,构建响应并返回。

Sock.hpp

#pragma once
#include <iostream>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM
const static int backlog = 32;

enum
{
    SOCK_ERR = 10,
    BING_ERR,
    LISTEN_ERR,
    CONNECT_ERR
};

class Udp
{
public:
    Udp(int SOCK)
    {
        _listensock = socket(AF_INET, SOCK, 0);
        if (_listensock == -1)
        {
            Logmessage(Fatal, "socket err ,error code %d,%s", errno, strerror(errno));
            exit(SOCK_ERR);
        }
    }
    Udp(uint16_t port, int SOCK)
        : _port(port)
    {
        _listensock = socket(AF_INET, SOCK, 0);
        if (_listensock == -1)
        {
            Logmessage(Fatal, "socket err ,error code %d,%s", errno, strerror(errno));
            exit(10);
        }
    }

    void Bind()
    {
        struct sockaddr_in host;
        host.sin_family = AF_INET;
        host.sin_port = htons(_port);
        host.sin_addr.s_addr = INADDR_ANY; // #define INADDR_ANY 0x00000000
        socklen_t hostlen = sizeof(host);
        int n = bind(_listensock, (struct sockaddr *)&host, hostlen);
        if (n == -1)
        {
            Logmessage(Fatal, "bind err ,error code %d,%s", errno, strerror(errno));
            exit(BING_ERR);
        }
    }

    int FD()
    {
        return _listensock;
    }
    ~Udp()
    {
        close(_listensock);
    }

protected:
    int _listensock;
    uint16_t _port;
};

class Tcp : public Udp
{
public:
    Tcp(uint16_t port)
        : Udp(port, TCP)
    {
    }

    Tcp()
        : Udp(TCP)
    {
    }

    void Listen()
    {
        int n = listen(_listensock, backlog);
        if (n == -1)
        {
            Logmessage(Fatal, "listen err ,error code %d,%s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
    }

    int Accept(string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in client;
        socklen_t clientlen;
        int sock = accept(_listensock, (struct sockaddr *)&client, &clientlen);
        if (sock < 0)
        {
            Logmessage(Warning, "bind err ,error code %d,%s", errno, strerror(errno));
        }
        else
        {
            *clientip = inet_ntoa(client.sin_addr);
            *clientport = ntohs(client.sin_port);
        }
        return sock;
    }

    void Connect(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());
        socklen_t hostlen = sizeof(server);
        int n = connect(_listensock, (struct sockaddr *)&server, hostlen);
        if (n == -1)
        {
            Logmessage(Fatal, "Connect err ,error code %d,%s", errno, strerror(errno));
            exit(CONNECT_ERR);
        }
    }

    ~Tcp()
    {
    }
};

Server.hpp

#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include "Sock.hpp"

using fun_t = std::function<string(string &)>;
class Httpserver;
struct Args
{
    Args(Httpserver *ser, string ip, uint16_t port, int fd)
        : _ip(ip), _port(port), _pserver(ser), _fd(fd)
    {
    }
    int _fd;
    uint16_t _port;
    string _ip;
    Httpserver *_pserver;
};

class Httpserver
{
public:
    Httpserver(fun_t func, uint16_t port)
        : _func(func)
    {
        tcp = new Tcp(port);
        tcp->Bind();
        tcp->Listen();
        cout << "服务器创建成功" << endl;
    }
    void start()
    {
        while (1)
        {
            string clientip;
            uint16_t clientport;
            cout << "start accept" << endl;
            int sock = tcp->Accept(&clientip, &clientport);
            cout << "get a new connect" << endl;
            // 多线程处理请求
            pthread_t t;
            Args *args = new Args(this, clientip, clientport, sock);
            pthread_create(&t, nullptr, ThreadRun, args);
        }
    }

    ~Httpserver()
    {
        delete tcp;
    }

private:
    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        Args *ts = static_cast<Args *>(args);
        ts->_pserver->serverIO(ts->_fd);
        delete ts;
        return nullptr;
    }

    void serverIO(int fd)
    {

        string message;
        char buff[102400];
        // 1.确信,读取一个完整的http请求报文
        int n = recv(fd, buff, sizeof(buff), 0);
        message = buff;

        string re = _func(message);
        send(fd, re.c_str(), re.length(), 0);
        close(fd);
    }

private:
    Tcp *tcp;
    fun_t _func;
};

Server.cc

#include <sstream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include "Server.hpp"
#include "util.hpp"

const string wwwroot = "./html/3";
const string defaupath = "/index.html";

class HttpRequest
{
public:
    HttpRequest()
    {
    }

    ~HttpRequest() {}
    void Print()
    {
        cout << method_ << ":" << url_ << ":" << httpVersion_ << endl;

        for (auto &e : body_)
            cout << e << endl;
        cout << "suffix:" << suffix_ << endl;
        cout << "path:" << path_ << endl;
    }

public:
    std::string method_;            // 请求方法
    std::string url_;               // 资源地址
    std::string httpVersion_;       // 协议版本
    std::vector<std::string> body_; // 报头

    std::string path_;   // 请求资源路径
    std::string suffix_; // 资源类型
};

HttpRequest Deserialize(string &message)
{
    HttpRequest req;
    // 1.拿到请求行
    string oneline = HeadOneLine(message, SEP);
    // 2.解析请求行
    DisposeOneLine(oneline, &req.method_, &req.url_, &req.httpVersion_);
    // 3.解析反序列化报头
    while (!message.empty())
    {
        string mes = HeadOneLine(message, SEP);
        req.body_.push_back(mes);
    }
    // 4.设置请求资源路径 url:/a/b/c
    if (req.url_[req.url_.length() - 1] == '/')
    {
        req.path_ = wwwroot + defaupath; //./html/index.html
    }
    else
    {
        req.path_ = wwwroot + req.url_; //./html/a/b/c
    }

    // 5.设置请求资源类型
    auto pos = req.url_.find('.');
    if (pos == string::npos)
        req.suffix_ = ".html";
    else
        req.suffix_ = req.url_.substr(pos);

    return req;
}

string Dispose(string &message)
{
    // 这里我们一定读取的是一个完整的报文

    // 一个网页会有很多的资源,每一个资源(网页,图片,视频),都需要一次http请求来得到
    // 所以我们需要知道,每次请求的资源是什么,即需要知道请求的url是什么,url的类型是什么
    // 反序列化请求
    HttpRequest req = Deserialize(message);
    req.Print();
    // 响应————最简单的一个响应
    // 4.有效载荷
    string body = Readfile(req.path_) + SEP;
    // 1.响应的状态行————"HTTP版本 状态码 状态描述\r\n"
    string request_head = string("HTTP/1.0 200 OK") + SEP;
    // 2.响应报头————Content-Length,Content-Type
    request_head += string("Content-Length: ") + to_string(body.length()) + SEP;
    request_head += GetContentType(req.suffix_) + SEP;
    // 3.空行
    request_head += SEP;
    // 整体的响应报文
    string responce = request_head + body;
    return responce;
}

void daemonize()
{
    // 1.忽略SIGPIPE信号
    signal(SIGPIPE, SIG_IGN);
    // 2.更改进程的工作目录
    // chdir();

    // 3.让自己不要成为进程组组长
    if (fork() > 0)
        exit(0);
    // 4.设置自己是一个独立的会话
    setsid();
    // 5.重定向0,1,2
    int fd = 0;
    if (fd = open("dev/null", O_RDWR) != -1)
    {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        // 6.关闭掉不需要的fd
        if (fd > STDERR_FILENO)
        {
            close(fd);
        }
    }
}

int main(int argc, char *argv[])
{
    daemonize();
    uint16_t port = atoi(argv[1]);
    Httpserver httpser(Dispose, port);
    httpser.start();

    return 0;
}

Util.hpp

#pragma once
#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define SEP "\r\n"
using namespace std;

string Readfile(const string path)
{
    // 1. 获取文件本身的大小
    string fileContent;
    struct stat st;
    int n = stat(path.c_str(), &st);
    if (n < 0)
        return "";
    int size = st.st_size;
    // 2. 调整string的空间
    fileContent.resize(size);
    // 3. 读取
    int fd = open(path.c_str(), O_RDONLY);
    if (fd < 0)
        return "";
    read(fd, (char *)fileContent.c_str(), size);
    close(fd);
    return fileContent;
}

string HeadOneLine(string &message, const string &sep)
{
    auto pos = message.find(sep, 0);
    if (pos == string::npos)
        return "";
    string oneline = message.substr(0, pos);
    message.erase(0, pos + sep.size());
    return oneline;
}

void DisposeOneLine(const string &oneline, string *method, string *url, string *httpVersion)
{
    stringstream line(oneline);
    line >> *method >> *url >> *httpVersion;
}

string GetContentType(const string &suffix)
{
    std::string content_type = "Content-Type: ";
    if (suffix == ".html" || suffix == ".htm")
        content_type + "text/html";
    else if (suffix == ".css")
        content_type += "text/css";
    else if (suffix == ".js")
        content_type += "application/x-javascript";
    else if (suffix == ".png")
        content_type += "image/png";
    else if (suffix == ".jpg")
        content_type += "image/jpeg";
    else
    {
    }
    return content_type;
}

效果展示:

七.HTTP的方法

HTTP的方法有很多但是最常用的只有两个:GET,POST。

1.GET方法

我们要促使浏览器使用不同的方法进行资源请求,提交,要使用html的表单。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSDN</title>
</head>

<body>

    <h1>test</h1>
    <form action="/a/b/c.exe" method="get">
        姓名: <input type="text" name="myname" value=""><br />
        密码: <input type="password" name="mypasswd"><br />
        <input type="submit" value="提交"><br />
    </form>

</body>

</html>

尝试提交数据:

2.POST方法

再次尝试提交数据:

 区别:

  1. GET能获取一个静态网页,GET也能提交参数,通过URL的方式提交参数。
  2. POST请求,提交的参数的时候,是通过正文的部分提交的参数。
  3. GET方法提交参数,不私密(没有不安全的说法),安全对HTTP来说本身就没有保障。
  4. POST提交参数比较私密一些。
  5. 由于GET使用url提交参数,所以大小一般会受限。
  6. POST使用正文提交参数,正文理论上可以非常大。

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

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

相关文章

电子电器架构 —— 车载网关边缘节点路由转发策略

电子电器架构 —— 车载网关边缘节点路由转发策略 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 PS:小细节,本文字数5000+,详细描述了网关在车载框架中的具体性能设置。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无…

SpringCloud -Token传递之Feign

目录 方法一 RequestHeader 方法二 使用Feign的Interceptor 步骤一 实现RequestInterceptor接口 步骤二&#xff1a;配置Feign 通常微服务对于用户认证信息解析有两种方案 在 gateway 就解析用户的 token 然后路由的时候把 userId 等相关信息添加到 header 中传递下去。在…

观测云助力跨境电商大幅提高加载性能

话不多说&#xff0c;先上结果 什么是用户体验 用户体验基本包含访问网站的性能、可用性和正确性。通俗的讲&#xff0c;就是一把通过用户访问测量【设计者】意图的尺子。 用户体验的基本价值 如果正确实施了终端用户体验&#xff0c;可以第一时间发现&#xff0c;确认影响了…

腾讯云服务器新用户优惠有哪些?腾讯云服务器新人优惠整理汇总

你们是否曾经幻想过拥有一台属于自己的服务器&#xff0c;却因为价格而望而却步呢&#xff1f;今天&#xff0c;我要告诉你一个好消息——腾讯云服务器现在针对新用户推出了一系列的优惠政策&#xff0c;让你可以用超低的价格购买到性能强大的服务器&#xff01; 首先&#xf…

后端老项目迁移方法

老项目迁移方法 需求&#xff1a; 因某个模块MySQL表结构、表关系 错乱复杂&#xff0c;而且其他模块的代码也在操作这个模块的数据库 耦合严重 导致Web工程代码紊乱、不易理解、性能低下&#xff0c; 故在 系统由A JavaWeb工程迁移至B工程 时&#xff0c;重构MySQL表结构、表…

LaTeX 数学公式常见问题及解决方案

本文汇总了博主在使用 LaTeX 写文档过程中遇到的所有数学公式常见问题及对应的 LaTeX 解决方案 持续更新... 目录 1. 连等式2. 公式重新开始编号2.1 图片/表格重新编号 1. 连等式 在数学公式推导过程中常常会遇到如 Figure 1 所示的连等式&#xff0c;一般需要保证等号或者不等…

Halcon (0):C# 联合Halcon方式简介和就业市场说明

文章目录 文章专栏前言相关视频联合C#开发直接导出C#代码Halcon引擎调用开发函数封装库工程导出 总结就业市场 文章专栏 Halcon开发 前言 根据我的测试&#xff0c;我发现Halcon和WPF中的halcon插件&#xff0c;代码具有对应性。就是你会了Halcon&#xff0c;WPF也差不多久会了…

带您识别RJ45网口连接器/网口插座口的LED灯的平脚/斜脚,带弹/不带弹细节区分

Hqst华强盛&#xff08;盈盛电子&#xff09;导读&#xff1a;网口连接器,网口插座&#xff0c;也叫网口母座,因为产品规格众多&#xff0c;常常因为细小差别&#xff0c;耽误工程设计级或者生产排期延误&#xff0c;今天就带大家一起来认识下平脚RJ45网口连接器/网口插座与斜脚…

基于深度学习的单帧图像超分辨率重建综述

论文标题&#xff1a;基于深度学习的单帧图像超分辨率重建综述作者&#xff1a; 吴 靖&#xff0c;叶晓晶&#xff0c;黄 峰&#xff0c;陈丽琼&#xff0c;王志锋&#xff0c;刘文犀发表日期&#xff1a;2022 年9 月阅读日期 &#xff1a;2023.11.18研究背景&#xff1a; 图像…

剑指JUC原理-20.并发编程实践

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

CTF-PWN-tips

文章目录 overflowscanfgetreadstrcpystrcat Find string in gdbgdbgdb peda Binary ServiceFind specific function offset in libc手工自动 Find /bin/sh or sh in library手动自动 Leak stack addressFork problem in gdbSecret of a mysterious section - .tlsPredictable …

【Python数学练习1】

一、题目 中文描述&#xff1a; 给出正整数N&#xff0c;输出满足条件的数对(a,b)的个数&#xff0c;满足gcd(a,b)b, a,b < n 数学描述&#xff1a; 二、解法 解法1&#xff1a; 对应Python代码&#xff1a; def num_fact(n):num 0for i in range(1, n 1):if n % i …

Python程序打包指南:手把手教你一步步完成

最近感兴趣想将开发的项目转成Package&#xff0c;研究了一下相关文章&#xff0c;并且自己跑通了&#xff0c;走了一下弯路&#xff0c;这里记录一下如何打包一个简单的Python项目&#xff0c;展示如何添加必要的文件和结构来创建包&#xff0c;如何构建包&#xff0c;以及如何…

视频剪辑技巧:简单步骤,批量剪辑并随机分割视频

随着社交媒体平台的广泛普及和视频制作需求的急剧增加&#xff0c;视频剪辑已经成为了当今社会一项不可或缺的技能。然而&#xff0c;对于许多初学者来说&#xff0c;视频剪辑可能是一项令人望而生畏的复杂任务。可能会面临各种困难&#xff0c;如如何选择合适的软件和硬件、如…

栈和队列的初始化,插入,删除,销毁。

目录 题外话 顺序表和链表优缺点以及特点 一.栈的特点 二. 栈的操作 2.1初始化 2.2 栈的销毁 2.3 栈的插入 2.3 输出top 2.4 栈的删除 2.5 输出栈 题外话 顺序表和链表优缺点以及特点 特点&#xff1a;顺序表&#xff0c;逻辑地址物理地址。可以任意访问&#xff0c…

systemverilog:interface中端口方向、Clocking block的理解

1、interface中端口方向的理解 &#xff08;1&#xff09;从testbench的角度看&#xff0c;tb中信号的输入输出方向与interface中信号输入输出方向一致&#xff1a; &#xff08;2&#xff09;从DUT角度看&#xff0c;DUT中信号输入输出方向与interface中信号输入输出方向相反…

数据库的分库分表 详解

前言 一个系统随着用户量上升&#xff0c;产生的数据也越来越多&#xff0c;到达一定程度&#xff0c;数据库就会产生瓶颈。 首先单机数据库所能承载的连接数&#xff0c;io和吞吐量都是有限的&#xff0c;并发量上来数据库就渐渐顶不住了。 如果单表的数据量过大&#xff0…

腾讯云新用户优惠活动有哪些可以参加?腾讯云新人服务器优惠活动

腾讯云作为国内领先的云服务提供商&#xff0c;不仅为用户提供稳定可靠的云服务器&#xff0c;还为新用户带来了一系列的优惠活动和代金券&#xff0c;以降低购买成本&#xff0c;提高业务效益。在这里&#xff0c;我们将为您详细介绍腾讯云服务器的新人优惠活动及代金券&#…

JavaEE——简单认识HTML

文章目录 一、简单解释 HTML二、认识 HTML 的结构三、了解HTML中的相关标签1.注释标签2.标题标签3.段落标签 p4. 换行标签 br5.格式化标签6.图片标签解释 src解释 alt解释其他有关 img 标签的属性 7.超链接标签 a8.表格标签9.列表标签10.input 标签11. select 下拉菜单以及 div…

Flink1.17 DataStream API

目录 一.执行环境&#xff08;Execution Environment&#xff09; 1.1 创建执行环境 1.2 执行模式 1.3 触发程序执行 二.源算子&#xff08;Source&#xff09; 2.1 从集合中读取数据 2.2 从文件读取数据 2.3 从 RabbitMQ 中读取数据 2.4 从数据生成器读取数据 2.5 …