【boost网络库从青铜到王者】第七篇:asio网络编程中的异步echo服务器,以应答为主

文章目录

  • 1、简介
  • 2、echo模式应答异步服务器
      • 2.1、Session会话类
      • 2.2、Server类为服务器接收连接的管理类
  • 3、客户端
  • 4、隐患
  • 5、总结

1、简介

前文已经介绍了异步操作的api,今天写一个简单的异步echo服务器,以应答为主。

2、echo模式应答异步服务器

2.1、Session会话类

Session类主要是处理客户端消息接收发送的会话类,为了简单起见,我们不考虑粘包问题,也不考虑支持手动调用发送的接口,只以应答的方式发送和接收固定长度 (1024字节长度) 的数据。

“session.h” :

#pragma once
#include<iostream>
#include<string>
#include<boost/asio.hpp>class Session
{
public:Session(boost::asio::io_context& ioc);
public:boost::asio::ip::tcp::socket& GetSocket();void Start();protected://接收数据回调 tcp接收缓冲区有数据void HandlerRead(const boost::system::error_code& err, size_t bytes_transferred);//发送数据回调 tcp发送缓冲区有空闲空间,就会从用户缓冲区拷贝到tcp发送缓冲区,然后发送数据void HandleSend(const boost::system::error_code& err);
protected:enum {max_length = 1024};//数组接收数据char data_[max_length];private:boost::asio::ip::tcp::socket socket_;
};

这段代码是一个使用Boost.Asio库实现的 C++ 类定义,用于网络通信会话。您解释这段代码:

  • 头文件包含:
    • 这些行包括了必要的头文件。#pragma once 是一个预处理指令,确保被包含的头文件只会被包含一次,防止多次包含和潜在的问题。
#pragma once
#include<iostream>
#include<string>
#include<boost/asio.hpp>
  • 类声明:
    • 代码定义了一个名为 SessionC++ 类。它有一个构造函数,接受一个 boost::asio::io_context 对象的引用,并且有两个公共成员函数:GetSocketStart
class Session
{
public:Session(boost::asio::io_context& ioc);
public:boost::asio::ip::tcp::socket& GetSocket();void Start();
protected:// ...
private:// ...
};
  • 公共成员函数:

    • Session(boost::asio::io_context& ioc):这是 Session 类的构造函数。它接受一个 io_context 的引用作为参数,通常用于管理异步I/O操作。

    • boost::asio::ip::tcp::socket& GetSocket():此函数返回一个TCP套接字对象的引用。它可能允许外部代码访问与此会话相关联的套接字。

    • void Start(): 此函数预计启动会话。代码片段没有提供其实际实现,但它可能启动一些用于网络通信的异步操作。

  • 受保护的成员函数:

    • 这些是受保护的成员函数。它们可能被派生类覆盖或在内部用于处理读取和发送操作。

      • HandlerRead(const boost::system::error_code& err, size_t bytes_transferred): 这个函数似乎是用于处理通过TCP连接接收的数据的回调。它接受错误代码和传输的字节数作为参数。

      • HandleSend(const boost::system::error_code& err): 这个函数似乎是用于处理发送数据完成的回调。它也接受一个错误代码作为参数。

protected:void HandlerRead(const boost::system::error_code& err, size_t bytes_transferred);void HandleSend(const boost::system::error_code& err);
  • 私有成员变量:
private:boost::asio::ip::tcp::socket socket_;

这个私有成员变量 socket_TCP套接字的实例,用于会话内的网络通信。

  • 常量:
enum {max_length = 1024
};

这定义了一个名为 max_length 的常量,其值为1024。这很可能是用于接收数据的缓冲区的最大长度。

总的来说,这段代码是一个使用Boost.Asio库的网络会话类的基本轮廓。它提供了管理网络通信所需的结构和组件,但没有在此片段中提供会话行为的实际实现。

“session.cpp” :

#include "Session.h"Session::Session(boost::asio::io_context& io_context):socket_(io_context)
{memset(data_, 0, sizeof(data_));
}boost::asio::ip::tcp::socket& Session::GetSocket() {return socket_;
}void Session::Start() {memset(data_, 0, max_length);socket_.async_read_some(boost::asio::buffer(data_, max_length),std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));
}void Session::HandlerRead(const boost::system::error_code& err, std::size_t bytes_transferred) {if (0 != err.value()) {std::cout << "read data failed!err_code is: " << err.value() << " .message: " << err.what() << std::endl;delete this;}else {std::cout << "receive data is: " << data_ << std::endl;//大部分服务器这样设计全双工通信memset(data_, 0, sizeof(data_));//继续让接收/读数据监听,这样就会造成删除bugsocket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)), std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));socket_.async_send(boost::asio::buffer(data_, max_length),std::bind(&Session::HandleSend, this, std::placeholders::_1));}
}void Session::HandleSend(const boost::system::error_code& err) {if (0 != err.value()) {std::cout << "send data failed!err code is: " << err.value() << " .message: " << err.what() << std::endl;}else {memset(data_, 0, sizeof(data_));//继续让接收/读数据监听socket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)), std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));}}

这是与之前提供的 Session 类相关的实现文件。解释这段代码:

  • 构造函数:
    • 这是 Session 类的构造函数的实现。它接受一个 boost::asio::io_context 的引用作为参数,并使用该上下文初始化了 socket_。此外,它使用 memset 函数将 data_ 数组的内容初始化为零。
Session::Session(boost::asio::io_context& io_context): socket_(io_context)
{memset(data_, 0, sizeof(data_));
}
  • GetSocket 函数:
    • 这个函数返回 socket_ 的引用,允许外部代码访问与会话相关联的TCP套接字。
boost::asio::ip::tcp::socket& Session::GetSocket() {return socket_;
}
  • Start 函数,在Start方法中我们调用异步读操作,监听对端发送的消息。当对端发送数据后,触发HandlerRead回调函数:
    • Start 函数似乎是启动会话的函数。它首先使用 memsetdata_ 数组的内容初始化为零,然后调用 socket_async_read_some 函数,启动异步读取操作。当数据到达时,会调用 HandlerRead 回调函数处理读取的数据。
void Session::Start() {memset(data_, 0, max_length);socket_.async_read_some(boost::asio::buffer(data_, max_length),std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));
}
  • HandlerRead 函数,HandlerRead函数内将收到的数据发送给对端,当发送完成后触发HandleSend回调函数:
    • HandlerRead 函数是用于处理异步读取操作完成时的回调。它检查是否有错误发生,如果有错误,它会输出错误消息并删除会话对象。如果没有错误,它输出接收到的数据,并通过再次调用 async_read_some 来继续监听接收/读取数据的操作。同时,它也启动了异步发送操作,将数据发送回客户端。
void Session::HandlerRead(const boost::system::error_code& err, std::size_t bytes_transferred) {if (0 != err.value()) {std::cout << "read data failed! err_code is: " << err.value() << " . message: " << err.what() << std::endl;delete this;}else {std::cout << "receive data is: " << data_ << std::endl;// 大部分服务器这样设计全双工通信memset(data_, 0, sizeof(data_));// 继续让接收/读数据监听, 这样就会造成删除bugsocket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)),std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));socket_.async_send(boost::asio::buffer(data_, max_length),std::bind(&Session::HandleSend, this, std::placeholders::_1));}
}
  • HandleSend 函数:
    • HandleSend 函数用于处理异步发送操作完成时的回调。它检查是否有错误发生,如果有错误,它会输出错误消息并删除会话对象。如果没有错误,它继续通过再次调用 async_read_some 来监听接收/读取数据的操作。HandleSend函数内又一次监听了读事件,如果对端有数据发送过来则触发HandlerRead,我们再将收到的数据发回去。从而达到应答式服务的效果。
void Session::HandleSend(const boost::system::error_code& err) {if (0 != err.value()) {std::cout << "send data failed! err code is: " << err.value() << " . message: " << err.what() << std::endl;delete this;}else {memset(data_, 0, sizeof(data_));// 继续让接收/读数据监听socket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)),std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));}
}

总的来说,这段代码是 Session 类的实现,它处理了异步的数据读取和发送操作,并提供了一些错误处理和数据清理的逻辑。这是一个基本的网络会话类的实现,用于处理网络通信。

2.2、Server类为服务器接收连接的管理类

Server类为服务器接收连接的管理类。

“server.h":

#pragma once
#include<iostream>
#include<boost/asio.hpp>
#include"Session.h"class Server
{
public:Server(boost::asio::io_context& io_context, int16_t port);
private:void StartAccept();void HandleAccept(Session* session, const boost::system::error_code& err);
private:boost::asio::io_context& io_context_;boost::asio::ip::tcp::acceptor acceptor_;
};

这段代码定义了一个名为 ServerC++类,该类似乎用于创建和管理一个基于Boost.Asio库的TCP服务器。以下是对这段代码的解释:

  • 头文件包含:
    • 这些行包括了必要的头文件。#pragma once 用于确保头文件只被包含一次,避免多次包含的问题。
#pragma once
#include <iostream>
#include <boost/asio.hpp>
#include "Session.h"
  • 类声明:
    • 这段代码定义了一个名为 Server 的**C++**类,该类有一个公共构造函数和两个私有成员函数,以及两个私有成员变量。
class Server
{
public:Server(boost::asio::io_context& io_context, int16_t port);
private:void StartAccept();void HandleAccept(Session* session, const boost::system::error_code& err);
private:boost::asio::io_context& io_context_;boost::asio::ip::tcp::acceptor acceptor_;
};
  • 构造函数:
    • 这是 Server 类的构造函数的实现。构造函数接受一个 boost::asio::io_context 对象的引用和一个端口号作为参数。在构造函数内部,它初始化了 io_context_ 和 acceptor_ 成员变量。io_context_ 用于管理异步操作,而 acceptor_ 用于监听传入的连接。构造函数还调用了 StartAccept 函数来开始接受连接。
Server::Server(boost::asio::io_context& io_context, int16_t port): io_context_(io_context), acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{StartAccept();
}
  • 私有成员函数: StartAccept将要接收连接的acceptor绑定到服务上,其内部就是将accpeptor对应的socket描述符绑定到epolliocp模型上,实现事件驱动。HandleAccept为新连接到来后触发的回调函数。
    • void StartAccept():StartAccept 函数创建一个新的 Session 对象,然后使用 acceptor_async_accept 函数来异步等待并接受传入的连接。当连接被接受时,会调用 HandleAccept 函数来处理连接。
void Server::StartAccept() {Session* new_session = new Session(io_context_);acceptor_.async_accept(new_session->GetSocket(),std::bind(&Server::HandleAccept, this, new_session, std::placeholders::_1));
}
  • void HandleAccept(Session session, const boost::system::error_code& err):*
    • HandleAccept 函数用于处理接受连接操作的回调。如果没有错误发生,它会调用 Session 对象的 Start 方法,启动会话。如果发生错误,它会删除 Session 对象。然后,无论如何,它都会继续调用 StartAccept 函数,以便等待下一个连接。
void Server::HandleAccept(Session* session, const boost::system::error_code& err) {if (!err) {session->Start();} else {delete session;}StartAccept(); // 继续等待下一个连接
}

总的来说,这段代码定义了一个基于Boost.Asio的TCP服务器类 Server,该类在构造函数中初始化了必要的成员变量并开始接受传入的连接。它使用了异步操作来处理连接请求,并在每次连接接受后启动一个新的会话(Session)。

“server.cpp":

#include"server.h"Server::Server(boost::asio::io_context& io_context,int16_t port):io_context_(io_context),acceptor_(io_context,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),port))
{StartAccept();
}void Server::StartAccept() {Session* new_session = new Session(io_context_);acceptor_.async_accept(new_session->GetSocket(),std::bind(&Server::HandleAccept, this, new_session, std::placeholders::_1));
}void Server::HandleAccept(Session* new_session, const boost::system::error_code& err)
{if (err.value() != 0){std::cout << "acceptor session failed.error_code is: " << err.value() << " .message: " << err.what() << std::endl;delete new_session;}else {std::cout << "accept new session success!" << std::endl;std::cout << "client connect,the ip:" << new_session->GetSocket().remote_endpoint().address() << std::endl;new_session->Start();}//继续监听新的客户端连接StartAccept();
}

这段代码是C++server.cpp 文件,它实现了一个基于Boost.Asio库的TCP服务器类 Server 的成员函数。下面是对代码的详细解释:

  • 构造函数:
    • 这是 Server 类的构造函数的实现。构造函数接受一个 boost::asio::io_context 对象的引用和一个端口号作为参数。在构造函数内部,它初始化了 io_context_acceptor_ 成员变量。io_context_ 用于管理异步操作,而 acceptor_ 用于监听传入的连接。构造函数还调用了 StartAccept 函数来开始接受连接。
Server::Server(boost::asio::io_context& io_context, int16_t port): io_context_(io_context), acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{StartAccept();
}
  • StartAccept 函数:
    • StartAccept 函数创建一个新的 Session 对象,然后使用 acceptor_async_accept 函数来异步等待并接受传入的连接。当连接被接受时,会调用 HandleAccept 函数来处理连接。
void Server::StartAccept() {Session* new_session = new Session(io_context_);acceptor_.async_accept(new_session->GetSocket(),std::bind(&Server::HandleAccept, this, new_session, std::placeholders::_1));
}
  • HandleAccept 函数:
    • HandleAccept 函数是用于处理接受连接操作的回调。如果没有错误发生(err.value() == 0),它会输出连接成功的消息,并显示客户端的IP地址。然后,它调用了 Session 对象的 Start 方法来启动会话。如果有错误发生,它会输出错误消息并删除 Session 对象。无论如何,最后它会继续调用 StartAccept 函数,以便等待下一个连接。
void Server::HandleAccept(Session* new_session, const boost::system::error_code& err)
{if (err.value() != 0){std::cout << "acceptor session failed. error_code is: " << err.value() << " . message: " << err.what() << std::endl;delete new_session;}else {std::cout << "accept new session success!" << std::endl;std::cout << "client connect, the IP: " << new_session->GetSocket().remote_endpoint().address() << std::endl;new_session->Start();}// 继续监听新的客户端连接StartAccept();
}

总的来说,这段代码是 Server 类的成员函数的实现,它用于接受客户端的连接请求,并在接受连接后启动会话。这是一个基本的TCP服务器的一部分,用于处理传入的连接请求。

“main.cpp”:

#include"server.h"int main() {try {boost::asio::io_context io_context;Server server(io_context, 9273);io_context.run();}catch (std::exception& e) {std::cout << "exception: " << e.what() << std::endl;}return 0;
}

这段代码是一个C++程序的 main 函数,它创建了一个基于Boost.Asio库的TCP服务器并运行它。以下是对代码的详细解释:

  • 头文件包含:
    • 这行代码包含了名为 “server.h” 的头文件,该头文件应该包含了 Server 类的声明以及其他必要的头文件。
#include "server.h"
  • main 函数:
    • boost::asio::io_context io_context;:创建了一个 io_context 对象,它用于管理异步操作。Boost.Asio库通常需要一个 io_context 对象来协调和管理异步操作。

    • Server server(io_context, 9273); 创建了一个 Server 类的对象 server,并传递了 io_context 对象和一个端口号(在此示例中是9273)作为参数。这样做将启动服务器并开始监听指定的端口。

    • io_context.run(); 调用 io_context 对象的 run 方法,开始运行事件循环,该事件循环会一直运行,直到没有待处理的异步操作。在这里,它将一直运行以监听和处理客户端连接请求。

    • catch (std::exception& e) { … }: 这是一个异常处理块,用于捕获任何可能抛出的异常。如果发生异常,它将打印异常的描述信息。

int main() {try {boost::asio::io_context io_context;Server server(io_context, 9273);io_context.run();}catch (std::exception& e) {std::cout << "exception: " << e.what() << std::endl;}return 0;
}

总的来说,这个 main 函数创建了一个TCP服务器,并通过调用 io_context.run() 启动服务器并进入事件循环,等待客户端连接请求。如果发生异常,它会捕获异常并打印错误信息。这是一个简单的服务器入口点,用于启动服务器应用程序。

3、客户端

客户端的设计用之前的同步模式即可,客户端不需要异步的方式,因为客户端并不是以并发为主,当然写成异步收发更好一些。 这里代码就不展示了,有兴趣去前一篇文章查看。

运行服务器之后再运行客户端,输入字符串后,就可以收到服务器应答的字符串了。
在这里插入图片描述
在这里插入图片描述
echo应答模式:
在这里插入图片描述

4、隐患

demo示例为仿照asio官网编写的,其中存在隐患,就是当服务器即将发送数据前(调用async_write前),此刻客户端中断,服务器此时调用async_write会触发发送回调函数,判断ec为非0进而执行delete this逻辑回收session。但要注意的是客户端关闭后,在tcp层面会触发读就绪事件,服务器会触发读事件回调函数。在读事件回调函数中判断错误码ec为非0,进而再次执行delete操作,从而造成二次析构,这是极度危险的。
在这里插入图片描述

5、总结

本文介绍了异步的应答服务器设计,但是这种服务器并不会在实际生产中使用,主要有两个原因:

  • 因为该服务器的发送和接收以应答的方式交互,而并不能做到应用层想随意发送的目的,也就是未做到完全的收发分离(全双工逻辑)。
  • 该服务器未处理粘包,序列化,以及逻辑和收发线程解耦等问题。
  • 该服务器存在二次析构的风险。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【钻石OA】1区SCI,无需版面费,仅2个月录用!

重 点 本期推荐 本期小编给大家推荐的是无需版面费的1区农林科学类SCI&#xff08;钻石OA&#xff09;。 目前进展顺利&#xff0c;在WOS数据库中各项指标表现良好&#xff0c;且无预警记录。 领域符合录用率高&#xff0c;1区SCI最快2个月录用&#xff01; 期刊官网系统提…

零距离揭秘绝地求生:玩家最关心的吃鸡要领和细节全攻略!

绝地求生作为当下最火爆的吃鸡游戏&#xff0c;吸引了无数玩家的关注和参与。然而&#xff0c;要成为顶尖的吃鸡玩家&#xff0c;并不简单。在这篇文章中&#xff0c;我们将为你揭露绝地求生玩家最关心的吃鸡要领和细节&#xff0c;为你提供一份全方位的游戏指南&#xff01; 1…

vue内置组件Transition的详解

1. Transition定义 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动画&#xff1a; <Transition>会在一个元素或组件进入和离开 DOM 时应用动画。 <TransitionGroup> 会在一个 v-for 列表中的元素或组件被插入&#xff0c;移动&#xff0…

八大排序详解

目录 1.排序的概念及应用 1.1 排序的概念 1.2 排序的应用 1.3 常见的排序算法 2.常见排序算法的实现 2.1 直接插入排序 2.1.1 基本思想 2.1.2 动图解析 2.1.3 排序步骤&#xff08;默认升序&#xff09; 2.1.4 代码实现 2.1.5 特性总结 2.2 希尔排序 2.2.1 基本思…

修改Jenkins主目录

Windows系统上安装了Jenkins后&#xff0c;默认数据目录&#xff0c;也就是工作区目录&#xff0c;即&#xff1a;每次构建的项目数据文件所在目录为&#xff1a; 随着构建项目的增多&#xff0c;一直用这个目录&#xff0c;C盘东西越来越多&#xff0c;电脑就会越来越卡&#…

【链表】排序链表-力扣148题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

3+单基因泛癌+铜死亡纯生信思路

今天给同学们分享一篇3单基因泛癌铜死亡纯生信思路的生信文章“Systematic pan-cancer analysis identifies SLC31A1 as a biomarker in multiple tumor types”&#xff0c;这篇文章于2023年3月27日发表在BMC Med Genomics 期刊上&#xff0c;影响因子为3.622。 溶质载体家族3…

面试必杀技:Jmeter性能测试攻略大全(第一弹)

前言 性能测试是一个全栈工程师/架构师必会的技能之一&#xff0c;只有学会性能测试&#xff0c;才能根据得到的测试报告进行分析&#xff0c;找到系统性能的瓶颈所在&#xff0c;而这也是优化架构设计中重要的依据。 第一章 测试流程&#xff1a; 需求分析→环境搭建→测试…

【IDEA】idea恢复pom.xml文件显示灰色并带有删除线

通过idea打开spring boot项目后&#xff0c;发现每个服务中的pom.xml文件显示灰色并带有删除线&#xff0c;下面为解决方案 问题截图 解决方案 打开file——settings——build,execution,deployment——Ignored Files&#xff0c;把pom.xml前面的复选框去掉&#xff0c;去掉之…

IPV6知识总结

目录 一、IPV6与IPV4相比改进之处1. “无限“的地址空间2. 层次化的地址结构3. 即插即用4. 简化了报头头部5.保证端到端网络的完整性6.安全性增强7.增强QoS特性 二、IPV6的规则IPV6地址主要分为了三类&#xff1a;单播地址&#xff0c;组播地址&#xff0c;任播地址单播地址组播…

Docker从认识到实践再到底层原理(七)|Docker存储卷

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…

浅谈软件测试面试一些常见的问题

一、简历及岗位匹配度 说到简历&#xff0c;其实这一点是很重要但又被很多职场小白忽视的问题。经常有人说我投了很多简历&#xff0c;但是没有公司给我打电话&#xff0c;怎么办&#xff1f; 首先&#xff0c;应该明白的一点&#xff1a;面对求职市场的激烈竞争性&#xff0…

RFID技术引领汽车零部件加工新时代

RFID技术的兴起引领了汽车零部件加工领域的新时代&#xff0c;作为一种利用无线电频率进行自动识别的技术&#xff0c;RFID技术能够快速、准确地识别物体并获取相关数据&#xff0c;在汽车零部件加工中&#xff0c;RFID技术具有重要的应用价值&#xff0c;可以提高生产效率、降…

C++ Primer 类和对象(3)

类和结构体是比较相似&#xff0c;而传统的C的结构体中都是一些数据的类型&#xff0c;类除了有数据之外还有函数。所以可以把类想象成一个具有既有数据又有函数的复合数据类型。 类是一种将抽象转换为用户定义类型的C工具&#xff0c;它将数据表示和操纵数据的方法组合成一个整…

网络安全复习大纲wcf

单选10判断10填空30简答25分析25 选择 &#xff08;1&#xff09;计算机网络安全是指利用计算机网络管理控制和技术措施&#xff0c;保证在网络环境中数据的&#xff08; &#xff09;、完整性、网络服务可用性和可审查性受到保护。 A、保密性 B、抗攻击性 C、网络服务管理性 …

vite跨域proxy设置与开发、生产环境的接口配置,接口在生产环境下,还能使用proxy代理地址吗

文章目录 vite的proxy开发环境设置如果后端没有提供可以替换的/mis等可替换的后缀的处理办法接口如何区分.env.development开发和.env.production生产环境接口在生产环境下&#xff0c;还能使用proxy代理地址吗&#xff1f; vite的proxy开发环境设置 环境&#xff1a; vite 4…

网安新战场:CTF的那些事儿

CTF CTF的前世今生CTF竞赛中的挑战和难题CTF竞赛必备知识CTF竞赛中的技巧与策略&#xff1a; 写在末尾 主页传送门&#xff1a;&#x1f4c0; 传送 CTF的前世今生 CTF&#xff08;Capture The Flag&#xff09;是一种网络安全竞赛&#xff0c;旨在测试参与者解决各种网络安全问…

AI赋能的3D资产管理

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 想象一下&#xff0c;作为一名视频游戏设计师&#xff0c;你希望在游戏中使用 3D 龙模型。 以前&#xff0c;你可以通过两种方式执行此操作&#xff1a; 自己制作复杂的 3D 模型或从多个角度拍摄龙模型的照片。前往 3D 模…

Git_03_暂存工作区的修改/自由切换分支

# 保存当前未commit的代码 > git stash # 保存当前未commit的代码并添加备注 > git stash "备注内容" # 列出stash的所有记录 > git stash list # 应用最近一次的stash > git stash apply # 删除stash的所有记录 > git stash clear1.查看工作区的改动…

5+铜死亡+单基因泛癌生信思路

今天给同学们分享一篇铜死亡单基因泛癌的生信文章“Pan-cancer integrated bioinformatics analysis reveals cuproptosis related gene FDX1 is a potential prognostic and immunotherapeutic biomarker for lower-grade gliomas”&#xff0c;这篇文章于2023年2月7日发表在F…