【网络】3.HTTP(讲解HTTP协议和写HTTP服务)
目录
- 1 认识URL
- 1.1 URI的格式
- 2 HTTP协议
- 2.1 请求报文
- 2.2 响应报文
- 3 模拟HTTP
- 3.1 Socket.hpp
- 3.2 HttpServer.hpp
- 3.2.1 start()
- 3.2.2 ThreadRun()
- 3.2.3 HandlerHttp()
- 总结
1 认识URL
什么是URI?
URI 是 Uniform Resource Identifier的缩写,URI就是由某个协议方案表示的资源的定位标识符。采用HTTP协议时,协议方案就是http。除此之外,还有ftp、mailto、telnet等。
什么是URL?
URI用字符串标识某一互联网资源,而URL表示资源的地点(互联网上所处的位置)。可见URL是URI的子集。
1.1 URI的格式

登录信息认证
指定用户名和密码作为从五毒气短获取资源时必要的登录信息(身份认证)。此项是可选项。
服务器地址
使用绝对URI必须指定带访问的服务器地址。地址可以是DNS,或者是IPV4,IPV6格式。
服务器端口号
指定服务器连接的网络端口号,如果用户省略则自动使用默认端口号。
带层次的文件路径
指定服务器上的文件路径来定位特指的资源。这与UNIX系统的文件目录结构类似。如果不写,默认是首页,一般是index.html
查询字符串
针对已指定的文件路径内的资源,可以使用查询字符串兑换如任意参数。此项可选。
片段标识符
使用片段标识符通常可标记处已获取资源的子资源(文档内的某个位置)。但是在RFC中没有明确规定其使用方法。该项为可选项
2 HTTP协议
HTTP协议用于客户端和服务器之间的通信。
客户端
请求访问文本或图像等资源的一端称为客户端。
服务端
提供资源响应的一端称为服务端。
2.1 请求报文
下面是HTTP请求的格式:

真实的请求如下:

对比一下格式与真实情况:

2.2 响应报文
下面是HTTP响应的格式:

真实响应如下:

下面是对比:

3 模拟HTTP
下面的代码是socket套接字实现,主要为了http通信提供网络接口。
3.1 Socket.hpp
#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>enum
{SocketErr = 2,BindErr,ListenErr,
};const int backlog = 10;class Sock
{
public:Sock(){}~Sock(){}public:void Socket(){_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){std::cout << "sock error " << strerror(errno) << errno << std::endl;exit(SocketErr);}}void Bind(uint16_t port){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr =INADDR_ANY;if (bind(_sockfd, (struct sockaddr*)&local, sizeof(local)) < 0){std::cout << "bind error " << strerror(errno) << errno << std::endl;exit(BindErr);}}void Listen(){if (listen(_sockfd, backlog) < 0){std::cout << "listen error " << strerror(errno) << errno << std::endl;exit(ListenErr);}}int Accept(std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;socklen_t len = sizeof(peer);int newfd = accept(_sockfd, (struct sockaddr*)& peer, &len);if (newfd < 0){std::cout << "accept error " << strerror(errno) << errno << std::endl;return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));*clientip = ipstr;*clientport = ntohs(peer.sin_port);return newfd; }bool Connnect(const std::string &ip, const uint16_t &port){struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family =AF_INET;peer.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));int n = connect(_sockfd, (struct sockaddr*)&peer, sizeof(peer));if (n == -1){std::cout << "connect to" << ip << " : " << port << " error " << std::endl;return false;}return true;}void Close(){close(_sockfd);}int Fd(){return _sockfd;}private://文件描述符int _sockfd;
};
3.2 HttpServer.hpp
class HttpServer
{
public:HttpServer(int port = defaultport):_port(defaultport){}
private:Sock _listensock;uint16_t _port;
};
模拟的是使用HTTP协议的过程,客户端需要向浏览器访问,
输入的内容为ip:port。 例如:http://124.223.90.51:8085
因此,需要创建一个函数来启动HTTP服务器:
3.2.1 start()
bool Start(){//创建套接字_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();for (;;){std::string clientip;uint16_t clientport;int sockfd = _listensock.Accept(&clientip, &clientport);pthread_t tid;ThreadData *td = new ThreadData(sockfd, this);td->sockfd = sockfd;pthread_create(&tid, nullptr, ThreadRun, td);}}
1.创建监听套接字
_listensock.Socket();
_listensock.Bind(_port);
_listensock.Listen();
_listensock.Socket():创建 TCP 套接字(socket())。_listensock.Bind(_port):绑定端口 _port(bind())。_listensock.Listen():监听端口,等待客户端连接(listen())。
作用:服务器启动,监听 _port 端口,准备接受 HTTP 请求。.
2.接受客户端连接
int sockfd = _listensock.Accept(&clientip, &clientport);
- Accept():阻塞等待客户端连接,成功返回新连接的套接字 sockfd,并获取客户端的 IP 和端口。
- 客户端访问
http://服务器IP:端口,就会触发 Accept()。
作用:获取客户端连接,并准备创建线程处理。.
3.创建新线程处理客户端请求
pthread_t tid;
ThreadData *td = new ThreadData(sockfd, this);
td->sockfd = sockfd;
pthread_create(&tid, nullptr, ThreadRun, td);
- 创建 ThreadData 结构体,存储 sockfd 和 this(服务器指针)。
pthread_create()创建新线程ThreadRun(td),让线程处理 sockfd 连接。
作用:服务器为每个客户端请求创建一个新线程并行处理,提高并发能力。.
3.2.2 ThreadRun()
static void* ThreadRun(void* args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);// char buffer[10240];// //ssize_t n = read(?; buffer, sizeof(buffer - 1)); 可以使用read// //也可以使用recv// ssize_t n = recv(td->sockfd, buffer, sizeof(buffer - 1), 0);// if (n > 0)// {// buffer[n] = 0;// std::cout << buffer;// }HandlerHttp(td->sockfd, td->httpsvr);delete td;return nullptr;}
-
这段代码是 HttpServer 服务器每个新线程的执行函数 ThreadRun(),用于处理客户端的 HTTP 请求。
-
当客户端连接服务器时,服务器会创建一个新线程执行 ThreadRun(),读取 HTTP 请求并进行处理。
pthread_detach(pthread_self()) 作用:
- 让线程在完成后自动释放资源,不需要 pthread_join() 手动回收。
- 避免僵尸线程(已结束但未回收的线程)。
- 适用于短生命周期的线程,如 HTTP 服务器的请求处理线程。
为什么要线程分离 ?
- HTTP 请求通常是短暂的,处理完成后线程就不需要存在了。
- 如果不分离,主线程需要 pthread_join() 逐个回收,会浪费资源。
- 让线程自动销毁,提高服务器并发能力。
为什么ThreadRun被设置为static?
- pthread_create() 需要一个 C 语言风格的函数指针
pthread_create的函数签名如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
第三个参数
start_routine是一个函数指针,必须符合 void* ()(void) 这种标准格式。普通成员函数有一个隐藏的
this指针,无法直接传递给 pthread_create()。
所以,必须用:
普通的 C 函数(static 成员函数), 或全局函数。
3.2.3 HandlerHttp()
static void HandlerHttp(int sockfd, HttpServer *httpsvr){char buffer[10240];ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); //在这里,不能保证读到了完整的http请求if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl; //假设我们读到的是一个完整的http请求HttpRequest req;req.Deserialize(buffer);req.Parse();std::string text;bool ok = true; text = ReadHtmlContent(req.file_path); //读取客户端请求的文件 --> "./index.html"if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/";err_html += "err.html";text = ReadHtmlContent(err_html);}//返回响应的过程std::string response_lines;if (ok){response_lines = "HTTP/1.0 200 OK\r\n";}else {response_lines = "HTTP/1.0 404 Not Found\r\n";}std::string response_header = "Content-Length: ";response_header += std::to_string(text.size());response_header += "\r\n";response_header += "Content-Type: text/html\r\n";response_header += "\r\n";response_header += "Set-Cookie : name=haha&&passds=12345";response_header += "\r\n";std::string blank_lines = "\r\n";std::string response = response_lines;response += response_header;response += blank_lines;response += text;//将内容发送给客户端 -- 响应send(sockfd, response.c_str(), response.size(), 0);}close(sockfd);}
HandlerHttp函数的作用是处理HTTP请求并返回响应
HandlerHttp() 的作用是 处理 HTTP 请求并返回响应,它是 HTTP 服务器的核心逻辑,负责:
- 读取客户端请求数据
- 解析 HTTP 请求
- 获取请求的资源(如 HTML 页面)
- 构造 HTTP 响应
- 将 HTTP 响应返回给客户端
- 关闭连接
1. 如何解析HTTP请求?
根据前文所说,HTTP请求格式如下:

可以看到,HTTP请求是一个已经序列化的报文,因此,我们如果想要知道具体的Method, URI,Http_Version就必须进行反序列化。
void Deserialize(std::string req)
{while(true){ssize_t pos = req.find(seq); // 找到 "\r\n" 的位置if (pos == std::string::npos) // 没有找到 "\r\n",说明 HTTP 头部已经解析完毕break;std::string temp = req.substr(0, pos); // 提取一行 HTTP 头部信息if (temp.empty()) // 如果这一行是空的,说明遇到了 HTTP 头部和正文的分隔行break;req_header.push_back(temp); // 将解析出的头部信息存入 `req_header`req.erase(0, pos + seq.size()); // 删除已经解析的部分,继续处理剩下的内容}// req 现在去掉了所有头部,剩下的就是 HTTP 请求的正文text = req;
}
假设客户端发送了如下 HTTP 请求:

那么req_header 里的内容:

经过反序列化之后,req_header的内容如下:
req_header[0] = “GET /index.html HTTP/1.1”
req_header[1] = “Host: 124.223.90.51:8085”
…
text = “name=hello&password=1234”;
2. 如何获取Method/URI/Http_Version?
std::stringstream ss(req_header[0]);
ss >> method >> url >> http_version;
① std::stringstream ss(req_header[0])
std::stringstream是 C++ 的 字符串流(string stream),类似std::cin,可以像读取标准输入一样 按空格分割字符串。- 这里
req_header[0]是"GET /index.html HTTP/1.1",将其存>入 ss 后,ss 变成了一个可以逐个提取单词的输入流。
② ss >> method >> url >> http_version;
ss >> method→ 提取 “GET”,存入 method。ss >> url→ 提取 “/index.html”,存入 url。ss >> http_version→ 提取 “HTTP/1.1”,存入 http_version。
最终:

3. 如何根据客户端的输入确定要访问哪个文件?
首先定义:
const std::string wwwroot = "./wwwroot";
./wwwroot路径是HTTP的根目录,所有的文件都放在根目录下,如果客户端不指定访问具体的哪个文件,那么默认访问根目录。
file_path = wwwroot;if (url == "/" || url == "/index.html") //根目录{file_path += "/";file_path += homepage;}else{file_path += url;}
通过上面的代码确定file_path的值,也就可以精准访问到具体的文件。
4. 如何读取html文件的内容?
之前确定了file_path的值,也就是确定了读取的具体的文件,接下来就是进入到文件内部读取文件的内容。
std::ifstream in(htmlpath, std::ios::binary);
- std::ifstream 打开文件,std::ios::binary 以 二进制模式 读取(防止换行符转换)。
- 如果 htmlpath 指定的文件 不存在或打不开,流对象 in 不会打开。
in.seekg(0, std::ios_base::end);
auto len = in.tellg();
in.seekg(0, std::ios_base::beg);
seekg(0, std::ios_base::end);→ 将文件指针移动到文件末尾,这样 tellg() 可以获取 文件大小。len = in.tellg();→ 获取文件的长度(字节数)。seekg(0, std::ios_base::beg);→ 将文件指针移动回文件开头,准备读取内容。
std::string content;
content.resize(len);
- 创建字符串 content,并分配 len 个字符的空间,以存放 HTML 文件的内容。
in.read((char*)content.c_str(), content.size());
- 读取文件内容到 content:
in.read()读取 content.size() 个字符,并存入 content 中。content.c_str()获取 std::string 的底层 C 风格字符数组的指针,保证数据存储正确
5. 为什么要采用二进制的读法?
因为读取的不一定是html文件,有可能是图片,视频。如果是普通的read方法,可能无法读取图片资源。
6. 如何返回HTTP响应?

1. 根据读到的内容判断状态码:
if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/";err_html += "err.html";text = ReadHtmlContent(err_html);}//返回响应的过程std::string response_lines;if (ok){response_lines = "HTTP/1.0 200 OK\r\n";}else {response_lines = "HTTP/1.0 404 Not Found\r\n";}
2. 添加Key:Value部分:
std::string response_header = "Content-Length: ";response_header += std::to_string(text.size());response_header += "\r\n";response_header += "Content-Type: text/html\r\n";response_header += "\r\n";response_header += "Set-Cookie : name=haha&&passds=12345";response_header += "\r\n";
3.添加空行部分
std::string blank_lines = "\r\n";
4.整理所有的内容返回给客户端
std::string response = response_lines;response += response_header;response += blank_lines;response += text;//将内容发送给客户端 -- 响应send(sockfd, response.c_str(), response.size(), 0);
总结
HttpServer.hpp的内容如下:
#pragma once#include <iostream>
#include <pthread.h>
#include "Socket.hpp"
#include <string>
#include <vector>
#include <fstream>
#include <sstream>static const int defaultport = 8085;const std::string seq = "\r\n";
const std::string wwwroot = "./wwwroot";
const std::string homepage = "index.html";class HttpServer;class ThreadData
{
public:ThreadData(int fd, HttpServer *ts):sockfd(fd),httpsvr(ts){}
public:int sockfd;HttpServer *httpsvr;
};class HttpRequest
{
public://反序列化 -- 将从客户端读到的http请求push_back进req_header中void Deserialize(std::string req){while(true){ssize_t pos = req.find(seq);if (pos == std::string::npos)break;std::string temp = req.substr(0, pos);if (temp.empty())break;req_header.push_back(temp);req.erase(0, pos + seq.size());}//req去掉前面的内容之后,剩下的全是文本text = req;}void DebugPrint(){for (auto &line : req_header){std::cout << "-------------------" << std::endl;std::cout << line << "\n\n";}std::cout << "method : " << method << std::endl;std::cout << "url : " << url << std::endl;std::cout << "http version : " << http_version << std::endl;std::cout << "file path : " << file_path << std::endl;std::cout << "text : " << text << std::endl;}void Parse(){std::stringstream ss(req_header[0]);ss >> method >> url >> http_version; //用stringstream将method等直接分割了file_path = wwwroot;if (url == "/" || url == "/index.html") //根目录{file_path += "/";file_path += homepage;}else{file_path += url;}// auto pos = file_path.rfind(".");// if (pos == std::string::npos)}
public:std::vector<std::string> req_header; //请求std::string text;//解析之后的结果 --> 这是http的请求报文的格式std::string method;std::string url;std::string http_version;std::string file_path;
};class HttpServer
{
public:HttpServer(int port = defaultport):_port(defaultport){}bool Start(){//创建套接字_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();for (;;){std::string clientip;uint16_t clientport;int sockfd = _listensock.Accept(&clientip, &clientport);pthread_t tid;ThreadData *td = new ThreadData(sockfd, this);td->sockfd = sockfd;pthread_create(&tid, nullptr, ThreadRun, td);}} static void* ThreadRun(void* args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);// char buffer[10240];// //ssize_t n = read(?; buffer, sizeof(buffer - 1)); 可以使用read// //也可以使用recv// ssize_t n = recv(td->sockfd, buffer, sizeof(buffer - 1), 0);// if (n > 0)// {// buffer[n] = 0;// std::cout << buffer;// }HandlerHttp(td->sockfd, td->httpsvr);delete td;return nullptr;}//固定版本// static void HanderHttp(int sockfd, HttpServer *httpsvr)// {// char buffer[10240];// ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); //在这里,不能保证读到了完整的http请求// if (n > 0)// {// buffer[n] = 0;// std::cout << buffer;// //返回相应的过程// std::string text = "mayue is a pig! xixi~";// std::string response_lines = "HTTP/1.0 200 OK\r\n";// std::string response_header = "Content-Length: ";// response_header += std::to_string(text.size());// response_header += "\r\n";// std::string blank_lines = "\r\n";// std::string response = response_lines;// response += response_header;// response += blank_lines;// response += text;// //将内容发送给发送方 -- 响应// send(sockfd, response.c_str(), response.size(), 0);// }// close(sockfd);// }//读取文件内容static std::string ReadHtmlContent(const std::string &htmlpath){std::ifstream in(htmlpath, std::ios::binary);if (!in.is_open())return "";in.seekg(0, std::ios_base::end);auto len = in.tellg();in.seekg(0, std::ios_base::beg);std::string content;content.resize(len);in.read((char*)content.c_str(), content.size());in.close();return content;}//显示不同的html,进行处理static void HandlerHttp(int sockfd, HttpServer *httpsvr){char buffer[10240];ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); //在这里,不能保证读到了完整的http请求if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl; //假设我们读到的是一个完整的http请求HttpRequest req;req.Deserialize(buffer);req.Parse();std::string text;bool ok = true; text = ReadHtmlContent(req.file_path); //读取客户端请求的文件 --> "./index.html"if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/";err_html += "err.html";text = ReadHtmlContent(err_html);}//返回响应的过程std::string response_lines;if (ok){response_lines = "HTTP/1.0 200 OK\r\n";}else {response_lines = "HTTP/1.0 404 Not Found\r\n";}std::string response_header = "Content-Length: ";response_header += std::to_string(text.size());response_header += "\r\n";response_header += "Content-Type: text/html\r\n";response_header += "\r\n";response_header += "Set-Cookie : name=haha&&passds=12345";response_header += "\r\n";std::string blank_lines = "\r\n";std::string response = response_lines;response += response_header;response += blank_lines;response += text;//将内容发送给客户端 -- 响应send(sockfd, response.c_str(), response.size(), 0);}close(sockfd);}~HttpServer(){}private:Sock _listensock;uint16_t _port;
};
HttpServer.cc
#include "Httpserver.hpp"
#include <iostream>
#include <memory>using namespace std;int main()
{//std::unique<HttpServer> svr(new HttpServer());HttpServer *svr = new HttpServer();svr->Start();return 0;
}

相关文章:
【网络】3.HTTP(讲解HTTP协议和写HTTP服务)
目录 1 认识URL1.1 URI的格式 2 HTTP协议2.1 请求报文2.2 响应报文 3 模拟HTTP3.1 Socket.hpp3.2 HttpServer.hpp3.2.1 start()3.2.2 ThreadRun()3.2.3 HandlerHttp() 总结 1 认识URL 什么是URI? URI 是 Uniform Resource Identifier的缩写&…...
在K8s中部署动态nfs存储provisioner
背景 之前,我已经在一台worker node上安装了local lvm 的provisioner来模拟需要本地高IOPS的数据库等stafeful应用的实现。 为了后续给虚拟机里的K8s集群安装可用的metrics和logs监控系统(metrics和logs的时序数据库需要永久存储)࿰…...
优雅管理Python2 and python3
python2 和 python3, 由于没有像其他软件的向下兼容,必须同时安装Python2 和Python3 ,介绍在linux和windows下优雅管理。 一、linux中安装Python2和Python3 linux 中用conda 创建虚拟环境,来管理不同版版工具 由于主流使用Python3…...
创建与管理MySQL数据库
数据库是现代应用程序的核心部分,无论是Web开发、数据分析还是企业级应用,数据库的创建与管理是基础且关键的技能。本教程旨在帮助自学编程的学习者掌握如何通过SQL命令创建、管理和操作数据库。通过本教程,可以学会如何创建数据库、查看已有数据库、选择数据库以及删除不再…...
基于微信小程序的辅助教学系统的设计与实现
标题:基于微信小程序的辅助教学系统的设计与实现 内容:1.摘要 摘要:随着移动互联网的普及和微信小程序的兴起,基于微信小程序的辅助教学系统成为了教育领域的一个新的研究热点。本文旨在设计和实现一个基于微信小程序的辅助教学系统,以提高教…...
Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...
网络模型简介:OSI七层模型与TCP/IP模型
计算机网络是现代信息社会的基石,而网络通信的基础在于理解网络模型。网络模型是对通信过程的抽象,它帮助我们理解数据从源到目的地的传输过程。常见的网络模型有 OSI 七层模型 和 TCP/IP 模型,这两种模型在理论和实践中都起着重要作用。 一、…...
大模型本地化部署(Ollama + Open-WebUI)
文章目录 环境准备下载Ollama模型下载下载Open-WebUI 本地化部署的Web图形化界面本地模型联网查询安装 Docker安装 SearXNG本地模型联网查询 环境准备 下载Ollama 下载地址:Ollama网址 安装完成后,命令行里执行命令 ollama -v查看是否安装成功。安装成…...
Java 性能优化与新特性
Java学习资料 Java学习资料 Java学习资料 一、引言 Java 作为一门广泛应用于企业级开发、移动应用、大数据等多个领域的编程语言,其性能和特性一直是开发者关注的重点。随着软件系统的规模和复杂度不断增加,对 Java 程序性能的要求也越来越高。同时&a…...
【Linux系统】进程间通信:共享内存
认识共享内存 通过 一些系统调用,在物理内存中开辟一块空间,然后将该空间的起始地址,通过页表映射到两个进程的虚拟地址空间的共享区中,这样不就共享了一块空间吗!!! 这种技术就是共享内存&am…...
渗透测试之WAF组合条件绕过方式手法详解以及SQL注入参数污染绕过
目录 组合绕过waf 先看一些语句 绕过方式 我给出的注入语句是: 这里要注意的几点是: 组合绕过方式 完整过狗注入语句集合 http请求分块传输方法 其它方式绕过 http参数污染绕过waf 面试题:如何参数污染绕过waf 可以通过http参数污染绕过wa…...
oracl:多表查询>>表连接[内连接,外连接,交叉连接,自连接,自然连接,等值连接和不等值连接]
SQL(Structured Query Language,结构化查询语言)是一种用于管理和操作关系数据库的标准编程语言。 sql分类: 数据查询语言(DQL - Data Query Language) 查询的关键词 select 多表查询>>表连接 表连接: 把2个…...
Day31-【AI思考】-关键支点识别与战略聚焦框架
文章目录 关键支点识别与战略聚焦框架**第一步:支点目标四维定位法****第二步:支点验证里程碑设计****第三步:目标网络重构方案****第四步:动态监控仪表盘** 执行工具箱核心心法 关键支点识别与战略聚焦框架 让思想碎片重焕生机的…...
ARIMA详细介绍
ARIMA(AutoRegressive Integrated Moving Average,自回归积分滑动平均模型)是一种用于时间序列分析和预测的统计模型。它结合了自回归(AR)、差分(I)和移动平均(MA)三种方…...
如何解决Unit sshd.service could not be found
出现 Unit sshd.service could not be found 错误时,通常是因为系统中未安装 OpenSSH 服务、服务名称不匹配或系统未使用 systemd 管理服务。以下是详细的解决方案: 一、确认 SSH 服务是否安装 1. 检查是否已安装 OpenSSH 服务器 不同 Linux 发行版的包…...
飞致云开源社区月度动态报告(2025年1月)
自2023年6月起,中国领先的开源软件公司飞致云以月度为单位发布《飞致云开源社区月度动态报告》,旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况,以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源运营数据概览&…...
【搜索回溯算法篇】:拓宽算法视野--BFS如何解决拓扑排序问题
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索(BFS)解决拓扑排…...
WPS怎么使用latex公式?
1、下载并安装mathtype https://blog.csdn.net/weixin_43135178/article/details/125143654?sharetypeblogdetail&sharerId125143654&sharereferPC&sharesourceweixin_43135178&spm1011.2480.3001.8118 2、将mathtype嵌入在WPS MathType面板嵌入器,免费工具…...
简单的爱心跳动表白网页(附源码)
一:准备工作 在开始之前,确保已经具备基础的 HTML、CSS 和 JavaScript 知识。同时,也要准备好一个代码编辑器,比如 VS Code 或 Sublime Text。接下来,我们需要创建三个文件:index.html、styles.css 和 scr…...
【AI】DeepSeek 概念/影响/使用/部署
在大年三十那天,不知道你是否留意到,“deepseek”这个词出现在了各大热搜榜单上。这引起了我的关注,出于学习的兴趣,我深入研究了一番,才有了这篇文章的诞生。 概念 那么,什么是DeepSeek?首先百…...
代理模式 - 代理模式的应用
引言 代理模式(Proxy Pattern)是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象的访问。代理对象通常会在客户端和目标对象之间起到中介的作用,从而可以在不改变目标对象的情况下,增加额外的功能或控…...
DeepSeek超越ChatGPT的能力及部分核心原理
DeepSeek超越ChatGPT的能力及部分核心原理 目录 DeepSeek超越ChatGPT的能力及部分核心原理超越ChatGPT的能力核心原理超越ChatGPT的能力 推理计算能力更强:在复杂的数学计算、法律文件审查等任务中,DeepSeek的推理能力可媲美甚至超越部分国际顶尖AI模型,包括ChatGPT。例如在…...
【4Day创客实践入门教程】Day3 实战演练——桌面迷你番茄钟
Day3 实战演练——桌面迷你番茄钟 目录 Day3 实战演练——桌面迷你番茄钟1. 选择、准备元件、收集资料2. 硬件搭建3.编写代码 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟…...
Git 出现 Please use your personal access token instead of the password 解决方法
目录 前言1. 问题所示2. 原理分析3. 解决方法前言 1. 问题所示 执行Git提交代码的时候,出现如下所示: lixiaosong@IT07 MINGW64 /f/java_project/JavaDemo (master) $ git push -u origin --all libpng warning: iCCP: known incorrect sRGB profile libpng warning...
LeetCode题练习与总结:不含连续1的非负整数--600
一、题目描述 给定一个正整数 n ,请你统计在 [0, n] 范围的非负整数中,有多少个整数的二进制表示中不存在 连续的 1 。 示例 1: 输入: n 5 输出: 5 解释: 下面列出范围在 [0, 5] 的非负整数与其对应的二进制表示: 0 : 0 1 : 1 2 : 10 3 :…...
AndroidCompose Navigation导航精通1-基本页面导航与ViewPager
文章目录 前言基本页面导航库依赖导航核心部件简单NavHost实现ViewPagerPager切换逻辑图阐述Pager导航实战前言 在当今的移动应用开发中,导航是用户与应用交互的核心环节。随着 Android Compose 的兴起,它为开发者提供了一种全新的、声明式的方式来构建用户界面,同时也带来…...
【环境搭建】1.1源码下载与同步
目录 写在前面 一,系统要求 二,安装depot_tools 三,获取代码 四,代码同步 五,代码结构 写在前面 当前的开发背景是基于Google的开源Chromium,来开发Android设备的浏览器方案。 一,系统要…...
Node.js——body-parser、防盗链、路由模块化、express-generator应用生成器
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…...
python | OpenCV小记(一):cv2.imread(f) 读取图像操作(待更新)
python | OpenCV小记(一):cv2.imread(f)读取图像操作 1. 为什么 [:, :, 0] 提取的是第一个通道(B 通道)?OpenCV 的通道存储格式索引操作 [:, :, 0] 的解释常见误解 1. 为什么 [:, :,…...
C语言指针专题四 -- 多级指针
目录 1. 多级指针的核心原理 1. 多级指针的定义 2. 内存结构示意图 3. 多级指针的用途 2. 编程实例 实例1:二级指针操作(修改一级指针的值) 实例2:动态二维数组(二级指针) 实例3:三级指…...
