当前位置: 首页 > 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. 设置脚本权限…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...