Linux | 网络通信 | http协议介绍 | cookie策略讲解

news2024/11/24 20:39:35

文章目录

    • url统一资源定位符
    • http协议介绍
      • GET vs POST
      • http状态码
      • http常见header
    • cookie + session

上篇博客定制了一个协议,该协议用来进行简单的计算,其包含了数据的序列化和反序列化,编码和解码的定制,并且该协议基于TCP通信,是一种客户端请求服务端响应的模型。如果你也实现了一个自己的协议,肯定会有疑问:自己定制的协议不够成熟,而且应用场景有限,有没有一个成熟且应用场景广泛的协议?这还真有,它就是上网必须使用的协议——http,https

url统一资源定位符

要了解http就要先了解url
在这里插入图片描述
协议名:表示该url采用的协议
登录信息:一般在url中被隐去,其体现在http请求的报头或者正文当中
服务器地址:也称域名,域名最终会被解析为IP地址
服务器端口号:网络通信的本质是进程间通信,所以需要告知IP+端口号,使通信能够进行。但是由于服务器的端口号通常是固定的,不写端口号时,端口号也能通过其他特殊方法得知,通信照样能进行
文件路径:用具体的路径来表示想要访问服务器的哪些资源
查询字符串:通常用来过滤网页中的信息,得到想要的信息
片段标识符:也称锚点,用来指定网页的停靠位置,或者音视频的播放位置

可以看到,url中不同字段需要用特殊符号进行分隔,这就意味着每一字段中不能出现这些特殊符号,或者说如果出现了这些特殊符号,需要对这些特殊符号做处理。比如搜索c++这这个关键字在这里插入图片描述
由于‘+‘是特殊字符,所以其被编码成其他格式。再者,搜索汉字时,汉字也会被重新编码
在这里插入图片描述
虽然url栏显式的是汉字,但是把url复制下来再粘贴,得到的url是

https://www.baidu.com/s?wd=%E5%93%88%E5%93%88

这里说明一下特殊字符的编码规则,将特殊字符转换成十六进制,以两个十六进制数为一组,从低到高一次取出每一组,再它们的前面加上%,编码成%XY的形式。查询ASCII码表,+的码值为43,表示成十六进制是2B,所以在url中,其被编码成%2B
在这里插入图片描述
由于url采用的是utf-8编码格式,在该格式下汉字被编码成3个字节,由于两个十六进制数表示1个字节,刚才“哈哈”被编码后,有6组%XY格式的数据,也就是6个字节。关于把特殊字符进行编码的过程,我们叫做urlencode,将%XY格式的数据解码的过程,我们叫做urldecode

再回过头来看url,其全称是Uniform Resource Locator,统一资源定位符,通过url我们就能定位互联网上某一台主机的某些资源。我们使用http协议进行请求,就是请求获取某一天主机(服务器)上的某些资源(音视频,文本),当然,服务器的响应就是将客户端请求获取的资源返回。所以说,我们访问的网页,看到的视频,文字,不是凭空产生的,这些资源都是存储在某些服务器的磁盘上,在我们请求资源时,由服务器发送给我们的。

http协议介绍

比起上篇博客,自己定制的协议,http协议能够使用的场景实在是太多了,同样的,关于http的协议格式也是更复杂的在这里插入图片描述
可以看到http协议格式使用\r\n作为不同字段的分隔符,以http请求为例,在第一个分隔符之前的数据就是http的请求行,里面含有

method:具体http请求的方法,如GET,POST
url:刚才说过的,url用来定位具体资源
http/1.1:http协议的版本

在第一行,请求行之后的字段就是请求报头,其中可能含有客户端主机的信息,连接的属性,有效载荷的长度等等信息,这些信息以key:value的格式保存在报头中,由于这些字段以\r\n分隔,所以可以直观的认为报头的每一行都是一对key:value信息。继续这样进行读取,我们会遇到一个空行,也就是说在报头的最后一个key:value后会有两个\r\n,前一个\r\n用来分隔最后的key:value字段,那么后一个key:value是用来划分有效载荷与报头的,读取http请求时,如果遇到了一个空行,就说明接下来的数据是这次请求的有效载荷了。客户端的请求无非两种,一是请求获取服务端的资源,二是请求将客户端的资源上传到服务端,这些信息都将在有效载荷中体现

至于服务端的响应,其格式与客户端的请求几乎相同,都是三个字段:响应行,响应报头,有效载荷。它们的分隔规则是一样的,不同的是响应行中的数据

http/1.1 状态码 状态码描述

可以看到请求和响应都含有协议的具体版本信息,这样做的目的是:为了使通信能够正常进行,通信双方要保证通信协议版本的一致性,所以客户端和服务端互相发送http协议的版本,如果两者的版本不同,将按照:以较低版本进行通信的原则,进行协议的调整。响应行还包括了状态码和其描述,通常我们请求的网页都是能正常访问的,但是也会有错误情况出现,错误码就表征了请求中的错误,最常见到的错误码就是404,如果你要访问的服务端资源不存在,那么服务端的状态码将被设置为404

GET vs POST

http协议中有很多方法:GET,POST,PUT,DELETE,OPTIONS…其中最经常使用的方法是GET,POST,至于其他方法就较为少见了,如果遇到就现学吧。这里主要说明GET和POST的区别,其中的依据来自
HTTP 方法:GET 对比 POST。首先,GET和POST都是明文传送,两者都是不安全的,使用这两个方法时,我们的数据总是在网络上裸奔,只是方式不同罢了

(在之前的博客中,我搭建了一个TCP网络通信模型,由于http协议是基于tcp的,所以我这里就直接改造这个模型,实现一份服务端代码,对浏览器(客户端)的请求做出响应)

#include <iostream>
#include <string>
#include <stdlib.h>
#include <fstream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

using namespace std;

#define CRLF "\r\n"
#define HOME_PAGE "index.html"
#define ROOT_PAGE "wwwroot"
#define SPACE " "

string getPath(const string& req)
{
    // 请求行的获取
    size_t head_pos = req.find(CRLF);
    if (head_pos == string::npos)
        return "";
    string head = req.substr(0, head_pos);
    // 资源路径的获取
    size_t path_start = head.find(SPACE);
    size_t path_end = head.rfind(SPACE);
    if (path_start == string::npos || path_end == string::npos)
        return "";
    // 获取资源地址
    string path = head.substr(path_start + 1, path_end - path_start - 1);
    // 如果地址为/,默认访问家目录
    if (path[0] == '/' && path.size() == 1) path += HOME_PAGE;
    return path;
}

string readFile(const string& path)
{
    ifstream file(path);
    if (!file.is_open()) return "404";
    // 将资源的所有数据返回
    string line;
    string content;
    // 读取整份文件的内容
    while (file.peek() != EOF)
    {
        getline(file, line);
        content += line;
    }

    return content;
}

// 先获取请求报头中的资源位置
// 读取该资源,将其返回
void handlerRequest(int sock)
{
    char buf[10240] = {0};
    ssize_t r_ret = read(sock, buf, sizeof(buf));
    if (r_ret < 0)
        cout << "read fail" << endl;
    else if (r_ret == 0)
        cout << "client quit" << endl;
    // 读取成功
    else
    {
        string req_str = buf;
        // for test
        cout << req_str << endl;
        // 获取请求行中的资源
        string path = getPath(req_str);
        // 读取客户请求的资源,当前根路径的保存
        string resource_path = ROOT_PAGE;
        resource_path += path;
        // 获取文件资源
        string resource = readFile(resource_path);
        string suffix = "";
        // 获取文件的后缀
        size_t suffix_pos = resource_path.rfind('.');
        if (suffix_pos != string::npos)
            suffix = resource_path.substr(suffix_pos);
        // 创建响应
        string response = "HTTP/1.1 200 OK\r\n";
        // 根据后缀为响应报头添加不同字段
        if (suffix == ".jpg") response += "Content-type: image/jpeg\r\n";
        else response += "Content-type: text/html\r\n";
        // 请求正文长度字段的添加
        response += ("Content-Length: " + to_string(resource.size()) + "\r\n");
        response += "\r\n";
        // 响应正文
        response += resource;
        // 向客户端发送响应
        write(sock, response.c_str(), response.size());
    }
}

class tcpServer
{
public:
    tcpServer(uint16_t port, std::string ip = "") : _ip(ip), _port(port) {}
    ~tcpServer() {}

    void init()
    {
        // 创建套接字文件
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            std::cerr << "socket: fail" << std::endl;
            exit(-1);
        }
        // 填充套接字信息
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        _ip.empty() ? local.sin_addr.s_addr = INADDR_ANY : inet_aton(_ip.c_str(), &local.sin_addr);
        // 将信息绑定到套接字文件中
        if (bind(_listen_sockfd, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            std::cerr << "bind: fail" << std::endl;
            exit(-1);
        }
        // 至此,套接字创建完成,所有的步骤与udp通信一样
        // 使套接字进入监听状态
        if (listen(_listen_sockfd, 5) < 0)
        {
            std::cerr << "listen: fail" << std::endl;
            exit(-1);
        }
        // 套接字初始化完成
        std::cout << "listen done" << std::endl;
    }


    void loop()
    {
        // signal(SIGCHLD, SIG_IGN); // 设置SIGCHLD信号为忽略,这样子进程就会自动释放资源
        // 创建保存套接字信息的结构体
        struct sockaddr_in peer;
        socklen_t peer_len = sizeof(peer);
        // 接受监听队列中的套接字请求
        while (true)
        {
            int server_sockfd = accept(_listen_sockfd, (struct sockaddr *)&peer, &peer_len);
            if (server_sockfd < 0)
            {
                std::cerr << "accept: fail" << std::endl;
                continue;
            }
            std::cout << "accept done" << std::endl;

            // 提取请求方的套接字信息
            uint16_t peer_port = ntohs(peer.sin_port);
            std::string peer_ip = inet_ntoa(peer.sin_addr);
            // 打印请求方的套接字信息
            std::cout << "accept: " << peer_ip << " [" << peer_port << "]" << std::endl;

            // 使用孙子进程提供服务
            // grandparent
            pid_t id = fork();
            // 祖父进程
            if (id == 0)
            {
                // 父进程
                pid_t cid = fork();
                if (cid > 0)
                {
                    // 关闭父进程
                    exit(1);
                }
                // 孙子进程,孤儿,由1号进程管理
                handlerRequest(server_sockfd);
            }
            // 阻塞的回收进程资源
            waitpid(id, nullptr, 0);
        }
    }
private:
    std::string _ip;
    uint16_t _port;
    int _listen_sockfd;
};

static void Usage(std::string proc)
{
    std::cerr << "Usage:\n\t" << proc << " port" << std::endl;
    std::cerr << "example:\n\t" << proc << " 8080\n"
              << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = atoi(argv[1]);

    tcpServer svr(port);
    svr.init();
    svr.loop();
    return 0;
}

tcp通信的部分就不再赘述了。为防止僵尸进程,该通信模型创建孙子进程为客户端提供服务,而退出子进程,使孙子进程成为孤儿进程,由1号进程负责其资源的释放。服务端接收到客户端的请求,会对该请求进行解析,得到请求需要的资源路径,服务端会将该资源返回给客户端。下面这份网页就是服务端默认返回给用户的资源

<!-- index.html -->
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>测试</title>
</head>

<body>
    <h3>hello my server!</h3>
    <p>我终于测试完了我的代码</p>
    <form action="/a/b/c.html" method="get">
        Username: <input type="text" name="user"><br>
        Password: <input type="password" name="passwd"><br>
        <input type="submit" value="Submit">
    </form>
</body>

</html>

这个网页中有一个表单,客户可以提交它们的用户名和密码信息,其中action标签表示将这些信息发送到指定网页上(当然是客户端再向服务端发送一次请求了),为了测试简单,这里将信息发送到一个不存在的网页中,我们只观察GET和POST的区别。
在这里插入图片描述
注意,服务端接收到客户端的请求后,会将客户端的请求打印出来,方便我们的测试,在浏览器的url中输入IP:port,访问服务在这里插入图片描述
我们输入用户名123,密码456,点击submit按钮,这些数据会被发送到action指定的网页上,并且方法是get
在这里插入图片描述

很正常,因为网页不存在,所以返回404。但是我们注意到,输入的用户名和密码在url中以查询字符串的方式显示了出来。在这里插入图片描述
将网页提交数据的方式修改为post,重新运行服务,重新连接到该服务,输入用户名密码,submit
在这里插入图片描述

在这里插入图片描述
可以看到,用户名和密码没有在url中体现,观察服务端打印的客户端请求,可以看到这些信息出现在请求的正文部分

总结一下:GET将数据以查询字符串的方式拼接到url中,而POST将数据以正文的方式置于请求中,两者都是明文传输,不同的只是POST较GET更隐蔽一些,但两者在本质上都是不安全的。

http状态码

1xx信息状态码Informational接收的请求正在处理,由于现在的网络响应快,这样的状态码很少使用了
2xx成功状态码Success请求正常处理完毕
3xx重定向状态码Redirection将请求重定向到其他资源上
4xx客户端错误状态码Client Error服务器无法处理请求,请求非法
5xx服务端错误状态码Server Error服务端运行出错

这些状态码也不需要具体记忆,记住几个常用的就行了,比如200(OK),404(Not Found),403(Forbidden),302(Redirect),504(Bad Gateway,服务器访问上游服务器超时)

void for_redirection(int sock)
{
    char buf[10240] = {0};
    ssize_t r_ret = read(sock, buf, sizeof(buf));
    if (r_ret < 0)
        cout << "read fail" << endl;
    else if (r_ret == 0)
        cout << "client quit" << endl;
    // 读取成功
    else
    {
        string req_str = buf;
        // 创建响应
        string response = "HTTP/1.1 302 Moved Temporarily\r\n";
        // 请求正文长度字段的添加
        response += "location: https://baike.baidu.com/item/302/878045?fr=aladdin\r\n";
        response += "\r\n";
        // 向客户端发送响应
        write(sock, response.c_str(), response.size());
    }
}

简单的,将服务端的响应修改,请求行修改为"HTTP/1.1 302 Moved Temporarily\r\n",然后在报头中添加location字段,表示重定向的url,如果服务端只提供以上服务的话,不论客户端向服务端发送什么,客户端都将跳转到location字段的url上。不过关于301和302的区别还是要注意一下的,301是当前资源的永久性转移,而302是资源的临时性转移。如果收藏夹中,有一个url301了,浏览器可能会将该url修改为新的url,而302就不会

http常见header

Content-type:正文数据类型(html/text,image/jpeg)
Content-Length:正文长度
Host:告知服务端,客户端要访问的资源在哪台主机上
User-Agenr:声明用户的操作系统和浏览器版本信息
referer:当前页面是从哪个页面跳转过来了
location:配合3xx使用,指定重定向的url,客户端将访问该url
Cookie:存储用户一些数据,用来进行会话的维持

header就是http中的报头字段,以上展示的是字段中的key,每一字段以key:value的方式呈现在这里插入图片描述
最后还剩一个值得讲解的header:Connection,http1.0的Connection默认为closed,http1.1的Connection默认为keep-alive,一个为短连接一个为长连接。在互联网发展早期,网页中的信息没有现在这么的密集,一次http请求就可以获取完整的网页并呈现给客户。但是随着互联网的发展,网页中的信息越来越多,越来越复杂,所以一次http协议无法获取完整的网页,而http是基于tcp通信的,多次http请求就代表这多次tcp的连接,这样的做法势必会导致网页加载速度的下降,由此http/1.1默认开启长连接,只有一个网页的资源请求完成,tcp连接才会关闭。关于长连接的深入学习,具体实现可以阅读这篇博客

cookie + session

http有一个特性:无状态,即无法进行状态的保持,每一次请求都是独立的。但在网页端浏览b站时,登录账号过后,每次访问b站都会保持你的登录信息,其中的原理就与cookie有关在这里插入图片描述
在这里插入图片描述
(上图来自网络)当然了,首次登录b站时,你还是需要输入你的账号密码的,一旦选择登录,浏览器就会将你的用户信息通过http协议传输到b站的服务端,服务端认证成功,确认该账号是有效的之后,会生成一个cookie文件,并发送一个set-Cookie的响应,使客户端在磁盘或者内存上保持该cookie文件。之后客户端的http请求就会携带这份cookie文件,服务端收到cookie文件后进行解析,得到有效数据认证成功之后,才会将特定的响应返回给客户端,此时客户端看到的网页就是已经登陆账号的网页了。

如果cookie存储在磁盘上,那么关闭浏览器,甚至关机之后再打开该网页,你的账号登录信息依旧能保持,因为cookie文件在磁盘上,除非你直接删除,否则该文件会一直存在。但是cookie文件存储在内存(浏览器)中时,关闭了浏览器(注意不是关闭网页),浏览器的进程资源被释放,属于浏览器资源的cookie文件当然也被释放,此时再打开浏览器,登录信息就无法保持。如果只是把网站关闭,浏览器没关闭,再打开该网站,登录信息依旧是能保存的,因为cookie文件没有被释放。

但由于安全性的问题,将cookie文件存储在客户端的做法。因为客户端容易遭到攻击,或者说信息容易泄漏,如果cookie被非法窃取,cookie所有者的权益将会受到损害。所以现在主流的策略都是cookie+session,将cookie文件存储在服务端(Linux系统安全性极高)。其主要实现是:客户端提交登录信息,服务端接收后在后台数据库上将这些信息存储起来,然后生成一个唯一的id,这个id可以理解为key,数据库中存储的信息可以理解为value,这就是一对key:value模型。服务端将id返回给客户端,使客户端set-Cookie,将该id值保存起来,此后客户端的http请求都会带上该id值,服务端收到id后,找到其对应value,就能解析出客户的信息,也就能根据这些信息进行会话保持了。

但是cookie+session的方式同样是不安全的,id值同样可以被窃取,被非法利用。但是被窃取的只是一个id值,你的具体信息没有得到泄漏,具体信息存储在服务端的数据库中,要攻击这样的数据库还是比较难的。这也是相对直接使用cookie的优势吧,或者这么说,http协议就是不安全的,它的目的是通信的进行,侧重于通信的实现,至于安不安全就是另外一回事了,因此不推荐用http进行私密数据的传输,要进行这样的传输可以使用更侧重安全性的https协议

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

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

相关文章

STM32f103封装 led程序CubeMX

本文代码使用 HAL 库。 文章目录前言一、LED 原理图二、CubeMX创建工程三、LED 相关函数1. 输出电平函数&#xff1a;2. 延时函数&#xff1a;3. 翻转电平函数&#xff1a;四、详细代码实验现象 &#xff1a;总结代码 源码&#xff1a;前言 从这篇文章开始&#xff0c;我们讲解…

关于Kubernetes不兼容Docker

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/129153459 参考文献&#xff1a;https://www.cnblogs.com/1234roro/p/16892031.html 一、总结 总结起来就是一句话&#xff1a; k8s只是弃用了dockershim&#xff0c;并不是弃用了整个Docker&#xf…

JS代码可以重复进行混淆加密吗?

JS代码可以重复进行混淆加密吗&#xff1f;问题 同一段JS代码&#xff0c;是否可以反复、重复进行混淆加密&#xff1f; 本文&#xff0c;用实验给出答案。 实验过程 准备一段代码&#xff0c;如下&#xff1a; 运行&#xff0c;可以显示出代码执行耗时&#xff1a; 可以看…

华为OD机试 - 上班之路(Python)【2023-Q1 新题】

华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 上班之路 题目描…

打不过就拉拢!ChatGPT和MidJourney已经成我小秘书!太爽了

大家好&#xff0c;我是晓衡。这两周&#xff0c;我战斗力爆棚了&#xff01;每天大概睡了四~五个小时&#xff0c;而且中午也没有休息过&#xff0c;但精神却还很亢奋。直到周一下午&#xff0c;身体才有种被掏空的感觉&#xff0c;晚上 10 点就睡了。可能是兴奋劲还在&#x…

Python 刚学习就放弃的原因?

很大一部分小伙伴根本不清楚自己学的知识重点是什么&#xff0c;今天咱们就来整理一下python自学容易混淆的知识点【收藏有用】 Python 2 和 Python 3 的区别&#xff1a; Python 2 和 Python 3 有一些不兼容的语法和库&#xff0c;例如 Python 3 默认使用 Unicode 编码&#…

MySql_基础篇从下载到DDL语法

本笔记基于b站up主黑马程序员的视频整理 用于记录与回顾 数据库相关概念 数据库 DS 存储数据的仓库 数据库管理系统 DSMS 操纵和管理数据库的大型软件 sql 操作关系型数据库的编程语言是一套标准 mysql 默认端口号是3306 下载数据库 //下载部分建议转移到b站 搜索“MyS…

深度学习实战16(进阶版)-虚拟截图识别文字-可以做纸质合同和表格识别

大家好&#xff0c;我是微学AI&#xff0c;今天给大家带来一个关于虚拟截图识别文字的应用&#xff0c;可以运用在多个领域。 案例主要结合Mediapipe手势识别模型&#xff0c;识别出手势的21个关键点的坐标&#xff0c;计算机的摄像头根据食指的坐标信息获取用户想要截取的图片…

面试加分项:JVM 锁优化和逃逸分析详解

1 锁优化JVM 在加锁的过程中&#xff0c;会采用自旋、自适应、锁消除、锁粗化等优化手段来提升代码执行效率。1.1 自旋锁和自适应自旋现在大多的处理器都是多核处理器 &#xff0c;如果在多核心处理器&#xff0c;有让两个或者以上的线程并行执行&#xff0c;我们可以让一个等待…

JavaScript前端中的伪类元素before和after使用详解

在前端开发中&#xff0c;伪类是一种让你可以选择元素的某个状态或位置的 CSS 选择器。其中&#xff0c;:before 和 :after 伪类允许你在一个元素之前或之后插入内容。 :before 和 :after 伪类创建的元素是不在 HTML 文档中的&#xff0c;它们是通过 CSS 生成的。可以用它们来…

PMP考试详解,新考纲有什么变化?

一&#xff0c;为什么优先考虑PMP持证人员&#xff1f; PMP证书在我国大型企业、跨国企业、央企/国企等单位的招聘中都比较重视&#xff0c;特别是在许多项目投标环节中&#xff0c;明确标明需要有PMP持证人员&#xff0c;才能在投标、竞标中代表公司有资格承担项目。 除此之…

Unity中的Mathf数学运算讲解(值得收藏)

Unity中的Mathf数学运算有哪些&#xff1f; Mathf.Abs(f)绝对值 计算并返回指定参数 f 绝对值 例如&#xff1a; // 输出 10 Debug.log(Mathf.Abs(-10)) Debug.log(Mathf.Abs(10))Mathf.Sin正弦 static function Sin (f : float) : float 计算并返回以弧度为单位指定的角 f 的…

分布式架构之详解幂等

幂等的概念 在数学里&#xff0c;幂等有两种主要的定义。 1、在某二元运算下&#xff0c;幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素。例如&#xff0c;乘法下仅有两个幂等实数&#xff0c;为0和1。 2、某一元运算为幂等的时&#xff0c;其作用在…

醒醒吧,外包测试哪有前途,你只是一块干电池而已,随时会被替换掉

我25岁的时候&#xff0c;外包测试&#xff0c;薪资13.5k&#xff0c;人在深圳。 内卷什么的就不说了&#xff0c;而且人在外包那些高级精英年薪大几十的咱也接触不到&#xff0c;就说说外包吧。假设以我为界限&#xff0c;25岁一线城市13.5k&#xff0c;那22-24大部分情况下是…

【项目】Vue3+TS 退出登录 menu header搭建

&#x1f4ad;&#x1f4ad; ✨&#xff1a;【项目】Vue3TS 退出登录 menu header搭建   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;: 今天永远比昨天更好&#x1f49c;&#x1f49c;   &#x1f338;: 如有错误或不足之处&#xff0c;希望可以指正&#x…

Cesium集成WebXR_连接VR设备

Cesium集成WebXR 文章目录Cesium集成WebXR1. 需求2. 技术基础2.1 WebGL2.2 WebXR2.3 其他3. 示例代码4. 效果图5. 参考链接1. 需求 通过WebXR接口&#xff0c;将浏览器端连接到VR头盔&#xff0c;实现在VR头盔中浏览Cesium场景&#xff0c;并可将头盔旋转的操作同步映射为场景…

干测试5年,经常被开发看不起,现在总算证明了自己····

测试不止是点点点 我感觉我是一个比较有发言权的人吧&#xff0c;我在测试行业摸爬滚打5年&#xff0c;以前经常听到开发对我说&#xff0c;天天的点点点有意思没&#xff1f; 和IT圈外的同学、朋友聊起自己的工作&#xff0c;往往一说自己是测试&#xff0c;无形中也会被大家…

CVE-2022-39197 POC(CobaltStrike XSS <=4.7)漏洞复现

漏洞说明 根据9.20日CobaltStrike官方发布的最新4.7.1版本的更新日志中介绍&#xff0c;<4.7的teamserver版本存在XSS漏洞&#xff0c;从而可以造成RCE远程代码执行 一位名为“Beichendream”的独立研究人员联系我们&#xff0c;告知我们他们在团队服务器中发现的一个 XSS …

ABBYY FineReader16最新PDF图片文字识别软件

ABBYY FineReader16是非常好的一款 OCR 识别软件&#xff08;可以识别不可编辑的PDF和图片文件&#xff09;&#xff0c;操作非常简单。ABBYY FineReader 16是一款知名的OCR文字识别软件&#xff08;图片文字识别&#xff09;。ABBYY 16采用了ABBYY最新推出的基于AI的OCR技术&a…

JVM记录

一、JVM体系结构&#xff1a; 类装载器ClassLoader&#xff1a;用来装载.class文件执行引擎&#xff1a;执行字节码&#xff0c;或者执行本地方法运行时数据区&#xff1a;方法区、堆、Java栈、程序计数器、本地方法栈1、方法区&#xff1a; 也称“永久代”&#xff0c;“非堆”…