Linux网络:网络套接字-TCP回显服务器——多进程/线程池(生产者消费者模型)
1.多进程版本
这里选择创建子进程,让子进程再创建子进程。父进程等待子进程,子进程的子进程处理业务逻辑。因为子进程是直接退出,子进程的子进程变成孤儿进程被系统管理,所以父进程在等待的时候不是阻塞等待的,所以可以处理多条链接请求。或者可以忽略SIGCHID信号让子进程不需要等待。
服务端
pragma once #include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/wait.h>#define DEF_PORT 8081class TcpSever
{private:int port;int listen_sock;//监听套接字public: TcpSever(int _port=DEF_PORT):port(_port),listen_sock(-1){}void InitSever(){listen_sock=socket(AF_INET,SOCK_STREAM,0);//IPV4流式套接if(listen_sock<0){std::cerr<<"监听套接字创建失败"<<std::endl;exit(-1);}//绑定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;//随机绑定服务器ipif(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"绑定失败"<<std::endl;exit(-2);}//建立链接
#define BACK_LOG 5if(listen(listen_sock,BACK_LOG)<0){std::cerr<<"链接失败"<<std::endl;exit(-3);}}//获取链接void Loop(){//获取客户端信息struct sockaddr_in client;//signal(SIGCHID,hander);//hander是信号捕捉方法while(true){socklen_t len=sizeof(client);//获取新链接int sock=accept(listen_sock,(struct sockaddr*)&client,&len);if(sock<0){std::cerr<<"接受链接失败"<<std::endl;continue;//继续接受其他链接请求}//多进程处理多个客户端pid_t id=fork();if(id==0)//子进程{//子进程,与子进程的子进程不关心listen_sockclose(listen_sock);if(fork()>0){exit(0);//子进程直接退出后子进程的子进程变为孤儿进程被守护进程接管,不用等待了}//子进程的子进程,处理业务std::cout<<"获取新连接成功! ip["<<inet_ntoa(client.sin_addr)<<"] port["<<ntohs(client.sin_port)<<"]"<<std::endl;//整数ip转为字符ip形式,网络字节端口转化为本地端口//开始服务std::string CIp=inet_ntoa(client.sin_addr);int CPort=ntohs(client.sin_port);Sever(sock,CIp,CPort);exit(0);}//不能使用阻塞等待,可以使用信号机制忽略SIGCHID信号,或者使用两次fork函数close(sock);//父进程不关心客户端的链接请求,请求交给子进程做。waitpid(id,nullptr,0);//不会阻塞,因为id进程创建完子进程会立即退出,所以不会阻塞。}}void Sever(int sock,std::string&ip,int port){char buff[1024];while(true){memset(buff,0,sizeof(buff));ssize_t ReadSize=read(sock,buff,sizeof(buff)-1);if(ReadSize>0){buff[ReadSize]='\0';//添加字符串结束标志std::cout<<"Client["<<ip<<"-port-"<<port<<"]# "<<buff<<std::endl;write(sock,buff,ReadSize);//向客户端写入数据}else if(ReadSize==0){//对端关闭链接std::cout<<"Client["<<ip<<"-port-"<<port<<"]# 关闭"<<std::endl;break;}else{std::cerr<<sock<<"读取错误"<<std::endl;break;}}std::cout<<"服务终止"<<std::endl;}~TcpSever(){if(listen_sock>=0){close(listen_sock);}}
};
启动服务端
#include"Tcp_Sever.h"// Tcp_Sever +portvoid User(char*name)
{std::cout<<name<<" + port"<<std::endl;
}int main(int argc,char*argv[])
{if(argc!=2){User(argv[0]);exit(-1);}TcpSever*Sever=new TcpSever(atoi(argv[1]));//将字符串形式的端口号转化为整数形式Sever->InitSever();Sever->Loop();return 0;
}
客户端
#pragma once#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string>
#include<string.h>class TcpClient
{private:std::string sev_ip;//服务器ipint sev_port;int sock;public:TcpClient(std::string _ip,int _sev_port):sev_ip(_ip),sev_port(_sev_port),sock(-1){}void InitClient(){sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"创建套接字失败"<<std::endl;exit(-1);}//客户端不需要绑定监听}//启动客户端void Start(){//向服务器发送链接//填充服务器信息struct sockaddr_in sever;memset(&sever,0,sizeof(sever));sever.sin_family=AF_INET;sever.sin_port=htons(sev_port);//服务器开放的端口sever.sin_addr.s_addr=inet_addr(sev_ip.c_str());//服务器的ip地址if(connect(sock,(struct sockaddr*)&sever,sizeof(sever))==0){//与服务器交互std::cout<<"发送交互请求成功"<<std::endl;Request(sock);}else{std::cerr<<"交互失败"<<std::endl;}}void Request(int Sock){std::string meg;while(true){std::cout<<"输入信息#";std::cin>>meg;char buff[1024];write(Sock,meg.c_str(),meg.size());//读取服务器发送的消息ssize_t ReadSize=read(sock,buff,sizeof(buff)-1);if(ReadSize>0){buff[ReadSize]='\0';}std::cout<<"Sever#"<<buff<<std::endl;}}~TcpClient(){if(sock>=0){close(sock);}}
};
启动客户端
#include"Tcp_Client.h"// ./Tcp_Client +ip +portvoid User(char*name)
{std::cout<<name<<" + sever_ip + sever_port"<<std::endl;
}int main(int argc,char*argv[])
{if(argc!=3){User(argv[0]);exit(-1);}TcpClient*Client=new TcpClient(argv[1],atoi(argv[2]));Client->InitClient();Client->Start();return 0;
}
2.多线程版本
注意:在类内部的成员函数,调用类内非静态成员函数时会自动带this指针。而线程执行的函数要求void* Hander(void*)类型,所以线程执行函数要用static修饰。
服务端
#pragma once #include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>#define DEF_PORT 8081//通信结构体
class Pragram{public:int sock;std::string ip;int port;Pragram(int _sock,std::string _ip,int _port):sock(_sock),ip(_ip),port(_port){}
};class TcpSever
{private:int port;int listen_sock;//监听套接字public: TcpSever(int _port=DEF_PORT):port(_port),listen_sock(-1){}void InitSever(){listen_sock=socket(AF_INET,SOCK_STREAM,0);//IPV4流式套接if(listen_sock<0){std::cerr<<"监听套接字创建失败"<<std::endl;exit(-1);}//绑定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;//随机绑定服务器ipif(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"绑定失败"<<std::endl;exit(-2);}//建立链接
#define BACK_LOG 5if(listen(listen_sock,BACK_LOG)<0){std::cerr<<"链接失败"<<std::endl;exit(-3);}std::cout<<"服务启动成功"<<std::endl;}//获取链接void Loop(){//获取客户端信息struct sockaddr_in client;//signal(SIGCHID,hander);//hander是信号捕捉方法while(true){socklen_t len=sizeof(client);//获取新链接int sock=accept(listen_sock,(struct sockaddr*)&client,&len);if(sock<0){std::cerr<<"接受链接失败"<<std::endl;continue;//继续接受其他链接请求}pthread_t tid;std::string ip=inet_ntoa(client.sin_addr);int port=ntohs(client.sin_port);Pragram*msg=new Pragram(sock,ip,port);pthread_create(&tid,nullptr,HanderQuest,msg);}}private:static void* HanderQuest(void*arg){Pragram*msg=(Pragram*)arg;pthread_detach(pthread_self());//线程分离std::cout<<"获取新连接成功"<<"[ ip:"<<msg->ip<<" port:"<<msg->port<<" ]"<<std::endl;Sever(msg->sock,msg->ip,msg->port);close(msg->sock);delete msg;return nullptr;}static void Sever(int sock,std::string&ip,int port){char buff[1024];while(true){memset(buff,0,sizeof(buff));ssize_t ReadSize=read(sock,buff,sizeof(buff)-1);if(ReadSize>0){buff[ReadSize]='\0';//添加字符串结束标志std::cout<<"Client["<<ip<<"-port-"<<port<<"]# "<<buff<<std::endl;write(sock,buff,ReadSize);//向客户端写入数据}else if(ReadSize==0){//对端关闭链接std::cout<<"Client["<<ip<<"-port-"<<port<<"]# 关闭"<<std::endl;break;}else{std::cerr<<sock<<"读取错误"<<std::endl;break;}}std::cout<<"服务终止"<<std::endl;}~TcpSever(){if(listen_sock>=0){close(listen_sock);}}
};
客户端代码部分不变。
3.模板线程池版本
线程池代码:
PthreadPool.h
#pragma once #include<iostream>
#include<pthread.h>
#include<queue>#define NUM 5template<typename Task>
class ThreadPool
{private:std::queue<Task> _task_queue;//临界资源pthread_mutex_t _lock;int _thread_num;pthread_cond_t _cond;public:ThreadPool(int Num=NUM):_thread_num(Num){pthread_mutex_init(&_lock,nullptr);pthread_cond_init(&_cond,nullptr);}bool IsEmpty(){return _task_queue.empty();}static void*Routine(void*arg)//静态函数无隐含的this指针{pthread_detach(pthread_self());ThreadPool<Task>*self=(ThreadPool<Task>*)arg;while(true){pthread_mutex_lock(&(self->_lock));while(self->IsEmpty())//循环检测临界资源是否就绪{//休眠pthread_cond_wait(&(self->_cond),&(self->_lock));}//任务队列有任务Task t;self->Pop(t);pthread_mutex_unlock(&(self->_lock));//解锁后处理任务t.Run();}}void Push(const Task&in)//外部添加任务{pthread_mutex_lock(&_lock);_task_queue.push(in);//唤醒一个线程pthread_cond_signal(&_cond);pthread_mutex_unlock(&_lock);}void Pop(Task&out)//线程获取任务{out=_task_queue.front();_task_queue.pop();}~ThreadPool(){pthread_mutex_destroy(&_lock);pthread_cond_destroy(&_cond);}void InitThreadPool(){pthread_t tid;for(int i=0;i<_thread_num;i++){//为了避免传参时C++传this指针,在线程执行方法上带static。//传参数时将this指针传到线程执行方法上。 pthread_create(&tid,nullptr,Routine,this);}}
};
设计任务Task.h
#pragma once #include<iostream>
#include<string>
#include<unistd.h>
#include<string.h>struct Hander{void operator()(int sock,std::string&ip,int port){char buff[1024];while(true){memset(buff,0,sizeof(buff));ssize_t ReadSize=read(sock,buff,sizeof(buff)-1);if(ReadSize>0){buff[ReadSize]='\0';//添加字符串结束标志std::cout<<"Client["<<ip<<"-port-"<<port<<"]# "<<buff<<std::endl;write(sock,buff,ReadSize);//向客户端写入数据}else if(ReadSize==0){//对端关闭链接std::cout<<"Client["<<ip<<"-port-"<<port<<"]# 关闭"<<std::endl;break;}else{std::cerr<<sock<<"读取错误"<<std::endl;break;}}std::cout<<"服务终止"<<std::endl;close(sock);}
};class Task{private:int sock;std::string ip;int port;Hander condut;//仿函数public:Task()=default;Task(int _sock,std::string _ip,int _port):sock(_sock),ip(_ip),port(_port){}void Run(){condut(sock,ip,port);}};
服务端代码
#pragma once #include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>#include"ThreadPool.h"
#include"Task.h"#define DEF_PORT 8081//通信结构体
class Pragram{public:int sock;std::string ip;int port;Pragram(int _sock,std::string _ip,int _port):sock(_sock),ip(_ip),port(_port){}
};class TcpSever
{private:int port;int listen_sock;//监听套接字ThreadPool<Task>*Pool;public: TcpSever(int _port=DEF_PORT):port(_port),listen_sock(-1),Pool(nullptr){}void InitSever(){listen_sock=socket(AF_INET,SOCK_STREAM,0);//IPV4流式套接if(listen_sock<0){std::cerr<<"监听套接字创建失败"<<std::endl;exit(-1);}//绑定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;//随机绑定服务器ipif(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"绑定失败"<<std::endl;exit(-2);}//建立链接
#define BACK_LOG 5if(listen(listen_sock,BACK_LOG)<0){std::cerr<<"链接失败"<<std::endl;exit(-3);}std::cout<<"服务启动成功"<<std::endl;Pool=new ThreadPool<Task>(10);//10个线程}//获取链接void Loop(){Pool->InitThreadPool();//获取客户端信息struct sockaddr_in client;//signal(SIGCHID,hander);//hander是信号捕捉方法while(true){socklen_t len=sizeof(client);//获取新链接int sock=accept(listen_sock,(struct sockaddr*)&client,&len);if(sock<0){std::cerr<<"接受链接失败"<<std::endl;continue;//继续接受其他链接请求}std::string ip=inet_ntoa(client.sin_addr);int port=ntohs(client.sin_port);Task asig(sock,ip,port);Pool->Push(asig);}}~TcpSever(){if(listen_sock>=0){close(listen_sock);}}
};
客户端代码不需要改变
相关文章:
Linux网络:网络套接字-TCP回显服务器——多进程/线程池(生产者消费者模型)
1.多进程版本 这里选择创建子进程,让子进程再创建子进程。父进程等待子进程,子进程的子进程处理业务逻辑。因为子进程是直接退出,子进程的子进程变成孤儿进程被系统管理,所以父进程在等待的时候不是阻塞等待的,所以可…...
Redis 篇-深入了解基于 Redis 实现消息队列(比较基于 List 实现消息队列、基于 PubSub 发布订阅模型之间的区别)
🔥博客主页: 【小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录 1.0 消息队列的认识 2.0 基于 List 实现消息队列 2.1 基于 List 实现消息队列的优缺点 3.0 基于 PubSub 实现消息队列 3.1 基于 PubSub 的消息队列优缺点 4.0 基于 St…...
python 学习一张图
python学习一张图,python的特点的是学的快,一段时间不用,忘记的也快,弄一张图及一些入门案例吧。 写一个简单的测试: #!/usr/bin/python # -*- coding: UTF-8 -*- import osdef add_num(a, b):return a bif __name__…...
通过Docker部署 MongoDB 服务器
今天我们将在三丰云的免费服务器上进行 MongoDB 的部署测试。这款不错的免费服务器提供了很好的性能,1核CPU、1G内存、10G硬盘和5M带宽,足以满足我们的基本需求。三丰云的服务稳定,操作简单,真是一个值得推荐的选择,特…...
无人机避障雷达技术详解
随着无人机技术的飞速发展,其应用领域已经从最初的军事领域扩展到商业、农业、建筑巡检、应急救援、物流运输等多个领域。在这些多样化的应用场景中,无人机的安全性和稳定性显得尤为重要。无人机避障技术作为保障无人机安全飞行的核心技术之一࿰…...
2009-2023年上市公司华证esg评级评分数据(年度+季度)(含细分项)
2009-2023年上市公司华证esg评级评分数据(年度季度)(含细分项) 1、时间:2009-2023年 2、来源:整理自wind 3、指标:证券代码、年份、证券简称、评级日期、综合评级、综合得分、E评级、E得分、…...
C++ 模板进阶知识——stdenable_if
目录 C 模板进阶知识——std::enable_if1. 简介和背景基本语法使用场景 2. std::enable_if 的基本用法示例:限制函数模板只接受整数类型 3. SFINAE 和 std::enable_if示例:使用 SFINAE 进行函数重载SFINAE 的优点应用场景 4. 在类模板中使用 std::enable…...
国内外ChatGPT网站集合,无限制使用【2024-09最新】~
经过我一年多以来,使用各种AI工具的体验,我收集了一批AI工具和站点 这些工具都是使用的最强最主流的模型,也都在各个领域里都独领风骚的产品。 而且,这些工具你都可以无限制地使用。 无论你是打工人、科研工作者、学生、文案写…...
如何在VUE3中使用函数式组件
在Vue 3中,函数式组件的概念与Vue 2相似,但实现方式有所不同。函数式组件是一种无状态、无实例的组件,它们只根据传入的props和context来渲染输出。在Vue 3中,你可以通过定义一个函数并返回一个渲染函数来使用函数式组件。但是&am…...
linux访问外网的设置
Ubuntu | LUCKFOX WIKI 开发板配置 添加路由信息 sudo route add default gw 172.32.0.100添加 DNS servers 打开文件 sudo vi /etc/resolv.conf添加以下内容: nameserver 8.8.8.8联网测试 ping www.baidu.com开机自动配置 路由信息和 DNS servers 重启后会被清除,我们创建…...
PHP轻松创建高效收集问卷调查小程序系统源码
轻松创建,高效收集 —— 问卷调查小程序,你的调研神器! 一、告别繁琐,一键开启调研之旅 还在为设计问卷、收集数据而头疼不已吗?现在,有了“问卷调查小程序”,一切都变得轻松简单!无…...
Redis面试必问:Redis为什么快?Redis五大基本数据类型
请记住胡广一句话,所有的中间件所有的框架都是建立在基础之上,数据结构,计算机网络,计算机原理大伙一定得看透!!~ 1. Redis快的秘密 相信大部分Redis初学者都会忽略掉一个重要的知识点,Redis…...
InternVL2- dockerfile环境变量持久化使用`ENV`而不是`RUN export`来设置环境变量,以确保环境变量在容器运行时仍然可用
在Dockerfile中使用RUN export命令设置环境变量并不是一种持久化的方式。当你在Dockerfile中使用export命令时,它只会在当前构建阶段生效,并不会被持久化到生成的镜像中。这是因为export命令实际上是在shell环境中设置环境变量,而Docker构建过…...
Python(PyTorch和TensorFlow)图像分割卷积网络导图(生物医学)
🎯要点 语义分割图像三层分割椭圆图像脑肿瘤图像分割动物图像分割皮肤病变分割多模态医学图像多尺度特征生物医学肖像多类和医学分割通用图像分割模板腹部胰腺图像分割分类注意力网络病灶边界分割气胸图像分割 Python生物医学图像卷积网络 该网络由收缩路径和扩…...
DevOps实现CI/CD实战(七)- Jenkins集成k8s实现自动化CI
自动化CI操作 1. 安装gitlab plugin 工具 ##### 2. 配置流水线任务的构建触发器,复制URL:http://192.168.201.111:8080/project/pipeline 3. Gitlab配置Webhooks,将上面的url:http://192.168.201.111:8080/project/pipeline粘…...
从ES6到ES2023 带你深入了解什么是ES
从ES6到ES2023,我们深入探索ECMAScript(简称ES)的演变与发展,了解这一JavaScript标准背后的技术革新和进步。ECMAScript作为JavaScript的标准化版本,每年都在不断推出新版本,为开发者带来更加丰富和强大的功…...
openVX加速-常见问题:适用场景、AI加速、安装方式等
1. 哪些算法处理推荐使用 OpenVX OpenVX 是非常适合图像处理和计算机视觉任务的框架,特别是在需要高性能和硬件加速的场景下。如果你的前处理和后处理涉及到图像滤波、边缘检测、颜色转换等操作,使用 OpenVX 可以带来性能提升。 OpenVX 更适合处理以下…...
国产芯片LT8711HE:TYPE-C/DP1.2转HDMI2.0转换器,4k60Hz高分辨率
以下为LT8711HE芯片的简单介绍,如有介绍不尽之处,请指出 LT8711HE是一个高性能的Type-C/DP1.2到HDMI2.0转换器,用于连接USB Type-C源或DP1.2源到HDMI2.0接收器。 LT8711HE集成了一个DP1.2兼容的接收器和一个HDMI2.0兼容的发射器。另外&…...
论文翻译:arxiv-2024 Benchmark Data Contamination of Large Language Models: A Survey
Benchmark Data Contamination of Large Language Models: A Survey https://arxiv.org/abs/2406.04244 大规模语言模型的基准数据污染:一项综述 文章目录 大规模语言模型的基准数据污染:一项综述摘要1 引言 摘要 大规模语言模型(LLMs&…...
Java+Swing用户信息管理系统
JavaSwing用户信息管理系统 一、系统介绍二、功能展示1.管理员登陆2.用户信息查询3.用户信息添加4.用户信息修改5.用户信息删除 三、系统实现1.UserDao .java 四、其它1.其他系统实现 一、系统介绍 该系统实现了管理员系统登陆、用户信息查询、用户信息添加、用户信息修改、用…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
未授权访问事件频发,我们应当如何应对?
在当下,数据已成为企业和组织的核心资产,是推动业务发展、决策制定以及创新的关键驱动力。然而,未授权访问这一隐匿的安全威胁,正如同高悬的达摩克利斯之剑,时刻威胁着数据的安全,一旦触发,便可…...
PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
在构建任何动态、数据驱动的Web API时,一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说,深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言,以及学会如何在Python中操作数据库,是…...
