当前位置: 首页 > article >正文

【Linux网络编程】应用层协议HTTP(实现一个简单的http服务)

目录

前言

一,HTTP协议

1,认识URL

2,urlencode和urldecode

3,HTTP协议请求与响应格式

 二,myhttp服务器端代码的编写

HTTP请求报文示例

HTTP应答报文示例

代码编写 

网络通信模块 

处理请求和发送应答模块

结果展示

完整代码

main.cc 文件

http.hpp文件

makefile

相关测试网页(html形式)


前言

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

在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或传输超文本(如HTML)。

一,HTTP协议

HTTP协议是客户端与服务器之间通信的基础。客户端通过HTTP协议向服务器发送请求,服务器收到请求后处理并返回响应。

1,认识URL

URL是Uniform Resource Location的缩写,译为“统一资源定位符”。

我们平时所说的网址,就是URL,例如:

  • 开始部分https:表示我们获取资源采用的协议,这里的https起始是对http协议的一种加密,这里我们看作是http。
  • news.qq.com:这一部分表示域名,通过域名 可以找到要访问服务器的IP地址。如何找到呢?

  • 域名服务器是网络中的基础设施建设,内部保存着域名和对应的IP地址,当时使用浏览器访问百度时,浏览器内部一般内置了域名服务器的IP地址,比如8.8.8.8。通过域名服务器获取到IP地址,这个过程叫做DNS。最后进行对目标服务器的访问。
  • 但是,要访问目标服务器,需要知道IP地址+端口号,IP地址可以通过域名 获取到,但是端口号呢?其实,对于这些成熟的协议,端口号是固定的。https对应的端口号是443,http对应的端口号是80,ssh对应的端口号是22。
  • 而域名之后的剩余部分,/ch/tech:是我们要访问的资源路径。可以发现,其中"/",就是linux下的路径分割符,所以该部分就代表linux系统下的一个特定路径。
  • 而我们上网的行为分为两种,一个是从远端拿下来数据,另一个是将数据上传到远端,这其实就是IO。而我们想从远端拿下来数据时,就是获取资源,这些资源在哪呢?就在linux服务器内部,特定路径下的一个文件。
  • 通过这条URL,域名可以找到IP(具有唯一性),而路径,目标机器上特定 路径下的一个文件(也具有唯一性),所以通过URL可以定位到全网内特定的一个文件。

2,urlencode和urldecode

像?/:这样的字符,已经被当作特殊字符理解了。因此这些字符不能随意出现。如果出现了这些特殊意义的字符,需要客户端(一般是浏览器)对这些特殊字符进行编码(encode)。服务器自己需要进行解码decode。示例:

hello @??//  word编码后的结果是:

 解码后的结果是:

工具: UrlEncode编码/UrlDecode解码 - 站长工具

3,HTTP协议请求与响应格式

HTTP底层使用的是tcp协议。

HTTP请求(request)

首行:【方法】+【url】+【版本号】

Header:请求的属性,以冒号分割的键值对。每组属性之间使用\r\n分割,遇到空行表示Header结束。

DATA:空行后面的内容都是DATA。DATA允许为空。

HTTP响应格式(response),与请求格式类似。

 二,myhttp服务器端代码的编写

HTTP请求报文示例

  • 这里的uri是统一资源定位符,它的作用是,可以唯一的标识资源,并允许用户通过特定的协议与资源进行交互。而前面提到的url,是统一资源标识符,url是uri的一种形式。 
  • 在上面的内容中提到过,url统一资源标识符,也就是网址。它的域名之后的内容,其实是特定linux机器上的特定路径的一个文件,我们使用 url(网址)的时候,其实就是访问目标主机上特定路径下的一个文件。
  • 在这里,请求行中的uri,也代表要访问的路径 。
  • 需要注意的是,在uri中 ,"/"不是指linux下的根目录,而是web根目录。什么是web根目录?就是和当前项目在同一级的一个目录,其内部可能包含网址,图片,视频等等各种资源,所以我们实际访问的其实是是web根目录下的资源。

将整个请求看作是一个大的字符串,中间使用\r\n,或者使用一些空格,空行分割。

编写代码时的想法:

  • 为了表示这个大字符串,我们可以定义一个Request请求类来管理。
  • 类中的成员就包含请求行的三个属性,用三个字符串表示即可。中间部分是以键值对的形式,所以可以使用unordered_map来存储,还有一个空行,和正文部分,使用string即可。
  • 当我们的服务器端收到这个请求报文时,就需要对这个大字符串进行反序列化,填充类中的成员。也就是将这个大的字符串,转化为结构化数据。



HTTP应答报文示例

和请求报文结构类似。 

  • 同样我们定义一个response应答类,和request类似。从上图可以看出,其实正文部分,就是一个html,是我们要返回给客户端的一个网页。也就是客户端想要访问的资源。
  • 将来我们的response类中一定会包含一个string _text。表示正文部分。我们拖过客户端发来的请求报文,可以知道客户端想要访问是么资源,可以查看uri。如果我们将资源硬编码到代码中,那么就只可以访问一个文件。比如将html文件,当成一个大字符串,_text存储这个大字符串。那么我们在发送应答的时候,返回的就永远是这一个资源,所以不能将资源硬编码到代码中。
  • 我们可以根据客户端发来的请求,提取uri,找到要访问的资源。然后以打开该文件,再读取文件中的内容即可。
  • 最后发送 给客户端,需要我们将类中的成员序列化成一个大的字符串。也就是将结构化数据,转化为大的字符串。


代码编写 

  • 现在我们大概了解了HTTP协议的请求格式和应答格式。接下来使用浏览器作为客户端,发送请求,接受应答。我们自己编写一个myhttp服务器,对客户端发来的HTTP请求做解析,然后返回给客户端应答。
  • HTTP协议是基于tcp的。
  • 在这里使用多进程的方式,父进程不停的获取连接,子进程不断处理连接。

 首先是网络通信部分代码:

核心逻辑:

  • 服务端
  • 创建套接字 → 绑定地址 → 监听连接 → 接受请求 → 读取数据 → 回传数据。

网络通信模块 

const int gbacklog = 8;
int main(int argc, char *argv[])
{// 1,创建套接字int listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd < 0){std::cerr << "创建监听套接字失败" << std::endl;exit(1);}// 从命令行参数中获取端口号uint16_t port = std::stoi(argv[1]);// 填写sockaddr_in结构体,注意主机序列转化为网络字节序struct sockaddr_in addr;int addrlen = sizeof(addr);addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;// 2,绑定端口号和ip地址int n = bind(listenfd, (struct sockaddr *)&addr, sizeof(addr));if (n < 0){std::cerr << "绑定失败" << std::endl;exit(2);}// 3,开始监听int s = listen(listenfd, gbacklog);if (s < 0){std::cerr << "监听失败" << std::endl;exit(3);}// 4,获取连接,处理连接while (true){int sockfd = accept(listenfd, (struct sockaddr *)&addr, (socklen_t *)&addrlen);if (n < 0){std::cerr << "获取连接失败" << std::endl;continue; // 继续获取}// 创建子进程处理请求pid_t id = fork();if (id == 0){// 子进程// 关闭不需要的文件描述符close(listenfd);if (fork() > 0)exit(0); // 子进程退出// 孙子进程 处理请求handle_request(sockfd);// 孙子进程退出exit(0);}else if (id > 0){// 父进程// 关闭不需要的文件描述符close(sockfd);pid_t rid=::waitpid(id,nullptr,0);(void)rid;}else{std::cerr << "创建子进程失败" << std::endl;}}return 0;
}

至此实现了网络通信的功能。 通过回调函数处理客户端(浏览器)发送过来的请求。



 

处理请求和发送应答模块

  •  接下来就是子进程处理请求。
  • 现在实现requet类和response类,其中request需要实现反序列化,将大字符串变成一个结构化数据。而response需要实现序列化,将序列化数据转化为结构化数据。
  • 需要注意的是,我们在给客户端发送应答报文的时候,必须要发送状态行(也就是报文的第一行),它包含了HTTP版本,状态码和状态码描述,这些是必须返回给客户端的,而其他的内容 可以不发。

通过回调方法处理请求,发送应答

// 定义一个回调方法,处理请求
void handle_request(int sockfd)
{char buffer[BUFFER_SIZE];// 读取请求报文ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1,0);if (n > 0){buffer[n] = 0;//for debug//std::cout<<buffer<<std::endl;Request req;// 将读取到的字符串反序列为请求对象req.Deserilaze(buffer);// 构建应答报文Response resp;//获取客户端想要访问的资源文件resp.SetTargetFile(req.GetUri());//for debug//std::cout<<"##############################"<<std::endl;//std::cout<<req.GetUri()<<std::endl;//std::cout<<"##############################"<<std::endl;// 将目标文件内容填写到正问部分resp.SetText();resp._version = "HTTP/1.1";resp._code = 200; // successresp._desc = "OK";// 反序列化std::string resp_str = resp.Serilaze();// 发送应答报文send(sockfd, resp_str.c_str(), resp_str.size(),0);}
}

结果展示

之后通过浏览器访问我们的http服务,所获得的网页。

完整代码

main.cc 文件


// 服务器端
// 基于HTTP协议
#include "http.hpp"
#include <sys/wait.h>// 缓冲区大小
#define BUFFER_SIZE 4096// 定义一个回调方法,处理请求
void handle_request(int sockfd)
{char buffer[BUFFER_SIZE];// 读取请求报文ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1,0);if (n > 0){buffer[n] = 0;//for debug//std::cout<<buffer<<std::endl;Request req;// 将读取到的字符串反序列为请求对象req.Deserilaze(buffer);// 构建应答报文Response resp;//获取客户端想要访问的资源文件resp.SetTargetFile(req.GetUri());//for debug//std::cout<<"##############################"<<std::endl;//std::cout<<req.GetUri()<<std::endl;//std::cout<<"##############################"<<std::endl;// 将目标文件内容填写到正问部分resp.SetText();resp._version = "HTTP/1.1";resp._code = 200; // successresp._desc = "OK";// 反序列化std::string resp_str = resp.Serilaze();// 发送应答报文send(sockfd, resp_str.c_str(), resp_str.size(),0);}
}
const int gbacklog = 8;
int main(int argc, char *argv[])
{// 1,创建套接字int listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd < 0){std::cerr << "创建监听套接字失败" << std::endl;exit(1);}// 从命令行参数中获取端口号uint16_t port = std::stoi(argv[1]);// 填写sockaddr_in结构体,注意主机序列转化为网络字节序struct sockaddr_in addr;int addrlen = sizeof(addr);addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;// 2,绑定端口号和ip地址int n = bind(listenfd, (struct sockaddr *)&addr, sizeof(addr));if (n < 0){std::cerr << "绑定失败" << std::endl;exit(2);}// 3,开始监听int s = listen(listenfd, gbacklog);if (s < 0){std::cerr << "监听失败" << std::endl;exit(3);}// 4,获取连接,处理连接while (true){int sockfd = accept(listenfd, (struct sockaddr *)&addr, (socklen_t *)&addrlen);if (n < 0){std::cerr << "获取连接失败" << std::endl;continue; // 继续获取}// 创建子进程处理请求pid_t id = fork();if (id == 0){// 子进程// 关闭不需要的文件描述符close(listenfd);if (fork() > 0)exit(0); // 子进程退出// 孙子进程 处理请求handle_request(sockfd);// 孙子进程退出exit(0);}else if (id > 0){// 父进程// 关闭不需要的文件描述符close(sockfd);pid_t rid=::waitpid(id,nullptr,0);(void)rid;}else{std::cerr << "创建子进程失败" << std::endl;}}return 0;
}

http.hpp文件


#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <unordered_map>
#include <sstream>
#include <fstream>const std::string gspace = " ";
const std::string glinespace = "\r\n";
const std::string glinesep = ": ";
// web根目录
const std::string webroot = "./wwwroot";
// 默认访问的首页
const std::string homepage = "index.html";// http协议
// 包含请求和应答
// 请求
class Request
{
public:Request(){}~Request(){}// 反序列化接口bool Deserilaze(std::string bigstr){std::string reqline;// 读取第一行,第一行的末尾是"\r\n"// 所以在字符串中找到"\r\n"的位置,截取前面部分即可auto pos = bigstr.find(glinespace);if (pos == std::string::npos)return false; // 不包含完整的请求// 获取到第一行的内容reqline = bigstr.substr(0, pos);// 将第一行进行反序列化std::stringstream ss(reqline);ss >> _method >> _uri >> _version;if (_uri == "/") // 表示要访问的资源就是web根目录下的首页_uri = webroot + _uri + homepage;else_uri = webroot + _uri; // 表示要访问特定路径下的资源// 删除第一行bigstr.erase(0, pos + glinespace.size());return true;}std::string GetUri(){return _uri;}private:std::string _method;  // 请求方法std::string _uri;     // uristd::string _version; // http版本// 请求报头std::unordered_map<std::string, std::string> _headers;// 空行std::string _blankline;// 正文std::string _text;
};// 应答
class Response
{
public:Response():_blankline(glinespace){}~Response(){}// 序列化std::string Serilaze(){// 状态行std::string status_line = _version + gspace + std::to_string(_code) + gspace + _desc + glinespace;// 响应报头std::string resp_header;for (auto &header : _headers){std::string line = header.first + glinesep + header.second + glinespace;resp_header += line;}return status_line + resp_header + _blankline + _text;}// 设置想要访问的资源文件void SetTargetFile(const std::string file){_targetfile = file;}//将目标文件填写入正文部分void SetText(){std::ifstream in(_targetfile);if(!in.is_open()){return ;}std::string line;while(std::getline(in,line)){_text+=line;}in.close();}public:std::string _version; // http版本int _code;            // 退出码std::string _desc;    // 描述退出码的退出信息// 应答报头std::unordered_map<std::string, std::string> _headers;// 空行std::string _blankline;// 正文std::string _text;// 文件,用来填充正文std::string _targetfile;
};

makefile

myhttp:main.ccg++ -o $@ $^ -std=c++17
.PHONY:clean
clean:rm -f myhttp

相关测试网页(html形式)

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Default Home Page</title><style>body {font-family: Arial, sans-serif;margin: 0;padding: 0;background-color: #f4f4f4;color: #333;}header {background-color: #007bff;color: #fff;padding: 10px 20px;text-align: center;}nav {background-color: #343a40;padding: 10px 0;}nav a {color: #fff;text-decoration: none;padding: 10px 20px;display: inline-block;}nav a:hover {background-color: #5a6268;}.container {padding: 20px;}.welcome {text-align: center;margin-bottom: 20px;}.welcome h1 {margin: 0;}.content {background-color: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);}footer {background-color: #343a40;color: #fff;text-align: center;padding: 10px 0;position: fixed;width: 100%;bottom: 0;}</style>
</head>
<body><header><h1>Welcome to Our Website</h1></header><nav><a href="#">Home</a><a href="Login.html">Login</a> <!-- 跳转到登录页面 --><a href="Register.html">Register</a> <!-- 跳转到注册页面 --><a href="#">About</a><a href="#">Contact</a></nav><div class="container"><div class="welcome"><h1>Welcome to Our Default Home Page</h1><p>This is a simple default home page template.</p></div><div class="content"><h2>Introduction</h2><p>This is a basic HTML template for a default home page. It includes a header, navigation bar, a welcome section, and a content area. You can customize this template to suit your needs.</p></div></div><footer><p>&copy; 2025 Your Company Name. All rights reserved.</p></footer>
</body>
</html>

Login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Login Page</title><style>body {font-family: Arial, sans-serif;background-color: #f4f4f4;display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;}.login-container {background-color: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);width: 300px;text-align: center;}.login-container h2 {margin-bottom: 20px;}.form-group {margin-bottom: 15px;}.form-group label {display: block;margin-bottom: 5px;text-align: left;}.form-group input {width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 4px;}.form-group input[type="submit"] {background-color: #007bff;color: #fff;border: none;cursor: pointer;}.form-group input[type="submit"]:hover {background-color: #0056b3;}</style>
</head>
<body><div class="login-container"><h2>Login</h2><form action="/login" method="post"><div class="form-group"><label for="username">Username</label><input type="text" id="username" name="username" required></div><div class="form-group"><label for="password">Password</label><input type="password" id="password" name="password" required></div><div class="form-group"><input type="submit" value="Login"></div></form></div>
</body>
</html>

Register.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Register Page</title><style>body {font-family: Arial, sans-serif;background-color: #f4f4f4;display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;}.register-container {background-color: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);width: 350px;text-align: center;}.register-container h2 {margin-bottom: 20px;}.form-group {margin-bottom: 15px;}.form-group label {display: block;margin-bottom: 5px;text-align: left;}.form-group input {width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 4px;}.form-group input[type="submit"] {background-color: #28a745;color: #fff;border: none;cursor: pointer;}.form-group input[type="submit"]:hover {background-color: #218838;}</style>
</head>
<body><div class="register-container"><h2>Register</h2><form action="/register" method="post"><div class="form-group"><label for="username">Username</label><input type="text" id="username" name="username" required></div><div class="form-group"><label for="email">Email</label><input type="email" id="email" name="email" required></div><div class="form-group"><label for="password">Password</label><input type="password" id="password" name="password" required></div><div class="form-group"><label for="confirm-password">Confirm Password</label><input type="password" id="confirm-password" name="confirm-password" required></div><div class="form-group"><input type="submit" value="Register"></div></form></div>
</body>
</html>

相关文章:

【Linux网络编程】应用层协议HTTP(实现一个简单的http服务)

目录 前言 一&#xff0c;HTTP协议 1&#xff0c;认识URL 2&#xff0c;urlencode和urldecode 3&#xff0c;HTTP协议请求与响应格式 二&#xff0c;myhttp服务器端代码的编写 HTTP请求报文示例 HTTP应答报文示例 代码编写 网络通信模块 处理请求和发送应答模块 结…...

短视频+直播商城系统源码全解析:音视频流、商品组件逻辑剖析

时下&#xff0c;无论是依托私域流量运营的品牌方&#xff0c;还是追求用户粘性与转化率的内容创作者&#xff0c;搭建一套完整的短视频直播商城系统源码&#xff0c;已成为提升用户体验、增加商业变现能力的关键。本文将围绕三大核心模块——音视频流技术架构、商品组件设计、…...

STM32定时器---基本定时器

目录 一、定时器的概述 二、时基单元 三、基本定时器的的时序 &#xff08;1&#xff09;预分频器时序 &#xff08;2&#xff09;计数器时序 四、基本定时器的使用 一、定时器的概述 在没有定时器的时候&#xff0c;我们想要延时往往都是写一个Delay函数&#xff0c;里面…...

mysql快速在不同库中执行相同的sql

目录 背景 解决方案 方式一&#xff1a;利用变量拼接好sql&#xff0c;复制出来执行&#xff08;简单&#xff0c;推荐&#xff09; 方式二&#xff1a;使用存储过程和游标实现&#xff08;比较复杂&#xff0c;脚本需要拼接一个完整的&#xff0c;也比较麻烦&#xff0c;不…...

大模型微调 - transformer架构

什么是Transformer Transformer 架构是由 Vaswani 等人在 2017 年提出的一种深度学习模型架构&#xff0c;首次发表于论文《Attention is All You Need》中 Transformer 的结构 Transformer 编码器&#xff08;Encoder&#xff09; 解码器&#xff08;Decoder&#xff09; …...

【器件专题1——IGBT第1讲】IGBT:电力电子领域的 “万能开关”,如何撑起新能源时代?

一、IGBT 是什么&#xff1f;重新认识这个 “低调的电力心脏” 你可能没听过 IGBT&#xff0c;但一定用过它驱动的设备&#xff1a;家里的变频空调、路上的电动汽车、屋顶的光伏逆变器&#xff0c;甚至高铁和电网的核心部件里&#xff0c;都藏着这个 “电力电子开关的瑞士军刀”…...

文件IO(Java)

注&#xff1a;此博文为本人学习过程中的笔记 1.概念 狭义上的文件是指保存在硬盘上的文件&#xff0c;广义上指操作系统进行资源管理的一种机制&#xff0c;很多软件/硬件资源都可以抽象成文件&#xff0c;这里我们针对的是狭义上的文件。 在硬盘里还有文件夹&#xff0c;这…...

常见缓存淘汰算法(LRU、LFU、FIFO)的区别与实现

一、前言 缓存淘汰算法主要用于在内存资源有限的情况下&#xff0c;优化缓存空间的使用效率。以确保缓存系统在容量不足时能够智能地选择需要移除的数据。 二、LRU&#xff08;Least Recently Used&#xff09; 核心思想&#xff1a;淘汰最久未被访问的数据。实现方式&#x…...

Sentinel数据S2_SR_HARMONIZED连续云掩膜+中位数合成

在GEE中实现时&#xff0c;发现简单的QA60是无法去云的&#xff0c;最近S2地表反射率数据集又进行了更新&#xff0c;原有的属性集也进行了变化&#xff0c;现在的SR数据集名称是“S2_SR_HARMONIZED”。那么&#xff1a; 要想得到研究区无云的图像&#xff0c;可以参考执行以下…...

HTMLCSS模板实现水滴动画效果

.container 类&#xff1a;定义了页面的容器样式。 display: flex&#xff1a;使容器成为弹性容器&#xff0c;方便对其子元素进行布局。justify-content: center 和 align-items: center&#xff1a;分别使子元素在水平和垂直方向上居中对齐。min-height: 100vh&#xff1a;设…...

Cesium实现地形可视域分析

Cesium实现可视化分析 一、地形可视域主要实现技术(Ray + 地形碰撞检测) Cesium 本身的 Ray 类可以用来执行非常精确的射线检测,我们可以结合地形高度(sample)来逐点检测光线是否与 terrain 相交,从而判断是否可见。 1.1 优势 实时判断每条射线是否被 terrain 遮挡地形…...

前端如何获取文件的 Hash 值?多种方式详解、对比与实践指南

文章目录 前言一、Hash 值为何重要&#xff1f;二、Hash 值基础知识2.1 什么是 Hash&#xff1f;2.2 Hash 在前端的应用场景2.3 常见的 Hash 算法&#xff08;MD5、SHA 系列&#xff09; 三、前端获取文件 Hash 的常用方式3.1 使用 SparkMD5 计算 MD5 值3.2 使用 Web Crypto AP…...

【数据可视化艺术·应用篇】三维管线分析如何重构城市“生命线“管理?

在智慧城市、能源管理、工业4.0等领域的快速发展中&#xff0c;地下管线、工业管道、电力通信网络等“城市血管”的复杂性呈指数级增长。传统二维管理模式已难以应对跨层级、多维度、动态变化的管线管理需求。三维管线分析技术应运而生&#xff0c;成为破解这一难题的核心工具。…...

蓝桥杯 16.对局匹配

对局匹配 原题目链接 题目描述 小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分&#xff0c;代表他的围棋水平。 小明发现&#xff0c;网站的自动对局系统在匹配对手时&#xff0c;只会将积分差恰好是 K 的两名用户匹配在一起。如果两人分差小…...

【MinerU】:一款将PDF转化为机器可读格式的工具——RAG加强(Docker版本)

目录 创建容器 安装miniconda 安装mineru CPU运行 GPU加速 多卡问题 创建容器 构建Dockerfile文件 开启ssh服务&#xff0c;设置密码为1234等操作 # 使用官方 Ubuntu 24.04 镜像 FROM ubuntu:24.04# 安装基础工具和SSH服务 RUN apt-get update && \apt-get ins…...

DeepSeek回答过于笼统,提示词如何优化

针对DeepSeek回答过于笼统的问题&#xff0c;可通过以下方法优化&#xff0c;使输出更具体、详细&#xff1a; 一、优化提示词设计 明确具体要求 在提问中嵌入「背景限制示例」&#xff0c;例如&#xff1a; “作为跨境电商运营新手&#xff0c;请详细说明如何优化亚马逊产品标…...

C语言实现贪心算法

一、贪心算法核心思想 特征&#xff1a;在每一步选择中都采取当前状态下最优&#xff08;局部最优&#xff09;的选择&#xff0c;从而希望导致全局最优解 适用场景&#xff1a;需要满足贪心选择性质和最优子结构性质 二、经典贪心算法示例 1. 活动选择问题 目标&#xff1a…...

全球碳化硅晶片市场深度解析:技术迭代、产业重构与未来赛道争夺战(2025-2031)

一、行业全景&#xff1a;从“材料突破”到“能源革命”的核心引擎 碳化硅&#xff08;SiC&#xff09;作为第三代半导体材料的代表&#xff0c;凭借其宽禁带&#xff08;3.26eV&#xff09;、高临界击穿场强&#xff08;3MV/cm&#xff09;、高热导率&#xff08;4.9W/cmK&…...

FreeRTOS学习笔记【10】-----任务上下文切换

1 概念性内容 开机到调度需要经历的步骤有&#xff1a; 系统初始化任务创建启动调度器上下文切换时间分片任务执行 1.1 任务本质 FreeRTOS 的 任务&#xff08;Task&#xff09;本质上就是一个运行在任务自己的栈区中无限循环的函数 一段上下文&#xff08;context&#x…...

Appium自动化开发环境搭建

自动化 文章目录 自动化前言 前言 Appium是一款开源工具&#xff0c;用于自动化iOS、Android和Windows桌面平台上的本地、移动web和混合应用程序。原生应用是指那些使用iOS、Android或Windows sdk编写的应用。移动网页应用是通过移动浏览器访问的网页应用(appum支持iOS和Chrom…...

C++学习-入门到精通-【1】C++编程入门,输入/输出和运算符

C学习-入门到精通-【1】C编程入门&#xff0c;输入/输出和运算符 C编程入门&#xff0c;输入/输出和运算符 C学习-入门到精通-【1】C编程入门&#xff0c;输入/输出和运算符第一个C程序&#xff1a;输出一行文本算术运算 第一个C程序&#xff1a;输出一行文本 // 文本打印程序…...

UOJ 228 基础数据结构练习题 Solution

Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1​,a2​,⋯,an​)&#xff0c;有 m m m 个操作分三种&#xff1a; add ⁡ ( l , r , k ) \operatorname{add}(l,r,k) add(l,r,k)&#xff1a;对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 …...

面向高性能运动控制的MCU:架构创新、算法优化与应用分析

摘要&#xff1a;现代工业自动化、汽车电子以及商业航天等领域对运动控制MCU的性能要求不断提升。本文以国科安芯的MCU芯片AS32A601为例&#xff0c;从架构创新、算法优化到实际应用案例&#xff0c;全方位展示其在高性能运动控制领域的优势与潜力。该MCU以32位RISC-V指令集为基…...

某地农产品交易中心钢网架自动化监测项目

1. 项目简介 本项目规划建设现代物流产业园&#xff0c;新建6万平方米仓库&#xff0c;具体为新建3栋钢构仓库2万平方米&#xff0c;2栋砖混结构仓库1万平方米&#xff0c;3栋交易中心2万平方米&#xff0c;改造现有3栋3层砖混结构仓库1万平方米&#xff0c;配备智能化仓库物流…...

【无人机】无人机位置估计出现偏差的原因分析

目录 #0、原因分析 #1、过度振动的测定 #2、确定过度陀螺仪偏差 #3、偏航精度差的测定 #4、确定 GPS 精度差 #5、确定 GPS 数据丢失 #6、气压计地面效应补偿 #0、原因分析 位置背离的最常见原因是&#xff1a; 参考&#xff1a;Using the ECL EKF | PX4 Guide (v1.15)…...

element-plus(vue3)表单el-select下拉框的远程分页下拉触底关键字搜索实现

一、基础内核-自定义指令 1.背景 2.定义 3.使用 4.注意 当编辑时需要回显&#xff0c;此时由于分页导致可能匹配不到对应label文本显示&#xff0c;此时可以这样解决 二、升级使用-二次封装组件 三、核心代码 1.自定义指令 定义 ----------------selectLoadMoreDirective.…...

轻松完成视频创作,在线视频编辑器,无需下载软件,功能多样实用!

小白工具的在线视频编辑https://www.xiaobaitool.net/videos/edit/ 功能丰富、操作简便&#xff0c;在线裁剪或编辑视频工具&#xff0c;轻松完成视频创作能满足多种视频编辑需求。 格式支持广泛&#xff1a;可编辑超百种视频格式&#xff0c;基本涵盖常见和小众视频格式&#…...

高精度运算

1.乘法 #include <bits/stdc.h> using namespace std;char s1[2000], s2[2000]; int a[2000], b[2000], c[4000];int main() {cin >> s1 >> s2;int ls1 strlen(s1);int ls2 strlen(s2);int ls3 ls1 ls2;// 将字符串 s1 和 s2 转换为数组 a 和 bfor (int…...

express的模板handlebars用app.engine()创建配置和用exphbs.create()的区别

在使用 express-handlebars 时&#xff0c;app.engine 和 exphbs.create 都可以用来配置 Handlebars 模板引擎&#xff0c;但它们的使用方式和功能有一些区别。以下是详细的对比和说明 app.engine 方法 app.engine 是 Express 提供的方法&#xff0c;用于注册一个新的模板引擎…...

豆瓣图书数据采集与可视化分析(三)- 豆瓣图书数据统计分析

文章目录 前言一、数据读取与保存1. 读取清洗后数据2. 保存数据到CSV文件3. 保存数据到MySQL数据库 二、不同分类统计分析1. 不同分类的图书数量统计分析2. 不同分类的平均评分统计分析3. 不同分类的平均评价人数统计分析4. 不同分类的平均价格统计分析5. 分类综合分析 三、不同…...