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

C++服务器 支持http、tcp protobuf、websocket,linux开源框架 零依赖轻松编译部署 Reactor

开源地址: https://github.com/crust-hub/tubekit/tree/main
Github:https://github.com/gaowanlu
诚招有兴趣的小伙伴加入开发维护

Tubekit

The C++ TCP server framework based on the Reactor model continues to implement POSIX thread pool, Epoll, non blocking IO, object pool, log, socket network programming, support the dynamic library to implement custom protocol extensions, and use http parser to process http requests. Currently only supports Linux systems

Platform: Linux
Protocol: HTTP TCP Stream(Protobuf) WebSocket

Get Start

prepare

$ sudo apt update
$ sudo apt install protobuf-compiler libprotobuf-dev
$ apt install g++ cmake make
$ git clone https://github.com/crust-hub/tubekit.git

Build

$ cd tubekit
$ cd protocol
$ make
$ cd ..
$ cmake .
$ make -j3

Config

$ vim bin/config/main.ini

Run

$ chmod +x ./run.sh
$ ./run.sh

Stop

$ chmod +x ./kill.sh
$ ./kill.sh

App

support tcp keep-alive stream (protobuf) and http app (http-parser)、websocket

Directory Structure

Directory Structure Link

Third Party

@http-parser
@lua

HTTP样例

#include "app/http_app.h"
#include <string>
#include <vector>
#include <filesystem>
#include <tubekit-log/logger.h>#include "utility/mime_type.h"
#include "utility/url.h"using std::string;
using std::vector;
using tubekit::app::http_app;
using tubekit::connection::http_connection;
namespace fs = std::filesystem;
namespace utility = tubekit::utility;class html_loader
{
public:static string load(string body){static string frame1 = "<!DOCTYPE html>\<html>\<head>\<title></title>\</head>\<body>";static string frame2 = "</body>\</html>";return frame1 + body + frame2;}static string a_tag(string url, string text){string frame = "<a href=\"" + url + "\">" + text + "</a></br>";return frame;}
};void http_app::process_connection(tubekit::connection::http_connection &m_http_connection)
{m_http_connection.m_buffer.set_limit_max(202300);// load callbackm_http_connection.destory_callback = [](http_connection &m_connection) -> void{if (m_connection.ptr){FILE *file = (FILE *)m_connection.ptr;::fclose(file);m_connection.ptr = nullptr;}};m_http_connection.process_callback = [](http_connection &connection) -> void{string url = utility::url::decode(connection.url);auto find_res = url.find("..");if (std::string::npos != find_res){connection.set_response_end(true);return;}const string prefix = "/";fs::path t_path;if (url.empty() || url[0] != '/'){t_path = prefix + url;}else{t_path = url;}if (fs::exists(t_path) && fs::status(t_path).type() == fs::file_type::regular){std::string mime_type;try{mime_type = utility::mime_type::get_type(t_path.string());}catch (...){mime_type = "application/octet-stream";}std::string response = "HTTP/1.1 200 OK\r\nServer: tubekit\r\n";response += "Content-Type: ";response += mime_type + "\r\n\r\n";try{connection.m_buffer.write(response.c_str(), response.size());}catch (const std::runtime_error &e){LOG_ERROR(e.what());}connection.ptr = nullptr;connection.ptr = ::fopen(t_path.c_str(), "r");if (connection.ptr == nullptr){connection.set_response_end(true);return;}// Write when the contents of the buffer have been sent write_end_callback will be executed,// and the response must be set response_end to true, then write after write_end_callback will be continuously recalledconnection.write_end_callback = [](http_connection &m_connection) -> void{char buf[202300] = {0};int len = 0;len = ::fread(buf, sizeof(char), 202300, (FILE *)m_connection.ptr);if (len > 0){try{m_connection.m_buffer.write(buf, len);}catch (const std::runtime_error &e){LOG_ERROR(e.what());}}else{m_connection.set_response_end(true);}};return;}if (fs::exists(t_path) && fs::status(t_path).type() == fs::file_type::directory){connection.ptr = nullptr;const char *response = "HTTP/1.1 200 OK\r\nServer: tubekit\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n";try{connection.m_buffer.write(response, strlen(response));}catch (const std::runtime_error &e){LOG_ERROR(e.what());}//  generate dir listvector<string> a_tags;for (const auto &dir_entry : fs::directory_iterator{t_path}){std::string sub_path = dir_entry.path().string(); //.substr(prefix.size());a_tags.push_back(html_loader::a_tag(utility::url::encode(sub_path), sub_path));}string body;for (const auto &a_tag : a_tags){body += a_tag;}string html = html_loader::load(body);try{connection.m_buffer.write(html.c_str(), html.size());}catch (const std::runtime_error &e){LOG_ERROR(e.what());}connection.set_response_end(true);return;}const char *response = "HTTP/1.1 404 Not Found\r\nServer: tubekit\r\nContent-Type: text/text; charset=UTF-8\r\n\r\n";try{connection.m_buffer.write(response, strlen(response));}catch (const std::runtime_error &e){LOG_ERROR(e.what());}connection.set_response_end(true);};
}

protobuf样例

#include "app/stream_app.h"
#include "proto_res/proto_cmd.pb.h"
#include "proto_res/proto_example.pb.h"
#include "proto_res/proto_message_head.pb.h"
#include <tubekit-log/logger.h>
#include <string>
#include <set>
#include "thread/mutex.h"
#include "utility/singleton.h"
#include "connection/connection_mgr.h"
#include "socket/socket.h"
#include "socket/socket_handler.h"using tubekit::app::stream_app;
using tubekit::connection::connection_mgr;
using tubekit::connection::stream_connection;
using tubekit::socket::socket;
using tubekit::socket::socket_handler;
using tubekit::utility::singleton;namespace tubekit::app
{std::set<void *> global_player;tubekit::thread::mutex global_player_mutex;
}int process_protocol(tubekit::connection::stream_connection &m_stream_connection, ProtoPackage &package)
{// EXAMPLE_REQif (package.cmd() == ProtoCmd::EXAMPLE_REQ){ProtoExampleReq exampleReq;if (exampleReq.ParseFromString(package.body())){LOG_ERROR("%s", exampleReq.testcontext().c_str());// std::cout << exampleReq.testcontext() << std::endl;}else{return -1;}return 0;}return -1;
}void stream_app::process_connection(tubekit::connection::stream_connection &m_stream_connection)
{using tubekit::app::global_player;using tubekit::app::global_player_mutex;uint64_t all_data_len = m_stream_connection.m_recv_buffer.can_readable_size();char *all_data_buffer = new char[all_data_len];m_stream_connection.m_recv_buffer.copy_all(all_data_buffer, all_data_len);uint64_t offset = 0;do{char *tmp_buffer = all_data_buffer + offset;uint64_t data_len = all_data_len - offset;if (data_len == 0){break;}ProtoPackage protoPackage;if (!protoPackage.ParseFromArray(tmp_buffer, data_len)){// std::cout << "protoPackage.ParseFromArray failed" << std::endl;break;}if (0 != process_protocol(m_stream_connection, protoPackage)){// std::cout << "process_protocol failed" << std::endl;m_stream_connection.mark_close();m_stream_connection.m_recv_buffer.clear();break;}// std::cout << "datalen " << data_len << " package size " << protoPackage.ByteSizeLong() << std::endl;offset += protoPackage.ByteSizeLong();} while (true);if (!m_stream_connection.m_recv_buffer.read_ptr_move_n(offset)){m_stream_connection.mark_close();}delete[] all_data_buffer;
}void stream_app::on_close_connection(tubekit::connection::stream_connection &m_stream_connection)
{using tubekit::app::global_player;using tubekit::app::global_player_mutex;global_player_mutex.lock();global_player.erase(m_stream_connection.get_socket_ptr());LOG_ERROR("player online %d", global_player.size());global_player_mutex.unlock();
}void stream_app::on_new_connection(tubekit::connection::stream_connection &m_stream_connection)
{using tubekit::app::global_player;using tubekit::app::global_player_mutex;global_player_mutex.lock();global_player.insert(m_stream_connection.get_socket_ptr());LOG_ERROR("player online %d", global_player.size());global_player_mutex.unlock();
}bool stream_app::new_client_connection(const std::string &ip, int port)
{socket::socket *socket_object = singleton<socket_handler>::instance()->alloc_socket();if (!socket_object){LOG_ERROR("alloc_socket return nullptr");return false;}bool b_ret = socket_object->connect(ip, port);if (!b_ret){LOG_ERROR("connection remote %s:%d failed", ip.c_str(), port);singleton<socket_handler>::instance()->remove(socket_object);return false;}int i_ret = singleton<socket_handler>::instance()->attach(socket_object);if (0 != i_ret){LOG_ERROR("attach to socket_handler error ret %d", i_ret);singleton<socket_handler>::instance()->remove(socket_object);return false;}// maybe to do some management for client socket...return true;
}

websocket

#include "app/websocket_app.h"
#include <vector>
#include <tubekit-log/logger.h>
#include "utility/singleton.h"
#include "connection/connection_mgr.h"
#include <arpa/inet.h>using namespace tubekit::app;
using namespace tubekit::utility;
using namespace tubekit::connection;struct websocket_frame
{uint8_t fin;uint8_t opcode;uint8_t mask;uint64_t payload_length;std::vector<uint8_t> masking_key;std::string payload_data;
};enum class websocket_frame_type
{CONNECTION_CLOSE_FRAME = 0,TEXT_FRAME = 1,BINARY_FRAME = 2,PONG = 3,PING = 4,CONTINUATION_FRAME = 5,ERROR = 6
};void websocket_app::process_connection(tubekit::connection::websocket_connection &m_websocket_connection)
{LOG_ERROR("process_connection");uint64_t all_data_len = m_websocket_connection.m_recv_buffer.can_readable_size();if (all_data_len <= 0){LOG_ERROR("all_data_len <= 0");return;}char *data = new (std::nothrow) char[all_data_len];if (!data){return;}all_data_len = m_websocket_connection.m_recv_buffer.copy_all(data, all_data_len);size_t index = 0;while (true){if (index >= all_data_len){break;}size_t start_index = index;websocket_frame frame;websocket_frame_type type = websocket_frame_type::ERROR;switch ((uint8_t)data[index]){case 0x81:{type = websocket_frame_type::TEXT_FRAME;break;}case 0x82:{type = websocket_frame_type::BINARY_FRAME;break;}case 0x88:{type = websocket_frame_type::CONNECTION_CLOSE_FRAME;break;}case 0x89:{type = websocket_frame_type::PING;break;}default:{if (data[index] >= 0x00 && data[index] <= 0x7F){type = websocket_frame_type::CONTINUATION_FRAME;}break;}}if (type != websocket_frame_type::TEXT_FRAME && type != websocket_frame_type::BINARY_FRAME){m_websocket_connection.mark_close();break;}frame.fin = (data[index] & 0x80) != 0;frame.opcode = data[index] & 0x0F;index++;if (index >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index, all_data_len);break;}frame.mask = (data[index] & 0x80) != 0;frame.payload_length = data[index] & 0x7F;index++;if (index >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index, all_data_len);break;}if (frame.payload_length == 126){frame.payload_length = 0;if (index + 2 >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index + 2, all_data_len);break;}uint16_t tmp = 0;u_char *ph;ph = (u_char *)&tmp;*ph++ = data[index];*ph++ = data[index + 1];tmp = ntohs(tmp);frame.payload_length = tmp;index += 2;}else if (frame.payload_length == 127){frame.payload_length = 0;if (index + 8 >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index + 8, all_data_len);break;}uint32_t tmp = 0;u_char *ph = (u_char *)&tmp;*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];frame.payload_length = ntohl(tmp);frame.payload_length = frame.payload_length << 32;ph = (u_char *)&tmp;*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];tmp = ntohl(tmp);frame.payload_length = frame.payload_length | tmp;}if (frame.payload_length == 0){break;}if (frame.mask){if (index + 4 >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index + 3, all_data_len);break;}frame.masking_key = {(uint8_t)data[index], (uint8_t)data[index + 1], (uint8_t)data[index + 2], (uint8_t)data[index + 3]};index += 4;}// payload data [data+index,data+index+frame.payload_length]if (index >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index, all_data_len);break;}if (index - 1 + frame.payload_length >= all_data_len){LOG_ERROR("index - 1 + frame.payload_length=[%llu] >= all_data_len[%llu]", index - 1 + frame.payload_length, all_data_len);break;}std::string payload_data(data + index, frame.payload_length);if (frame.mask){for (size_t i = 0; i < payload_data.size(); ++i){payload_data[i] ^= frame.masking_key[i % 4];}}frame.payload_data = std::move(payload_data);// broadcastsingleton<connection_mgr>::instance()->for_each([&frame](connection::connection &conn) -> void{websocket_connection *ptr_conn = static_cast<websocket_connection *>(&conn);websocket_app::send_packet(*ptr_conn, frame.payload_data.c_str(), frame.payload_length, false);});// websocket_app::send_packet(m_websocket_connection, frame.payload_data.c_str(), frame.payload_length, false);// frame.payload_data.push_back(0);// LOG_ERROR("%s", frame.payload_data.c_str());m_websocket_connection.m_recv_buffer.read_ptr_move_n(index - start_index + frame.payload_length);index += frame.payload_length;}delete[] data;
}void websocket_app::on_close_connection(tubekit::connection::websocket_connection &m_websocket_connection)
{LOG_ERROR("on_close_connection");
}void websocket_app::on_new_connection(tubekit::connection::websocket_connection &m_websocket_connection)
{LOG_ERROR("on_new_connection");
}bool websocket_app::send_packet(tubekit::connection::websocket_connection &m_websocket_connection, const char *data, size_t data_len, bool use_safe)
{if (!data){return false;}uint8_t opcode = 0x81;size_t message_length = data_len;std::vector<uint8_t> frame;frame.push_back(opcode);if (message_length <= 125){frame.push_back(static_cast<uint8_t>(message_length));}else if (message_length <= 0xFFFF){frame.push_back(126);frame.push_back((message_length >> 8) & 0xFF);frame.push_back(message_length & 0xFF);}else{frame.push_back(127);for (int i = 7; i >= 0; --i){frame.push_back((message_length >> (8 * i)) & 0xFF);}}frame.insert(frame.end(), data, data + data_len);if (!use_safe){return m_websocket_connection.send((const char *)frame.data(), frame.size());}return singleton<connection_mgr>::instance()->safe_send(m_websocket_connection.get_socket_ptr(), (const char *)frame.data(), frame.size());
}

相关文章:

C++服务器 支持http、tcp protobuf、websocket,linux开源框架 零依赖轻松编译部署 Reactor

开源地址: https://github.com/crust-hub/tubekit/tree/main Github:https://github.com/gaowanlu 诚招有兴趣的小伙伴加入开发维护 Tubekit The C TCP server framework based on the Reactor model continues to implement POSIX thread pool, Epoll, non blocking IO, obj…...

1688API接口系列,1688开放平台接口使用方案(商品详情数据+搜索商品列表+商家订单类)

1688商品详情接口是指1688平台提供的API接口&#xff0c;用于获取商品详情信息。通过该接口&#xff0c;您可以获取到商品的详细信息&#xff0c;包括商品标题、价格、库存、描述、图片等。 要使用1688商品详情接口&#xff0c;您需要先申请1688的API权限&#xff0c;并获取ac…...

CentOS服务器网页版Rstudio-server及R包批量安装最佳实践

CentOS服务器安装网页版Rstudio-server及R包批量安装 以下为CentOS 7/8的Rstudio-server安装、配置和R包安装操作 1. 软件包安装 Centos 7安装 # 下载安装包&#xff0c;大小115.14 MB wget -c https://download2.rstudio.org/server/centos7/x86_64/rstudio-server-rhel-…...

centos7内核升级(k8s基础篇)

1.查看系统内核版本信息 uname -r 2.升级内核 2.1更新yum源仓库 yum -y update更新完成后&#xff0c;启用 ELRepo 仓库并安装ELRepo仓库的yum源 ELRepo 仓库是基于社区的用于企业级 Linux 仓库&#xff0c;提供对 RedHat Enterprise (RHEL) 和 其他基于 RHEL的 Linux 发行…...

数据结构与算法设计分析——NP完全理论

目录 一、P类问题与NP类问题的定义二、常见的NP类问题&#xff08;一&#xff09;旅行商问题&#xff08;TSP&#xff09;&#xff08;二&#xff09;哈密尔顿回路问题&#xff08;三&#xff09;判断回路问题&#xff08;四&#xff09;图的着色问题&#xff08;五&#xff09…...

AGNES层次聚类

已知数据集D中有9个数据点&#xff0c;分别是(1,2)&#xff0c;(2&#xff0c;3)&#xff0c;(2,1), (3,1),(2,4),(3,5),(4,3),(1,5),(4,2)。要求&#xff1a; (1)采用层次聚类的聚集算法进行聚类&#xff0c;k2。 (2)距离计算采用欧几里得距离。 (3)簇之间的距离采用单链接方…...

HCIP —— 双点重发布 + 路由策略 实验

目录 实验拓扑&#xff1a; 实验要求&#xff1a; 实验配置&#xff1a; 1.配置IP地址 2.配置动态路由协议 —— RIP 、 OSPF R1 RIP R4 OSPF R2 配置RIP、OSPF 双向重发布 R3配置RIP、OSPF 双向重发布 3.查询路由表学习情况 4.使用路由策略控制选路 R2 R3 5.检…...

Python标准库:datetime模块【侯小啾python领航班系列(二十五)】

Python标准库:datetime模块【侯小啾python领航班系列(二十五)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ…...

新版idea如何开启多台JVM虚拟机

1.看看自己的项目 2.可能开始的时候啥也没有&#xff0c;就点Run Configuration Type 3.再点击Edit Configurations... 4.点击号添加SpringBoot 5.主类选择一下&#xff0c;一般就一个&#xff0c;点他选了就行。 6.然后点击Modify Options 选择添加add VM Options 7.点击appl…...

软件工程单选多选补充

2. 4. 5. 6. 7. 8. 9. 10. 12。 13....

6-66.时间

本题要求输入小时、分钟和秒数&#xff0c;并将其输出。针对时间表示中出现的异常进行处理。例如小时数不应超过23&#xff0c;分钟不应超过59&#xff0c;秒数不应超过59。此外&#xff0c;以上三个变量均应大于等于0。 输入样例&#xff1a; 在这里给出三组输入。例如&…...

面试多线程八股文十问十答第一期

面试多线程八股文十问十答第一期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1.ThreadLocal如何实现线程安全 Java的ThreadLocal是一个线程本地变量&#xff0…...

Mybatis 操作续集(结合上文)

当我们增加一个数据之后,如果我们想要获取它的 Id 进行别的操作,我们该如何获取 Id 呢? 用那个Options package com.example.mybatisdemo.mapper;import com.example.mybatisdemo.model.UserInfo; import org.apache.ibatis.annotations.*;import java.util.List;Mapper pub…...

JVM基础篇:垃圾回收

目录 1.前言 1.1C/C的内存管理 1.2Java的内存管理 2.方法区的回收 3.堆回收 3.1引用计数法和可达性分析法 3.2五种对象引用 强引用 软引用 弱引用 虚引用 终结器引用 3.3垃圾回收算法评价标准 ①吞吐量 ②最大暂停时间 ③堆使用效率 3.4垃圾回收算法 ①标记清…...

蓝桥杯ACwing习题

题目 &#xff1a;https://www.acwing.com/problem/content/4409/ 解析 &#xff1a;根据题目我们可以知道 问的是方案数 那么首先就想到了 dp 仔细想一下 发现类似于蒙德里安的梦想那道状态压缩的题 &#xff0c; 所以我们先考虑怎么定义 f[i][j] f[i][j] 表示的是 已经放了…...

vue发送请求携带token,拼接url地址下载文件

封装请求 &#xff0c;该请求为普通的get请求 该请求返回值为&#xff1a; 请求成功之后拼接URL地址下载文件 代码块 downTemplateRequest(activeKeys.value).then((res) > {let url http://47.169.168.99:18888/media/${res.data.name};var elink document.createElemen…...

【PTA-C语言】编程练习3 - 循环结构Ⅱ

如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 编程练习3 - 循环结构&#xff08;9~15&#xff09; 7-9 特殊a串数列求和&#xff08;分数 15&#xff09;7-10 穷举法搬运砖块问题&#xff08;分数 15&#xff09;7-11 数字金字塔&#xff08;分数 15&…...

Google Chrome 下载 (离线版)

1 访问网址 Google Chrome 网络浏览器 2 点击 下载Chrome 3 直接运行 ChromeStandaloneSetup64.exe 其他&#xff1a; ####################### 谷歌浏览器 (Google Chrome) 最新版离线安装包下载 https://www.iplaysoft.com/tools/chrome/#google_vignette Google Chrome …...

2023年GopherChina大会-核心PPT资料下载

一、峰会简介 自 Go 语言诞生以来&#xff0c;中国便是其应用最早和最广的国家之一&#xff0c;根据 Jetbrains 在 2021 年初做的调查报告&#xff0c;总体来说目前大概有 110 万专业的开发者 选择 Go 作为其主要开发语言。就其全球分布而言, 居住在亚洲的开发者最多&#xff…...

从源代码出发,Jenkins 任务排队时间过长问题的解决过程

最近开发了一个部署相关的工具&#xff0c;使用 Jenkins 来构建应用。Jenkins 的任务从模板中创建而来。每次部署时&#xff0c;通过 Jenkins API 来触发构建任务。在线上运行时发现&#xff0c;通过 API 触发的 Jenkins 任务总是会时不时在队列中等待较长的时间。某些情况下的…...

openssl 生成CA及相关证书

实验环境&#xff1a;ubuntu18.04-desktop 获取openssl.cnf配置文件 # 这个返回的路径&#xff0c;不一定被使用了&#xff08;经测试&#xff0c;ubuntu18下的openssl似乎未加载任何配置文件&#xff09; openssl version -d生成私钥文件(pem) # 生成私钥 # genrsa&#xf…...

App测试之App日志收集及adb常用命令

文章目录 前言一、adb是什么1.APP测试收集手机日志常用的工具2.adb下载与安装3.ADT/SDK/ADB是什么4.adb连接真机 二、adb常用命令三、android系统日志文件1.logcat日志文件2.logcat日志文件分析 四、分析crash & ANR 日志1.发生crash如何分析2.发生ANR如何分析 总结扩展&am…...

【Java面试——并发基础、并发关键字】

3.1 并发基础 Java 并发 - 理论基础Java 并发 - 线程基础 多线程的出现是要解决什么问题的? 本质什么? CPU、内存、I/O 设备的速度是有极大差异的&#xff0c;为了合理利用 CPU 的高性能&#xff0c;平衡这三者的速度差异&#xff0c;计算机体系结构、操作系统、编译程序都…...

如何使用 Java 在Excel中创建下拉列表

下拉列表&#xff08;下拉框&#xff09;可以确保用户仅从预先给定的选项中进行选择&#xff0c;这样不仅能减少数据输入错误&#xff0c;还能节省时间提高效率。在MS Excel中&#xff0c;我们可以通过 “数据验证” 提供的选项来创建下拉列表&#xff0c;但如果要在Java程序中…...

Python安装步骤介绍

本文将介绍Python安装的详细步骤如下&#xff1a; 下载 python安装 python配置环境变量&#xff08;安装时勾选配置环境变量的则无需此步骤&#xff09; 一、python下载 官网&#xff1a;Download Python | Python.org 根据电脑位数下载所需的版本 二、Python安装 1.打开安…...

学习80min快速了解大型语言模型(ChatGPT使用)笔记

学习李宏毅&#xff1a;80min快速了解大型语言模型&#xff08;ChatGPT使用&#xff09;笔记 链接&#xff1a;https://www.youtube.com/watch?vwG8-IUtqu-s 1、创建一个属于自己的GPT 目前&#xff0c;GPT4具备一个功能&#xff0c;Create a GPT。利用这个功能可以创建一个…...

SQL错题集1

1.找出选修课程成绩最差的选课记录 注&#xff1a; 聚合函数只能用在group by和&#xff08;&#xff09;括号中 找最值可用排序order bylimit 1 2. 查询选修成绩 合格的课程 超过2门的 学生编号 3.删除姓名为"LiMing"的学生信息 注&#xff1a; 删除一整行信息&…...

uniapp运行到安卓基座app/img标签不显示

img是html中的标签&#xff0c;他也是一个单标签 image属于服务器控件&#xff0c;是个双标签 问题&#xff1a;uniapp运行到app安卓基座后图片无法显示 原因&#xff1a;自己使用了img标签&#xff0c;而且输入路径无提示&#xff0c;img标签导致图片不显示 解决&#xff…...

vscode非常好用的扩展插件

1、Code Spell Checker&#xff1a; 帮助我们检查单词是否拼写错误&#xff0c;检查规则遵循驼峰拼写法。 2、Color Highlight&#xff1a;高亮显示颜色值 3、Svg Preview&#xff1a; 实时预览svg图片&#xff08;修改width、height、fill等值来实时查看效果&#xff09; 4、…...

一文弄懂BFS【广度优先搜索(Breadth-First Search)】

BFS&#xff0c;全名为广度优先搜索(Breadth-First Search)&#xff0c;是一种用于图或树的遍历或搜索的算法。它的主要思想是由节点自身开始向它的邻居节点新进展开搜索&#xff0c;因此也常被形象地称为“层序遍历”。 BFS 基本思想 BFS 工作原理是&#xff0c;从开始节点开…...