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.其他系统实现 一、系统介绍 该系统实现了管理员系统登陆、用户信息查询、用户信息添加、用户信息修改、用…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...