应用层协议——HTTP协议

1. 认识HTTP协议

HTTP(Hyper Text Transfer Protocol)协议又叫做超文本传输协议,是一个简单的请求-响应协议,HTTP通常运行在TCP之上。

超文本的意思就是超越普通的文本,http允许传送文字,图片,视频,音频等,协议我们前面也说过,就是一种约定。

HTTP协议在应用层,主要是解决如何处理对端发送过来的数据,关于对端数据如何发送过来,丢包了怎么办等问题,不是应用层要考虑的,在传输层,网络层,数据链路层会帮我们解决这些问题,他只关心如何处理对端数据。

所以站在应用层的角度来看,就是双方的应用层之间直接进行通信

2. 认识URL

URL叫做统一资源定位符, 平时我们俗称的 "网址" 其实就是说的 URL。一个URL通常由以下几个部分构成

  • 协议方案名:最常见的就是http和https
  • 登录信息:包含用户名和密码,但是一般不会在URL中体现出来
  • 服务器地址:其实就是IP地址,只不过是被DNS服务解释成了域名
  • 服务器端口号:一般不用在URL中体现出来,因为当我们使用某种协议时,该协议实际就是在为我们提供服务,现在这些常用的服务与端口号之间的对应关系都是明确的,所以我们在使用某种协议时实际是不需要指明该协议对应的端口号的,因此在URL当中,服务器的端口号一般也是被省略的。
  • 带层次的文件路径:我们需要访问的资源在服务器的哪个位置例如上面的/dir/index.htm意思就是,我们要访问/dir/index目录下的.htm文件。而 /dir 并不是从系统根目录开始的,而是从web根目录开始(服务器会自动在为 /dir 添加文件路径)
  • 查询字符串:就是我们要给服务器传递的参数中间以&分隔。
  • 片段标识符:对资源部分的补充

举个例子:我们使用百度搜索http

显示的URL

我们对其划分

https就是协议方案名,服务器地址是www.baidu.com,文件路径是/s,后面的就是查询字符串了,也就是要传递给服务器的参数,我们仔细观察会发现查询字符串中有一个wd=http,其实就是我们刚才要搜索的内容

2.1 urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.比如, 我们使用百度搜索"?fsda*"

显示的wd我们会发现,除了?被转化成了%3F之外,其他字符都能成功显示出来。

转义的规则如下:

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

urlencode就是将特殊字符进行转义(编码),而urldecode就是将转义后的特殊字符转化回去。(解码)。

3. HTTP协议格式

HTTP是应用层协议,主要是分析对端传来的数据进行分析,得出你想要的资源,并且返回给你。例如上面的例子,我们使用百度搜索http的过程,百度的服务器在应用层分析出我们想要搜索的内容是http,服务器经过比较复杂的一系列操作之后,将结果再返回给我们。这种模式我们可以称为cs模型,c就是客户端,通常指的是我们client用户(使用者),s就是server服务端(提供服务),也可以是bs模型,因为用户一般都是浏览器(通过浏览器访问目标资源)。

HTTP通过是基于TCP的,而TCP是面向字节流的,在传输数据过程中,所有数据都是粘在一起的,无法区分报文与报文之间的间隔,我们要通过协议的方式解决这个问题,而HTTP就是已经制定好,比价h成熟的协议了。所以我们也要学习一下,给服务端发送请求报文的格式与收到服务端响应报文的格式。

3.1 HTTP请求报文格式

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

下面是几种比较常见的请求方法:

其中最重要的两个就是GET和POST方法。

注意,这里的url是web根目录,并不是Linux系统根目录,可以是机器上任何一个目录,由我们自己指定。例如我将来将所有的资源都保存在wwwroot目录下。我们在写服务端的时候让默认路径

std::string defaultpath = "./wwwroot";

浏览器给我们发送的url被提取之后,我们直接在url前面添加defaultpath。例如我们默认访问的是 / ,在添加之后就会变成

std::string url = "./wwwroot/";

我们后续可以进行处理,每个目录下我们都设置一个index.html的文件作为这个目录的首页,当我们查看url,发现他是一个目录的时候就让他默认访问这个目录下的index.html文件。

所以我们的url是 / 但是最终访问的资源其实是

std::string url = "./wwwroot/index.html";

这就是web根目录的作用,可以在指定目录下进行资源查找。

3.2 HTTP响应报文格式

  • 状态行:[http版本]+[状态码]+[状态码描述]
  • 响应报头:请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • 空行:遇到空行表示响应报头结束。
  • 响应正文:响应正文允许为空字符串,如果响应正文存在,则响应报头中会有一个Content-Length属性来标识响应正文的长度。

状态码和状态码描述:用于描述请求的结果

常见的状态码以及状态码描述:

  • [200 OK 成功]     请求正常被服务器处理
  • [204 Not Content]      请求处理成功但是没有资源可返回,相应报文中不含实体的主体部分,另外也不允许返回任何实体,浏览器返回的页面不发生更新一般在只需要客户端给服务端发消息,而对客户端不需要发送新内容的情况下使用
  • [206 Partial Content]     表示客户端进行了范围请求,客户端只要某一部分的信息,服务器成功执行了这部分的GET请求相应报文中包含Content-Range指定范围的实体内容
  • [301 Moved Permanently]     永久性重定向,资源移动会更新浏览器书签, 相应状态码返回时,所有浏览器都会把POST改成GET,并删除请求报文主体,之后请求会自动再次发送
  • [302 Found]     临时性重定向,资源移动不会更新浏览器书签,相应状态码返回时,所有浏览器都会把POST改成GET,并删除请求报文主体,之后请求会自动再次发送
  • [303 See Other]     资源的URI已经更新,临时按新的URI进行访问,使用GET请求获取相应的资源,相应状态码返回时,所有浏览器都会把POST改成GET,并删除请求报文主体,之后请求会自动再次发送
  • [304 Not Modified]    自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。如果网页自请求者上次请求后再也没有更改过,您应将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。服务器可以告诉 Googlebot 自从上次抓取后网页没有变更,进而节省带宽和开销。返回时不包含任何请求的主体部分,304和重定向无任何关系
  • [307 Temporary Redirect]     临时重定向,和302有相同的含义
  • [400 Bad Request]     请求报文中存在语法错误,服务端无法理解,需修改请求的内容后再次发送请求,浏览器会像对待200一样对待该状态码
  • [401 Unauthorized]     发送的请求需要有通过HTTP认证的认证信息。另外若之前已经已经进行过一次请求,则表示用户认证失败, 当浏览器初次接收到401响应,会弹出认证用的对话窗口
  • [403 Forbidden]    服务器拒绝请求该资源, 或者表示未获取文件系统的访问授权,访问权限出现某些问题, 服务器端可以说明拒绝的理由,并返回给客户端展示
  • [404 Not Found]     表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
  • [500 Interval Server Error]     服务器在执行请求时发生故障, 也可能是web应用存在的bug或者某些临时故障
  • [503 Service Unavailable]     服务器目前超负载,正忙着呢,正在进行停机维护,现在无法处理请求

在HTTP请求报文和响应报文中都包含了版本这一字段,为什么要交互版本呢

请求报文发送的是客户端的http版本,响应报文发送的是服务端的版本,主要是为了能让不同版本的客户端都能享受到服务。客户端告诉服务端自己的版本,服务端就可以为客户端提供适合他的版本的服务。例如微信最初的版本是没有微信支付的功能的,如果我们一直使用的是最初版本不更新的话,那么服务端就不会给我们提供微信支付功能,只有把微信更新到一定版本才会支持。

3.3 报文中的常见字段

  • Content-Type:数据类型(text/html等)。
  • Content-Length:正文的长度。
  • Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
  • Connection:字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。
  • User-Agent:声明用户的操作系统和浏览器的版本信息。
  • Referer:当前页面是哪个页面跳转过来的。
  • Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
  • Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能。
  • 1.Host字段:用于指明我们要访问资源的位置。

Host: www.A.com
  • 2.Content-Length:正文的长度
Content-Length: 100
  • 3.Content-Type:请求资源的类型,例如我们要请求一张网页就是html。
Content-Type: text/html
  • 4.Connection:用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。

http/1.0采用的是短链接的方式,即浏览器(客服端)发起请求,建立连接,服务器构建响应之后立刻就会断开连接。但是对于一个网页,包含的元素较多则需要建立多次连接,频繁的建立连接断开连接的资源消耗比较大,TCP需要经历3次握手,4次挥手的过程。

http/1.1采用长连接,即建立连接后不再断开,客户端一次可以发多个请求。当连接双方超过一定时间没有数据交互时,就会自动断开连接。如果Connection字段是keep-alive就代表是长连接。

Connection: keep-alive
  • 5.User-Agent:声明用户的操作系统和浏览器的版本信息。
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0

当我们想下载一个软件时,默认都会下载适合我们电脑型号的版本,就是因为我们会发送我们电脑的操作系统版本信息。

  • 6.Cookie和Session

HTTP 是一种不保存状态, 即无状态(stateless) 协议。 HTTP 协议自身不对请求和响应之间的通信状态进行保存。 也就是说在 HTTP 这个级别, 协议对于发送过的请求或响应都不做持久化处理。

但是我们今天登录csdn后,把浏览器关闭,甚至关机重启后,我们会发现我们的账号还是登录上的,并没有要求我们再次输入密码,这就是Cookie技术。

我们可以在对应网站查看 cookie,如果把Cookie删除,下次登录就需要重新输入账号密码了。关于Cookie的内容后面会详细说,先简单了解一下。

3.4 如何将报头和有效载荷分离

http协议中,每一行的内容都以 \r\n 结尾,所以我们可以读取到 \n 说明我们读取到了这一行。我们可以使用按行读的方式,如果读到了某一行是空行,说明我们读到了一个完整报文,剩下的内容,直到读到 \n 都是正文内容,也可以通过读取报头中的Content-Length字段提取正文的大小进行读取。

4. HTTP请求与响应

4.1 获取浏览器的请求报头

我们可以使用代码来获取浏览器的请求报头看一下。

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main()
{//创建套接字int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0){std::cerr << "create socket error!" << std::endl;return 1;}std::cout << "create socket success" << std::endl;//绑定struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(8080);local.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cerr << "bind error!" << std::endl;return 2;}std::cout << "bind success" << std::endl;//监听if (listen(listen_sock, 10) < 0){std::cerr << "listen error!" << std::endl;return 3;}std::cout << "listen success" << std::endl;//启动服务器struct sockaddr peer;memset(&peer, 0, sizeof(peer));socklen_t len = sizeof(peer);while (true){int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){std::cerr << "accept error!" << std::endl;continue;}if (fork() == 0){close(listen_sock);if (fork() > 0){exit(0);}char buffer[1024];recv(sock, buffer, sizeof(buffer), 0); //读取HTTP请求std::cout << "--------------------------http request begin--------------------------" << std::endl;std::cout << buffer << std::endl;std::cout << "---------------------------http request end---------------------------" << std::endl;close(sock);exit(0);}close(sock);waitpid(-1, nullptr, 0);}return 0;
}

GET / HTTP/1.1
Host: 123.60.181.162:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

这里可以看到我们我们收到的报文就是这个格式,其实GET就是请求方法, / 是请求的url,http/1.1是版本。值得一提的是, / 指的是web根目录,并不是Linux系统根目录,可以是机器上任何一个目录,由我们自己指定。

请求报头中的字段都是 KV格式,例如Host表示被请求资源方的IP和端口,其中Host就是属性名,后面的才是真正的IP和端口。而这个请求报文中并没有出现Content-Length属性,说明没有正文部分的内容。

4.2 给浏览器发送响应报头

在收到浏览器的请求之后,我们可以构建一个报头给浏览器响应回去。

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main()
{// 创建套接字int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0){std::cerr << "create socket error!" << std::endl;return 1;}std::cout << "create socket success" << std::endl;// 绑定struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(8080);local.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0){std::cerr << "bind error!" << std::endl;return 2;}std::cout << "bind success" << std::endl;// 监听if (listen(listen_sock, 10) < 0){std::cerr << "listen error!" << std::endl;return 3;}std::cout << "listen success" << std::endl;// 启动服务器struct sockaddr peer;memset(&peer, 0, sizeof(peer));socklen_t len = sizeof(peer);while (true){int sock = accept(listen_sock, (struct sockaddr *)&peer, &len);if (sock < 0){std::cerr << "accept error!" << std::endl;continue;}if (fork() == 0){close(listen_sock);if (fork() > 0){exit(0);}char buffer[1024];recv(sock, buffer, sizeof(buffer), 0); // 读取HTTP请求std::cout << "--------------------------http request begin--------------------------" << std::endl;std::cout << buffer << std::endl;std::cout << "---------------------------http request end---------------------------" << std::endl;// 读取index.html文件std::ifstream in("./index.html");if (!in.is_open()){std::cerr << "open file error " << std::endl;}std::string line;std::string file;while (std::getline(in, line)){file += line;}// 构建HTTP响应std::string status_line = "http/1.1 200 OK\n";								 // 状态行std::string response_header = "Content-Length: " + std::to_string(file.size()) + "\n";	 // 响应报头std::string blank = "\n";													 // 空行std::string response_text = file;											 // 响应正文std::string response = status_line + response_header + blank + response_text; // 响应报文// 响应HTTP请求send(sock, response.c_str(), response.size(), 0);close(sock);exit(0);}close(sock);waitpid(-1, nullptr, 0);}return 0;
}

代码的逻辑很简单,只是在接受到浏览器的请求报头之后,打开当前目录下的index.html文件。index.html文件内容也很简单

我们来测试一下最终的效果。

服务器启动,浏览器访问

请求报文如图所示,因为是长连接,所以有多个http请求。客户端请求后,服务端会构建http响应。

最终也是成功收到了我们想要的html资源。

4.3 使用telnet

我们可以使用telnet命令进行抓包,给服务器发送请求,并接受服务器的响应

可以看到我们现在给服务器响应的内容只有请求行和一个空行。

按下回车之后就可以看到响应报头了,包括状态行,响应报头以及正文部分,正文部分对应的内容其实就是html网页,浏览器会自动帮我们进行渲染

我们打开开发者模式就可以看到,这段html代码就是服务端的响应正文。

5. GET和POST方法

前面说过请求方法中最重要的两种方法就是GET和POST。

首先我们要认识,上网行为无非就是两种:

1.从网络中获取数据。

2.将数据上传到网络上

而将数据上传到网络我们通常使用的是GET获取POST方法,而从网络中获取数据一般使用的是GET方法。

两者的主要区别是

  • GET方法是通过url传参的。
  • POST方法是通过正文传参的。

我们先了解一下html中的表单。

我们先简单写一个表单的代码

<html><head></head><body><h1>Hello World</h1><form action="/a/index.html" method="GET">user:<br><input type="text" name="xname"><br>password:<br><input type="password" name="ypwd"><br></br><input type="submit" value="Log in"></form></body>
</html>

最终效果:

我们可以看到表单form中有一个属性method就是GET,说明此时我们使用的是GET方法。GET方法使用url传参。

当我们使用我们在表单中输入了账号和密码之后,点击Log in登录,

我们会发现url中果然有一个xname=zhangsan和ypwd=12345,这都是我们刚才输入的值。

所以服务端自然也可以根据url提取出账号和密码了。

如果是POST方法呢,POST方法通过正文传参

首先url中没有数据了。

我们可以看到在最后的正文部分果然是有账号和密码的。

所以我们可以知道,GET和POST的区别在于传参的位置不同,GET方法通过url传参,POST方法通过正文传参

那么问题来了,GET方法很容易被看到,POST方法不容易被看到,那么POST方法是不是比GET方法更安全?

其实无论是GET方法还是POST方法都是不安全的。POST方法虽然不容易被看到,但是不排除会被抓包,数据都是明文的,还是不安全,只能说POST方法比GET方法更加私密。安全这方面要看加密和解密。

6. Cookie和Session

HTTP 是无状态协议, 它不对之前发生过的请求和响应的状态进行管理。

那么现在会出现一个问题。假设我们要登录某视频网站观看VIP用户才能看的电影《战狼》

此时已经认证成功,接下来应该是登录此账号然后观看,但是因为跳转到登录页面,并且是在登录页面输入的账号密码,返回到主页面时并不会保存你的账号密码,因为http是无状态的。这样用户就永远无法成功登录账号。

由此,引入了Cookie技术,Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息, 通知客户端保存 Cookie。 当下次客户端再往该服务器发送请求时, 客户端会自动在请求报文中加入 Cookie 值后发送出去。服务器端发现客户端发送过来的 Cookie 后, 会去检查究竟是从哪一个客户端发来的连接请求, 然后对比服务器上的记录, 最后得到之前的状态信息

加入了Cookie之后,上面的例子变成了:

在第一次收到http响应时会保存一个Cookie文件,此后访问服务端时,都会默认加上Cookie信息。

内存级别 / 文件级别

Cookie文件分为内存级别和文件级别,内存级别顾名思义就是保存在内存当中,当我们关机重启之后就不存在了,此时我们需要重新登录,而文件级别就是写入磁盘当中,关机重启之后依然存在

Cookie被盗问题

在有了以上认识后,我们会想到,如果别人盗取了我们的cookie文件怎么办,他就知道了我们的账号和密码了。

此时就可以引入SessID的概念了。

服务端在收到用户名和密码后,会用用户名和密码生成一个独一无二的SessionID,并把SessionID和用户名密码关联起来,然后将SessionID返回给用户,用户将来访问时只使用SessionID。

这种方法无法解决Cookie被盗的问题,但是相对来说比较安全,用户的用户名和密码无法被盗取。

我们可以做一个测试:

std::string status_line = "http/1.1 200 OK\n";								 // 状态行
std::string response_header = "Content-Length: " + std::to_string(file.size()) + "\n";	 // 响应报头
response_header += "Set-Cookie: name=zhangsan";
response_header += "Set-Cookie: possword=123456";
std::string blank = "\n";													 // 空行
std::string response_text = file;											 // 响应正文
std::string response = status_line + response_header + blank + response_text; // 响应报文

构建的响应报头如上所示。

可以看到我们的网站的Cookie文件当中果然存在了用户名和密码。

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

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

相关文章

XAMPP是什么?XAMPP好不好用?

XAMPP是一个免费且开源的软件套件&#xff0c;用于在个人计算机上轻松搭建和运行 Apache 服务器、MySQL 数据库、PHP 和 Perl&#xff0c;让用户可以在个人电脑上搭建服务器环境的平台。 XAMPP的由来是 X(表示跨平台)、Apache、MySQL、PHP 和 Perl 的首字母缩写。 它集成了这…

Autosar NvM配置-手动配置Nvblock及使用-基于ETAS软件

文章目录 前言NvDataInterfaceNvBlockNvM配置SWC配置RTE Mapping使用生成的接口操作NVM总结前言 NVM作为存储协议栈中最顶层的模块,是必须要掌握的。目前项目基本使用MCU带的Dflash模块,使用Fee模拟eeprom。在项目前期阶段,应该充分讨论需要存储的内容,包括应用数据,诊断…

《Fundamentals of Power Electronics》——隔离型CUK转换器、

以下是隔离型CUK转换器的相关知识点&#xff1a; Cuk电路的隔离型版本获得方式不同。基础非隔离型Cuk电路如下图所示。 将上图中电容C1分成两个串联的电容C1a和C1b&#xff0c;得到结果如下图所示。 在两个电容之间插入一个变压器&#xff0c;得到如下图所示电路。 变压器极性…

再议大模型微调之Zero策略

1. 引言 尽管关于使用Deepspeed的Zero策略的博客已经满天飞了&#xff0c;特别是有许多经典的结论都已经阐述了&#xff0c;今天仍然被问到说&#xff0c;如果我只有4块40G的A100&#xff0c;能否进行全量的7B的大模型微调呢&#xff1f; 正所谓“纸上得来终觉浅&#xff0c;…

C#知识|将选中的账号信息展示到控制台(小示例)

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 上篇学习了控件事件的统一关联&#xff0c; 本篇通过实例练习继续学习事件统一处理中Tag数据获取、对象的封装及泛型集合List的综合运用。 01 实现功能 在上篇的基础上实现&#xff0c;点击选中喜欢的账号&#xff0…

Day1| Java基础 | 1 面向对象特性

Day1 | Java基础 | 1 面向对象特性 基础补充版Java中的开闭原则面向对象继承实现继承this和super关键字修饰符Object类和转型子父类初始化顺序 多态一个简单应用在构造方法中调用多态方法多态与向下转型 问题回答版面向对象面向对象的三大特性是什么&#xff1f;多态特性你是怎…

Transformer详解:从放弃到入门(三)

上篇文章中我们了解了多头注意力和位置编码&#xff0c;本文我们继续了解Transformer中剩下的其他组件。 层归一化 层归一化想要解决一个问题&#xff0c;这个问题在Batch Normalization的论文中有详细的描述&#xff0c;即深层网络中内部结点在训练过程中分布的变化问题。  …

秘籍解锁 primegaming亚马逊免费游戏领取+下载安装教程秘籍解锁

秘籍解锁&#xff01;primegaming亚马逊免费游戏领取下载安装教程秘籍解锁&#xff01; 亚马逊作为几大游戏平台之一也是常常送出各种免费以供玩家们游玩&#xff0c;就在近日&#xff0c;亚马逊平台优势豪掷千金为玩家们送出了两款大作&#xff0c;分别是古墓丽影年度版与乐高…

《设计一款蓝牙热敏打印机》

主控芯片用易兆威蓝牙ic&#xff0c;通讯接口&#xff1a;蓝牙、串口、usb 安卓apk用java kotlin编写、上位机用Qt编写。

PX4二次开发快速入门(三):自定义串口驱动

文章目录 前言 前言 软件&#xff1a;PX4 1.14.0稳定版 硬件&#xff1a;纳雷NRA12&#xff0c;pixhawk4 仿照原生固件tfmini的驱动进行编写 源码地址&#xff1a; https://gitee.com/Mbot_admin/px4-1.14.0-csdn 修改 src/drivers/distance_sensor/CMakeLists.txt 添加 add…

uniapp 监听APP切换前台、后台插件 Ba-Lifecycle

监听APP切换前台、后台 Ba-Lifecycle 简介&#xff08;下载地址&#xff09; Ba-Lifecycle 是一款uniapp监听APP切换前台、后台的插件&#xff0c;简单易用。 截图展示 也可关注博客&#xff0c;实时更新最新插件&#xff1a; uniapp 常用原生插件大全 使用方法 在 script…

Python实现打砖块游戏

提供学习或者毕业设计使用&#xff0c;功能基本都有&#xff0c;不能和市场上正式游戏相提比论&#xff0c;请理性对待&#xff01; 在本文中&#xff0c;我们将使用 Pygame 和 Tkinter 创建一个简单的打砖块游戏。游戏的目标是通过控制挡板来击碎屏幕上的砖块&#xff0c;同时…

Mac虚拟机软件哪个好用 mac虚拟机parallels desktop有什么用 Mac装虚拟机的利与弊 mac装虚拟机对电脑有损害吗

随着多系统使用需求的升温&#xff0c;虚拟机的使用也变得越来越普遍。虚拟机可以用于创建各种不同的系统&#xff0c;并按照要求设定所需的系统环境。另外&#xff0c;虚拟机在Mac电脑的跨系统使用以及测试软件系统兼容性等领域应用也越来越广泛。 一、Mac系统和虚拟机的区别 …

【Pytorch】6.torch.nn.functional.conv2d的使用

阅读之前应该先了解基础的CNN网络的逻辑 conv2d的作用 是PyTorch中用于执行二维卷积操作的函数。它的作用是对输入数据进行二维卷积操作&#xff0c;通常用于图像处理和深度学习中的卷积神经网络&#xff08;CNN&#xff09;模型。 conv2d的使用 我们先查看一下官方文档 inpu…

LibTorch入坑记--续2

一、安装faiss 我的faiss&#xff0c;用的是曾经安装过的 pip install faiss-gpu1.7 当时搞得环境名称是pni 二、配置环境 三、例子代码 #include <faiss/IndexFlat.h> #include <faiss/Index.h> #include <faiss/VectorTransform.h> #include <faiss/…

【Linux】Docker 安装部署 Nacos

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【Linux】Docker 安装部署 Nacos docker搜索na…

改进猫群算法丨多车场多车型路径问题求解复现

车间调度系列文章&#xff1a; 1、路径优化历史文章2、路径优化丨带时间窗和载重约束的CVRPTW问题-改进遗传算法&#xff1a;算例RC1083、路径优化丨带时间窗和载重约束的CVRPTW问题-改进和声搜索算法&#xff1a;算例RC1084、路径优化丨复现论文-网约拼车出行的乘客车辆匹配及…

太阳能光伏在生活中的三大作用

随着现在太阳能光伏的逐步发展&#xff0c;太阳能光伏已经越来越走近人们的生活&#xff0c;小编带大家盘点一下光伏在生活中的应用 一、发电 光伏的最初应用就是用来发电&#xff0c;以替代传统的化石燃料发电方式。光伏发电可以从根本上解决当今社会面临的能源短缺问题&…

嵌入式5-7

练习&#xff1a;优化登录框&#xff0c;输入完用户名和密码后&#xff0c;点击登录&#xff0c;判断账户是否为 Admin 密码 为123456&#xff0c;如果判断成功&#xff0c;则输出登录成功&#xff0c;并关闭整个登录界面&#xff0c;如果登录失败&#xff0c;则提示登录失败&a…

信创 | 信创产业数字化转型与升级:路径规划与实践!

信创产业的数字化转型与升级路径&#xff0c;主要围绕着构建国产化信息技术软硬件底层架构体系和全周期生态体系&#xff0c;解决核心技术关键环节“卡脖子”的问题&#xff0c;以推动中国经济数字化转型的平稳健康发展。 一、信创产业的发展趋势包括&#xff1a; 加强国产信息…