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

【Linux编程】TcpServer 类的设计与实现:构建高性能的 TCP 服务器(二)

TcpServer 类的设计与实现:构建高性能的 TCP 服务器

在现代网络编程中,构建一个高效、稳定的 TCP 服务器是许多网络应用的基础。本文将详细介绍一个基于 C++ 的 TcpServer 类的设计与实现,该类提供了创建 TCP 服务器、处理客户端连接、数据传输和接收等功能。通过这个类,我们可以更容易地理解和实现 TCP 通信的细节。

1. TcpServer 类概述

TcpServer 类是一个用于创建和管理 TCP 服务器的类。它封装了套接字创建、绑定、监听、客户端连接处理、数据发送和接收等操作,使得网络通信更加简洁和易于管理。

2. 类构造与析构
  • 构造函数 TcpServer::TcpServer(int _port)TcpServer::TcpServer(std::string _host, int _port) 初始化服务器的主机地址和端口,并创建套接字。
TcpServer::TcpServer(int _port) : TcpServer("0.0.0.0", _port) {
}TcpServer::TcpServer(std::string _host, int _port) : host(_host), port(_port) {std::cout << "create tcp server start." << std::endl;socket_fd = socket(AF_INET, SOCK_STREAM, 0);if (socket_fd == -1) {std::cout << "socket create error!";return;}int ret = set_epoll_mode(socket_fd, O_NONBLOCK);if (ret < 0) {std::cout << "epoll_mode failed:" << ret << std::endl;close(socket_fd);return;}server_addr.sin_family = AF_INET;inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr);server_addr.sin_port = htons(port);int opt = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));isCreate = true;std::cout << "create tcp server ok." << std::endl;
}
  • 析构函数 TcpServer::~TcpServer() 虚析构函数,确保派生类的析构函数被正确调用。
TcpServer::~TcpServer() {
}
3. 服务器启动与停止
  • 启动服务器 TcpServer::Start() 绑定套接字到指定端口,并开始监听。
void TcpServer::Start() {if (running || !isCreate) {std::cout << "TcpServer start failed!" << "running=" << running << ", port=" << port << std::endl;return;}auto ret = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (ret == -1) {std::cout << "bind faild:" << ret << std::endl;close(socket_fd);return;}ret = listen(socket_fd, SOMAXCONN);if (ret == -1) {std::cout << "Listen failed:" << ret << std::endl;close(socket_fd);return;}std::cout << "server open: " << host << ":" << port << std::endl;epoll_start();
}
  • 停止服务器 TcpServer::Stop() 关闭服务器并释放资源。
void TcpServer::Stop() {this->Close();
}
4. 资源管理
  • 关闭连接 TcpServer::Close() 关闭套接字和 epoll 文件描述符,释放资源。
void TcpServer::Close() {isCreate = false;running = false;socket_event.data.fd = socket_fd;int ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, &socket_event);ret = close(socket_fd);std::cout << "socket_fd已关闭:" << ret << std::endl;for (TcpClient *client : clients)client->Close();ret = close(epoll_fd);std::cout << "TcpServer epoll_fd已关闭:" << ret << std::endl;
}
5. 非阻塞模式设置
  • 设置非阻塞模式 TcpServer::set_epoll_mode() 设置套接字为非阻塞模式。
int TcpServer::set_epoll_mode(int sock_fd, int mode) {int flags = fcntl(sock_fd, F_GETFL, 0);if (flags == -1) {std::cout << "epoll_mode failed:" << sock_fd << std::endl;return -1;}return fcntl(sock_fd, F_SETFL, flags | mode);
}
6. 客户端连接处理
  • 客户端接受线程 TcpServer::client_accept_thread() 处理客户端连接和数据事件。
void TcpServer::client_accept_thread() {struct epoll_event client_events[1024];while (running) {int ret = epoll_wait(epoll_fd, client_events, 1024, -1);if (ret < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK)continue;else {std::cerr << "epoll_wait failed: " << ret << " : " << errno << " : " << strerror(errno) << std::endl;break;}}for (int n = 0; n < ret; ++n) {if (client_events[n].data.fd == socket_fd) {client_connect();} else {struct epoll_event client_event = client_events[n];auto client = std::find_if(clients.begin(), clients.end(), [&client_event](const TcpClient *_client){ return (_client->client_fd == client_event.data.fd); });if (client == clients.end())continue;int ret = (*client)->data_receive(*client);if (ret == 0) {clients.erase(client);delete *client;}}}}std::cout << "服务已关闭,不再提供任何服务!" << std::endl;isDispose = true;
}
  • 客户端连接 TcpServer::client_connect() 接受客户端连接并添加到 epoll 监控。
void TcpServer::client_connect() {struct sockaddr_in client_addr;socklen_t addr_len = sizeof(client_addr);int client_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &addr_len);std::cout << "accept:" << client_fd << " : " << strerror(errno) << std::endl;if (client_fd <= 0)return;TcpClient *client = new TcpClient;client->running = true;client->connected = true;client->client_fd = client_fd;client->local_addr = client_addr;int ret = set_epoll_mode(client_fd, O_NONBLOCK);if (ret == -1) {std::cout << "服务器接受客户端-set_epoll_mode failed:" << strerror(errno) << std::endl;client->Close();return;}client->add_epoll_event(client_fd, epoll_fd, EPOLLIN | EPOLLET);client->DataReceived = [this](void *sender, DataReceiveEventArgs *e){DataReceived.Invoke(this, e);};client->recv_data = new char[client->recv_data_length];clients.push_back(client);std::cout << "新的客户端已接入:" << inet_ntoa(client_addr.sin_addr) << ":" << htons(client_addr.sin_port) << std::endl;
}
7. epoll 事件处理
  • 启动 epoll TcpServer::epoll_start() 创建 epoll 实例并添加监听套接字。
void TcpServer::epoll_start() {epoll_fd = epoll_create1(0);if (epoll_fd == -1) {std::cout << "poll_create1 failed:" << epoll_fd << std::endl;close(socket_fd);return;}socket_event.events = EPOLLIN;socket_event.data.fd = socket_fd;int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &socket_event);if (ret == -1) {std::cout << "epoll_ctl failed:" << ret << std::endl;close(epoll_fd);return;}running = true;std::thread th = std::thread(&TcpServer::client_accept_thread, this);th.detach();
}

完整的代码:
TcpServer.h 头文件

#pragma once#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/fcntl.h>
#include <arpa/inet.h>
#include <algorithm>
#include <unistd.h>
#include <thread>
#include "DataReceiveEventArgs.h"
#include "TcpClient.h"class TcpServer
{
public:TcpServer(int _port);TcpServer(std::string _host, int _port);~TcpServer();public:EventHandler<DataReceiveEventArgs> DataReceived;public:void Start();void Stop();void Close();bool IsDispose();private:void epoll_start();                        // epoll初始化(创建epoll)int set_epoll_mode(int sock_fd, int mode); // epoll模式--创建socket时,为非阻塞模式void client_accept_thread();void client_connect();sockaddr_in get_remote_addr(int sock);private:int socket_fd;int epoll_fd;std::string host = "0.0.0.0";int port = 0;bool running = false;bool isCreate = false;int send_buff_size = 1024 * 1024;int recv_buff_size = 1024 * 1024;struct sockaddr_in server_addr;std::list<TcpClient *> clients;// 将监听套接字添加到 epoll 中,监控 EPOLLIN (表示有数据可读)事件struct epoll_event socket_event;bool isDispose = false;
};

TcpServer.cpp

#include "TcpServer.h"TcpServer::TcpServer(int _port) : TcpServer("0.0.0.0", _port)
{
}TcpServer::TcpServer(std::string _host, int _port) : host(_host), port(_port)
{std::cout << "create tcp server start." << std::endl;// AF_INET 表示使用 IPv4 协议// SOCK_STREAM 表示套接字的类型,表示 面向连接的流式套接字socket_fd = socket(AF_INET, SOCK_STREAM, 0);// std::cout << "create tcp server socket_fd:" << socket_fd << std::endl;if (socket_fd == -1){std::cout << "socket ceate error!";return;}// 文件描述符为非阻塞模式int ret = set_epoll_mode(socket_fd, O_NONBLOCK);if (ret < 0){std::cout << "epoll_mode failed:" << ret << std::endl;close(socket_fd);return;}server_addr.sin_family = AF_INET;// ip字符串转intinet_pton(AF_INET, host.c_str(), &server_addr.sin_addr);//server_addr.sin_addr.s_addr = ntohl(server_addr.sin_addr.s_addr);server_addr.sin_port = htons(port);// 以下设置表示当调用close关闭客户端时,立即释放端口,不等待// 在TCP服务端,客户端调用 close(client_fd) 关闭连接后,如果你尝试重新连接时出现端口没有完全释放的情况,通常是由于 TCP 连接的 TIME_WAIT 状态没有及时清理。这是 TCP 协议的正常行为。// 在TCP连接关闭后,端口会进入 TIME_WAIT 状态。这个状态的目的是确保最后的数据包能够正确到达。如果新的连接尝试在该端口上进行,而该端口仍然处于 TIME_WAIT 状态,就会出现端口被占用的情况。// TIME_WAIT 状态通常会持续一段时间(默认是4分钟,即240秒),这可以通过操作系统的内核参数来修改。// struct linger linger_opt;// linger_opt.l_onoff = 1;  // 启用// linger_opt.l_linger = 1; // 立即关闭// // 此方法亲测无效// // setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt));int opt = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));isCreate = true;std::cout << "create tcp server ok." << std::endl;
}TcpServer::~TcpServer()
{
}void TcpServer::Start()
{if (running || !isCreate){std::cout << "TcpServer start failed!" << "running=" << running << ", port=" << port << std::endl;// close(socket_fd);return;}// 绑定套接字到指定端口auto ret = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (ret == -1){std::cout << "bind faild:" << ret << std::endl;close(socket_fd);return;}// 开始监听ret = listen(socket_fd, SOMAXCONN);if (ret == -1){std::cout << "Listen failed:" << ret << std::endl;close(socket_fd);return;}std::cout << "server open: " << host << ":" << port << std::endl;epoll_start();
}void TcpServer::Stop()
{this->Close();
}void TcpServer::Close()
{isCreate = false;running = false;// 1. 删除socket_fd的epoll事件socket_event.data.fd = socket_fd;int ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, &socket_event);// 2. 关闭监听套接字ret = close(socket_fd);std::cout << "socket_fd已关闭:" << ret << std::endl;// 3. 从 epoll 中移除所有连接的客户端文件描述符for (TcpClient *client : clients)client->Close();// 4. 关闭 epoll 文件描述符ret = close(epoll_fd);std::cout << "TcpServer epoll_fd已关闭:" << ret << std::endl;
}bool TcpServer::IsDispose()
{return isDispose;
}int TcpServer::set_epoll_mode(int sock_fd, int mode)
{/*O_NONBLOCK(非阻塞模式):如果设置了这个标志,表示该套接字(或文件)是非阻塞的,执行读写操作时不会阻塞调用进程或线程。套接字在没有数据可读或可写时不会让程序等待,而是立即返回。O_RDWR、O_WRONLY、O_RDONLY(访问模式):表示套接字的打开方式。O_APPEND(追加模式):指示文件或套接字在写操作时会追加数据。*/int flags = fcntl(sock_fd, F_GETFL, 0); // 获取当前套接字的文件状态标志if (flags == -1){std::cout << "epoll_mode failed:" << sock_fd << std::endl;return -1;}// 设置套接字为非阻塞模式return fcntl(sock_fd, F_SETFL, flags | mode);
}void TcpServer::client_accept_thread()
{struct epoll_event client_events[1024];while (running){// 阻塞等待事件int ret = epoll_wait(epoll_fd, client_events, 1024, -1);if (ret < 0){if (errno == EAGAIN || errno == EWOULDBLOCK)continue;else{std::cerr << "epoll_wait failed: " << ret << " : " << errno << " : " << strerror(errno) << std::endl;break;}}// 处理返回的事件for (int n = 0; n < ret; ++n){if (client_events[n].data.fd == socket_fd){// 如果是监听套接字的事件,有新的客户端连接client_connect();}else{struct epoll_event client_event = client_events[n];auto client = std::find_if(clients.begin(), clients.end(), [&client_event](const TcpClient *_client){ return (_client->client_fd == client_event.data.fd); });if (client == clients.end())continue;// 客户端有数据int ret = (*client)->data_receive(*client);if (ret == 0){clients.erase(client);delete *client;}}}}std::cout << "服务已关闭,不再提供任何服务!" << std::endl;isDispose = true;
}void TcpServer::client_connect()
{struct sockaddr_in client_addr;socklen_t addr_len = sizeof(client_addr);int client_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &addr_len); // 接受连接std::cout << "accept:" << client_fd << " : " << strerror(errno) << std::endl;if (client_fd <= 0)return;TcpClient *client = new TcpClient;client->running = true;client->connected = true;client->client_fd = client_fd;client->local_addr = client_addr;// 设置客户端套接字为非阻塞模式int ret = set_epoll_mode(client_fd, O_NONBLOCK);if (ret == -1){std::cout << "服务器接受客户端-set_epoll_mode failed:" << strerror(errno) << std::endl;client->Close();return;}// client.SetSendBuffSize(send_buff_size);// client.SetRecvBuffSize(recv_buff_size);//  将新客户端套接字添加到 epoll 中,监听可读事件// client->create_epoll();client->add_epoll_event(client_fd, epoll_fd, EPOLLIN | EPOLLET);client->DataReceived = [this](void *sender, DataReceiveEventArgs *e){DataReceived.Invoke(this, e);};// client->start_receive();client->recv_data = new char[client->recv_data_length];clients.push_back(client);std::cout << "新的客户端已接入:" << inet_ntoa(client_addr.sin_addr) << ":" << htons(client_addr.sin_port) << std::endl;
}void TcpServer::epoll_start()
{// 创建epollepoll_fd = epoll_create1(0);if (epoll_fd == -1){std::cout << "poll_create1 failed:" << epoll_fd << std::endl;close(socket_fd);return;}// 监听可读事件socket_event.events = EPOLLIN;// 将监听套接字的文件描述符传给 epollsocket_event.data.fd = socket_fd;// 将监听套接字添加到 epoll 中,监控 EPOLLIN 事件(表示有数据可读)int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &socket_event);if (ret == -1){std::cout << "epoll_ctl failed:" << ret << std::endl;close(epoll_fd);return;}running = true;std::thread th = std::thread(&TcpServer::client_accept_thread, this);th.detach();
}
8. 总结

本文详细介绍了 TcpServer 类的设计与实现,包括构造与析构、服务器启动与停止、资源管理、非阻塞模式设置、客户端连接处理以及 epoll 事件处理。通过这个类,我们可以更容易地理解和实现 TCP 通信的细节。这个类提供了一个简洁的接口来管理 TCP 服务器,使得网络编程更加高效和易于维护。

TcpServer 类的设计注重模块化和可扩展性,允许开发者根据具体需求进行定制和扩展。通过使用 epoll 事件模型,TcpServer 类能够支持高并发的客户端连接,适用于需要处理大量并发连接的网络应用。此外,类中的非阻塞模式设置和资源管理机制确保了服务器的稳定性和高效性。

总的来说,TcpServer 类为构建高性能的 TCP 服务器提供了一个强大的基础。通过这个类,开发者可以快速构建和部署 TCP 服务器,满足各种网络应用的需求。

相关文章:

【Linux编程】TcpServer 类的设计与实现:构建高性能的 TCP 服务器(二)

TcpServer 类的设计与实现&#xff1a;构建高性能的 TCP 服务器 在现代网络编程中&#xff0c;构建一个高效、稳定的 TCP 服务器是许多网络应用的基础。本文将详细介绍一个基于 C 的 TcpServer 类的设计与实现&#xff0c;该类提供了创建 TCP 服务器、处理客户端连接、数据传输…...

Mono里运行C#脚本8—mono_image_storage_open打开EXE文件

Mono里运行C#脚本8—mono_image_storage_open打开EXE文件 前面分析哈希表的实现,以及文件打开的底层函数,还有保存到HASH表里的数据结构。 static MonoImageStorage * mono_image_storage_open (const char *fname) { char *key = NULL; key = mono_path_resolve_symlinks…...

XMLHttpRequest的基础知识

get请求 const xml new XMLHttpRequest(); xml.open("GET", "https://jsonplaceholder.typicode.com/todos/1", true); xml.onreadystatechange function () {if (xml.readyState 4 && xml.status 200) {console.log(xml.responseText);} }…...

力扣矩阵-算法模版总结

lc-73.矩阵置零-(时隔14天)-12.27 思路&#xff1a;(23min22s) 1.直接遍历遇0将行列设0肯定不行&#xff0c;会影响后续判断&#xff0c;题目又要求原地算法&#xff0c;那么进一步考虑是否可以将元素为0&#xff0c;其行列需要设为0的位置给存储下来&#xff0c;最后再遍历根据…...

如何在短时间内读懂复杂的英文文献?

当我们拿起一篇文献开始阅读时&#xff0c;就像是打开了一扇通往未知世界的大门。但别急着一头扎进去&#xff0c;咱们得像个侦探一样&#xff0c;带着疑问去探险。毕竟&#xff0c;知识的海洋深不可测&#xff0c;不带点“装备”怎么行&#xff1f;今天就聊聊&#xff0c;平时…...

基于aspose.words组件的word bytes转pdf bytes,去除水印和解决linux中文乱码问题

详情见 https://preferdoor.top/archives/ji-yu-aspose.wordszu-jian-de-word-byteszhuan-pdf-bytes...

Bert中文文本分类

这是一个经典的文本分类问题&#xff0c;使用google的预训练模型BERT中文版bert-base-chinese来做中文文本分类。可以先在Huggingface上下载预训练模型备用。https://huggingface.co/google-bert/bert-base-chinese/tree/main 我使用的训练环境是 pip install torch2.0.0; pi…...

【深度学习】Java DL4J基于 CNN 构建车辆识别与跟踪模型

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

【C#】C#打印当前时间以及TimeSpan()介绍

1. C#打印当前时间 string currentDate DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");Console.WriteLine(currentDate);2. TimeSpan()介绍 TimeSpan(long ticks)的单位是100ns //500ms new TimeSpan(10*1000*500);参考&#xff1a; C#-TimeSpan-计算时间差...

【Linux 网络 (五)】Tcp/Udp协议

Linux 网络 一前言二、Udp协议1&#xff09;、Udp协议特点2&#xff09;、Udp协议格式3&#xff09;、Udp报文封装和解包过程4&#xff09;、UDP的缓冲区 三、TCP协议1&#xff09;、TCP协议特点2&#xff09;、TCP协议格式1、4位首部长度、源端口、目的端口2、16位窗口大小3、…...

多旋翼无人机理论 | 四旋翼动力学数学模型与Matlab仿真

多旋翼无人机理论 | 四旋翼动力学数学模型与Matlab仿真 力的来源数学模型数学模型总结Matlab 仿真 力的来源 无人机的动力系统&#xff1a;电调-电机-螺旋桨 。 给人最直观的感受就是 电机带动螺旋桨转&#xff0c;产生升力。 螺旋桨旋转产生升力的原因&#xff0c;在很多年…...

Vue3项目中引入TailwindCSS(图文详情)

Vue3项目中引入TailwindCSS&#xff08;图文详细&#xff09; Tailwind CSS 是一个实用工具优先的 CSS 框架&#xff0c;提供丰富的低级类&#xff08;如 text-center、bg-blue-500&#xff09;&#xff0c;允许开发者通过组合这些类快速构建自定义设计&#xff0c;而无需编写…...

【开源项目】数字孪生化工厂—开源工程及源码

飞渡科技数字孪生化工厂管理平台&#xff0c;基于自研孪生引擎&#xff0c;将物联网IOT、人工智能、大数据、云计算等技术应用于化工厂&#xff0c;为化工厂提供实时数据分析、工艺优化、设备运维等功能&#xff0c;助力提高生产效率以及提供安全保障。 通过可视化点位标注各厂…...

咨询团队如何通过轻量型工具优化项目管理和提高团队协作效率?

引言 在咨询行业&#xff0c;项目的复杂性和多样性往往意味着团队成员需要协同工作、迅速适应客户需求的变化并且在较短的时间内交付高质量的成果。对于咨询团队来说&#xff0c;选择一个适合的项目管理工具&#xff0c;不仅能够提高工作效率&#xff0c;还能促进团队的协作、…...

javaWeb开发

Java Web开发作为软件开发领域的一个重要分支&#xff0c;已经历经数十年的发展&#xff0c;并凭借其强大的跨平台能力、丰富的生态系统以及高度的安全性&#xff0c;成为构建企业级应用的首选技术之一。以下是对Java Web开发的详细解析&#xff1a; 一、Java Web开发的基本概…...

如何在 Vue 中处理 API 请求?

在 Vue.js 中处理 API 请求是构建动态、交互式 Web 应用程序的核心部分。为了有效地与后端服务器通信&#xff0c;Vue 生态系统提供了多种方式来发起和管理 API 请求。以下是几种常见的方法和最佳实践&#xff1a; 1. 使用 Axios Axios 是一个基于 Promise 的 HTTP 客户端&am…...

基于Debian的Linux发行版的包管理工具

基于Debian的Linux发行版中除了apt和apt-get之外&#xff0c;还有以下几种包管理工具&#xff1a; dpkg&#xff1a;这是Debian系发行版中最基础的包管理工具&#xff0c;专门用于安装、卸载和查询.deb包。与高级包管理器不同&#xff0c;dpkg不自动解决包的依赖关系&#xff0…...

2022年国家公考《申论》题(行政执法)

2022年国家公考《申论》题&#xff08;行政执法&#xff09; 材料一 新型冠状病毒肺炎疫情发生后&#xff0c;党中央、国务院出台了一系列支持企业发展的惠企政策。N市积极落实各项惠企政策&#xff0c;不断优化营商环境&#xff0c;推动区域经济高质量跨越式发展。   “当时…...

贪心算法(常见贪心模型)

常见贪心模型 简单排序模型 最小化战斗力差距 题目分析&#xff1a; #include <bits/stdc.h> using namespace std;const int N 1e5 10;int n; int a[N];int main() {// 请在此输入您的代码cin >> n;for (int i 1;i < n;i) cin >> a[i];sort(a1,a1n);…...

git自动压缩提交的脚本

可以将当前未提交的代码自动执行 git addgit commitgit squash Git 命令安装指南 1. 创建脚本目录 如果目录不存在&#xff0c;创建它&#xff1a; mkdir -p ~/.local/bin2. 创建脚本文件 vim ~/.local/bin/git-squash将完整的脚本代码复制到此文件中。 3. 设置脚本权限…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

前端调试HTTP状态码

1xx&#xff08;信息类状态码&#xff09; 这类状态码表示临时响应&#xff0c;需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分&#xff0c;客户端应继续发送剩余部分。 2xx&#xff08;成功类状态码&#xff09; 表示请求已成功被服务器接收、理解并处…...

相关类相关的可视化图像总结

目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系&#xff0c;可直观判断线性相关、非线性相关或无相关关系&#xff0c;点的分布密…...