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

Linux——多路复用之select

目录

前言

一、select的认识

二、select的接口

三、select的使用

四、select的优缺点


前言

在前面,我们学习了五种IO模型,对IO有了基本的认识,知道了select效率很高,可以等待多个文件描述符,那他是如何等待的呢?我们又该如何使用呢?

一、select的认识

系统提供select函数来实现多路复用输入/输出模型

  • select系统调用是用来让我们的程序监视多个文件描述符的状态变化的
  • 程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变

select只负责等待,不负责拷贝,一次可以等待多个文件描述符。他的作用是让read和write不再阻塞

二、select的接口

select的调用接口如下

参数 1 int nfds:值最大的文件描述符+1。

参数 2 fd_set* readfds:fd_set本质是一张位图。代表select需要关心的读事件

参数 3 fd_set* writefds:代表select需要关心的读事件

参数 4 fd_set* execptfdsfds:代表select需要关心的异常事件,我们暂时不考虑

参数 5 struct timeval* timeout:时间结构体,成员有秒和微秒,代表等待的时间

                                                  {n,m}为阻塞等待n秒m微秒,时间结束后返回

                                                  {0,0}为非阻塞等待

                                                  nullptr为阻塞等待

参数2,3,4类似,都是输入输出型参数,参数5也是输入输出型参数,输出的是剩余时间

以readfds为例

输入时:比特位的位置,表示文件描述符的值,比特位的内容(0/1),用户关心内核,是否关心这个fd的读事件。

输出时:比特位的位置,表示文件描述符的值,比特位的内容(0/1),内核告诉用户,哪些文件fd上的读事件是否就绪

返回值:

  1. ret  >  0 :select等待的多个fd中,已经就需要的fd个数
  2. ret == 0 :select超时返回
  3. ret  <  0 :select出错

同时,fd_set 是特定的类型,我们对其赋值时,是不方便赋值的,因此库里面也给提供的一个函数,方便我们处理。

FD_CLR                    从文件描述符集合 set 中清除文件描述符 fd。

FD_ISSET                 检查文件描述符 fd 是否在文件描述符集合 set 中。

FD_SET                    将文件描述符 fd 添加到文件描述符集合 set 中。

FD_ZERO                 清空文件描述符集合 set,将其所有位都设置为零。

三、select的使用

Log.hpp

#pragma once#include <iostream>
#include <cstdarg>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pthread.h>
using namespace std;enum
{Debug = 0,Info,Warning,Error,Fatal
};enum
{Screen = 10,OneFile,ClassFile
};string LevelToString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Unknown";}
}const int default_style = Screen;
const string default_filename = "Log.";
const string logdir = "log";class Log
{
public:Log(int style = default_style, string filename = default_filename): _style(style), _filename(filename){if (_style != Screen)mkdir(logdir.c_str(), 0775);}// 更改打印方式void Enable(int style){_style = style;if (_style != Screen)mkdir(logdir.c_str(), 0775);}// 时间戳转化为年月日时分秒string GetTime(){time_t currtime = time(nullptr);struct tm *curr = localtime(&currtime);char time_buffer[128];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday, curr->tm_hour, curr->tm_min, curr->tm_sec);return time_buffer;}// 写入到文件中void WriteLogToOneFile(const string &logname, const string &message){FILE *fp = fopen(logname.c_str(), "a");if (fp == nullptr){perror("fopen failed");exit(-1);}fprintf(fp, "%s\n", message.c_str());fclose(fp);}// 打印日志void WriteLogToClassFile(const string &levelstr, const string &message){string logname = logdir;logname += "/";logname += _filename;logname += levelstr;WriteLogToOneFile(logname, message);}pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void WriteLog(const string &levelstr, const string &message){pthread_mutex_lock(&lock);switch (_style){case Screen:cout << message << endl; // 打印到屏幕中break;case OneFile:WriteLogToClassFile("all", message); // 给定all,直接写到all里break;case ClassFile:WriteLogToClassFile(levelstr, message); // 写入levelstr里break;default:break;}pthread_mutex_unlock(&lock);}// 提供接口给运算符重载使用void _LogMessage(int level, const char *file, int line, char *rightbuffer){char leftbuffer[1024];string levelstr = LevelToString(level);string currtime = GetTime();string  idstr = to_string(getpid());snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s][%s:%d]", levelstr.c_str(), currtime.c_str(), idstr.c_str(), file, line);string messages = leftbuffer;messages += rightbuffer;WriteLog(levelstr, messages);}// 运算符重载void operator()(int level, const char *file, int line, const char *format, ...){char rightbuffer[1024];va_list args;                                              // va_list 是指针va_start(args, format);                                    // 初始化va_list对象,format是最后一个确定的参数vsnprintf(rightbuffer, sizeof(rightbuffer), format, args); // 写入到rightbuffer中va_end(args);_LogMessage(level, file, line, rightbuffer);}~Log(){}private:int _style;string _filename;
};Log lg;class Conf
{
public:Conf(){lg.Enable(Screen);}~Conf(){}
};Conf conf;// 辅助宏
#define lg(level, format, ...) lg(level, __FILE__, __LINE__, format, ##__VA_ARGS__)

Socket.hpp 

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
using namespace std;
namespace Net_Work
{static const int default_backlog = 5;static const int default_sockfd = -1;using namespace std;enum{SocketError = 1,BindError,ListenError,ConnectError,};// 封装套接字接口基类class Socket{public:// 封装了socket相关方法virtual ~Socket() {}virtual void CreateSocket() = 0;virtual void BindSocket(uint16_t port) = 0;virtual void ListenSocket(int backlog) = 0;virtual bool ConnectSocket(string &serverip, uint16_t serverport) = 0;virtual Socket *AcceptSocket(string *peerip, uint16_t *peerport) = 0;virtual int GetSockFd() = 0;virtual void SetSockFd(int sockfd) = 0;virtual void CloseSocket() = 0;virtual bool Recv(string *buff, int size) = 0;virtual void Send(string &send_string) = 0;// 方法的集中在一起使用public:void BuildListenSocket(uint16_t port, int backlog = default_backlog){CreateSocket();BindSocket(port);ListenSocket(backlog);}bool BuildConnectSocket(string &serverip, uint16_t serverport){CreateSocket();return ConnectSocket(serverip, serverport);}void BuildNormalSocket(int sockfd){SetSockFd(sockfd);}};class TcpSocket : public Socket{public:TcpSocket(int sockfd = default_sockfd): _sockfd(sockfd){}~TcpSocket() {}void CreateSocket() override{_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0)exit(SocketError);}void BindSocket(uint16_t port) override{int opt = 1;setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));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;int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0)exit(BindError);}void ListenSocket(int backlog) override{int n = listen(_sockfd, backlog);if (n < 0)exit(ListenError);}bool ConnectSocket(string &serverip, uint16_t serverport) override{struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(serverport);// addr.sin_addr.s_addr = inet_addr(serverip.c_str());inet_pton(AF_INET, serverip.c_str(), &addr.sin_addr);int n = connect(_sockfd, (sockaddr *)&addr, sizeof(addr));if (n == 0)return true;return false;}Socket *AcceptSocket(string *peerip, uint16_t *peerport) override{struct sockaddr_in addr;socklen_t len = sizeof(addr);int newsockfd = accept(_sockfd, (sockaddr *)&addr, &len);if (newsockfd < 0)return nullptr;// *peerip = inet_ntoa(addr.sin_addr);// INET_ADDRSTRLEN 是一个定义在头文件中的宏,表示 IPv4 地址的最大长度char ip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET, &addr.sin_addr, ip_str, INET_ADDRSTRLEN);*peerip = ip_str;*peerport = ntohs(addr.sin_port);Socket *s = new TcpSocket(newsockfd);return s;}int GetSockFd() override{return _sockfd;}void SetSockFd(int sockfd) override{_sockfd = sockfd;}void CloseSocket() override{if (_sockfd > default_sockfd)close(_sockfd);}bool Recv(string *buff, int size) override{char inbuffer[size];ssize_t n = recv(_sockfd, inbuffer, size - 1, 0);if (n > 0){inbuffer[n] = 0;*buff += inbuffer;return true;}elsereturn false;}void Send(string &send_string) override{send(_sockfd, send_string.c_str(),send_string.size(),0);}private:int _sockfd;string _ip;uint16_t _port;};
}

        select只负责等待,不负责处理,最初我们有一个listen_sock需要交给select去管理,当有新链接到来是,listen_sock要去接受新链接,但是接受后,不能立刻read或者write,因为不确定当前事件是否就绪,需要将新链接也交给select管理

        如何将新链接交给select呢?我们得有一个数据结构(这里用的数组),把所有的fd都管理起来,新链接到来时,都可以往这个数组里面添加文件描述符fd。后面select遍历数组,就可以找到需要管理的fd了,但这样,我们需要经常遍历这个数组

  1. 添加时需要遍历找到空再插入
  2. select传参,需要遍历查找最大的文件描述符
  3. select等待成功后调用处理函数时,也需遍历查找就绪的文件描述符

        同时,由于select的事件参数是一个输入输出型参数,因此我们每次都得重新对该参数重新赋值。

如下是SelectServer.hpp的核心代码 

SelectServer.hpp

#pragma once
#include <iostream>
#include <string>
#include <sys/select.h>
#include "Log.hpp"
#include "Socket.hpp"using namespace Net_Work;
const static int gdefaultport = 8888;
const static int gbacklog = 8;
const static int num = sizeof(fd_set) * 8;class SelectServer
{
public:SelectServer(int port) : _port(port), _listensock(new TcpSocket()){}void HandlerEvent(fd_set rfds){for (int i = 0; i < num; i++){if (_rfds_array[i] == nullptr)continue;int fd = _rfds_array[i]->GetSockFd();// 判断事件是否就绪if (FD_ISSET(fd, &rfds)){// 读事件分两类,一类是新链接到来,一类是新数据到来if (fd == _listensock->GetSockFd()){// 新链接到来lg(Info, "get a new link");// 获取连接std::string clientip;uint16_t clientport;Socket *sock = _listensock->AcceptSocket(&clientip, &clientport);if (!sock){lg(Error, "accept error");return;}lg(Info, "get a client,client info is# %s:%d,fd: %d", clientip.c_str(), clientport, sock->GetSockFd());// 此时获取连接成功了,但是不能直接read write,sockfd仍需要交给select托管 -- 添加到数组_rfds_array中int pos = 0;for (; pos < num; pos++){if (_rfds_array[pos] == nullptr){_rfds_array[pos] = sock;lg(Info, "get a new link, fd is : %d", sock->GetSockFd());break;}}if (pos == num){sock->CloseSocket();delete sock;lg(Warning, "server is full, be carefull...");}}else{// 普通的读事件就绪std::string buffer;bool res = _rfds_array[i]->Recv(&buffer, 1024);if (res){lg(Info,"client say# %s",buffer.c_str());buffer+=": 你好呀,同志\n";_rfds_array[i]->Send(buffer);buffer.clear();}else{lg(Warning,"client quit ,maybe close or error,close fd: %d",fd);_rfds_array[i]->CloseSocket();delete _rfds_array[i];_rfds_array[i] = nullptr;}}}}}void InitServer(){_listensock->BuildListenSocket(_port, gbacklog);for (int i = 0; i < num; i++){_rfds_array[i] = nullptr;}_rfds_array[0] = _listensock.get();}void Loop(){_isrunning = true;// 循环重置select需要的rfdswhile (_isrunning){// 不能直接获取新链接,因为accpet可能阻塞// 所有的fd,都要交给select,listensock上面新链接,相当于读事件// 因此需要将listensock交给select// 遍历数组, 1.找最大的fd  2. 合法的fd添加到rfds集合中fd_set rfds;FD_ZERO(&rfds);int max_fd = _listensock->GetSockFd();for (int i = 0; i < num; i++){if (_rfds_array[i] == nullptr){continue;}else{// 添加fd到集合中int fd = _rfds_array[i]->GetSockFd();FD_SET(fd, &rfds);if (max_fd < fd) // 更新最大值{max_fd = fd;}}}// 定义时间struct timeval timeout = {0, 0};PrintDebug();// rfds是输入输出型参数,rfds是在select调用返回时,不断被修改,所以每次需要重置rfdsint n = select(max_fd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr);switch (n){case 0:lg(Info, "select timeout...,last time: %u.%u", timeout.tv_sec, timeout.tv_usec);break;case -1:lg(Error, "select error!!!");default:// 正常就绪的fdlg(Info, "select success,begin event handler,last time: %u.%u", timeout.tv_sec, timeout.tv_usec);HandlerEvent(rfds); break;}}_isrunning = false;}void Stop(){_isrunning = false;}void PrintDebug(){std::cout << "current select rfds list is :";for (int i = 0; i < num; i++){if (_rfds_array[i] == nullptr)continue;elsestd::cout << _rfds_array[i]->GetSockFd() << " ";}std::cout << std::endl;}private:std::unique_ptr<Socket> _listensock;int _port;bool _isrunning;// select 服务器要被正确设计,需要程序员定义数据结构,来吧所有的fd管理起来Socket *_rfds_array[num];
};

 Main.cc

#include <iostream>
#include <memory>
#include "SelectServer.hpp"void Usage(char* argv)
{std::cout<<"Usage: \n\t"<<argv<<" port\n"<<std::endl;
}
// ./select_server 8080
int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);return -1;}uint16_t localport = std::stoi(argv[1]);std::unique_ptr<SelectServer> svr = std::make_unique<SelectServer>(localport);svr->InitServer();svr->Loop();return 0;
}

四、select的优缺点

优点:select只负责等待,可以等待多个fd,IO的时候,效率会比较高一些。

缺点:

  1. 由于select是输入输出型参数,因此我们每次都要对select的参数重新设置。
  2. 编写代码时,select因为要使用第三方数组,充满了遍历,这可能会影响select的效率。
  3. 用户到内核,内核到用户,每次select调用和返回,都要对位图重新设置,用户和内核之间,要一直进行数据拷贝。
  4. select让OS在底层遍历需要关心所有的fd,这也会造成效率低下,这也是为何第一个参数需要传入max_fd + 1,就是因为select的底层需要遍历。
  5. fd_set 是系统提供的类型,fd_set大小是固定的,就意味着位图的个数是固定的,也就是select最多能够检测到fd的总数是有上限的。

相关文章:

Linux——多路复用之select

目录 前言 一、select的认识 二、select的接口 三、select的使用 四、select的优缺点 前言 在前面&#xff0c;我们学习了五种IO模型&#xff0c;对IO有了基本的认识&#xff0c;知道了select效率很高&#xff0c;可以等待多个文件描述符&#xff0c;那他是如何等待的呢&a…...

探索.NET内存之海:垃圾回收的艺术与实践

简述 在.NET的广阔天地中&#xff0c;内存管理如同航海中的罗盘&#xff0c;指引着程序的稳健运行和性能的极致优化。作为软件工程师&#xff0c;我们时常在代码的海洋中航行&#xff0c;而内存管理则是确保航程顺畅的关键。本文将带您深入.NET的内存管理世界&#xff0c;一探垃…...

路由数据获取及封装方法

数据库设计 自联表 定义tree字段 public class LabelValue{public int label { get; set; }public string? value { get; set; }public List<LabelValue> children { get; set; }}获取路由方法 public Response<object> getMenuList() {Response<object>…...

Visual Studio Code 实现远程开发

Background 远程开发是指开发人员在本地计算机上进行编码、调试和测试&#xff0c;但实际的开发环境、代码库或应用程序运行在远程服务器上。远程开发的实现方式多种多样&#xff0c;包括通过SSH连接到远程服务器、使用远程桌面软件、或者利用云开发环境等。这里我们是使用VSCo…...

基于STM32设计的人体健康监测系统(华为云IOT)(189)

基于STM32设计的人体健康监测系统(华为云IOT)(189) 文章目录 一、前言1.1 项目介绍【1】项目功能介绍【2】项目硬件模块组成1.2 设计思路【1】整体设计思路【2】整体构架【3】ESP8266模块配置【4】上位机开发思路【5】供电方式1.3 项目开发背景【1】选题的意义【2】可行性分析【…...

开源防病毒工具--ClamAV

产品文档&#xff1a;简介 - ClamAV 文档 开源地址&#xff1a;Cisco-Talos/clamav&#xff1a;ClamAV - 文档在这里&#xff1a;https://docs.clamav.net (github.com) 一、引言 ClamAV&#xff08;Clam AntiVirus&#xff09;是一个开源的防病毒工具&#xff0c;广泛应用…...

【网络】Socket编程

文章目录 正确理解端口号理解源IP地址和目的IP地址认识端口号端口号和进程ID 理解Socket网络字节序socket编程接口创建socket套接字bind绑定套接字listen建立监听accept接受连接connect建立连接sendto发送数据接收数据close关闭套接字 sockaddr结构体 正确理解端口号 理解源IP…...

【鸿蒙学习笔记】舜和酒店项目开发

这里写目录标题 前期准备1. 环境准备2. 开发工具准备 创建项目1. 使用 deveco-studio 创建 ShunHeHotel 项目2. 把ShunHeHotel 项目使用git进行版本控制3. 提交第1个commit&#xff0c;Alt0 → 输入commit message → 提交4. 查看已经提交的第一个提交5. gitcode 创建同名远程项…...

再进行程序的写时,不要使用eval函数——内建函数eval的坏处!!!!!!!!

一、安全性问题 执行任意代码&#xff1a; eval函数可以执行任意的Python表达式&#xff0c;包括算术运算、逻辑判断、字符串操作等&#xff0c;甚至可以访问当前作用域中的所有变量和函数。这意味着&#xff0c;如果eval处理的字符串来自不可信的源&#xff08;如用户输入、外…...

Flink HA

目录 Flink HA集群规划 环境变量配置 masters配置 flink-conf.yaml配置 测试 Flink HA集群规划 FLink HA集群规划如下&#xff1a; IP地址主机名称Flink角色ZooKeeper角色192.168.128.111bigdata111masterQuorumPeerMain192.168.128.112bigdata112worker、masterQuorumPee…...

神经网络中如何优化模型和超参数调优(案例为tensor的预测)

总结&#xff1a; 初级&#xff1a;简单修改一下超参数&#xff0c;效果一般般但是够用&#xff0c;有时候甚至直接不够用 中级&#xff1a;optuna得出最好的超参数之后&#xff0c;再多一些epoch让train和testloss整体下降&#xff0c;然后结果就很不错。 高级&#xff1a;…...

使用AJAX发起一个异步请求,从【api_endpoint】获取数据,并在成功时更新页面上的【target_element】

使用AJAX发起一个异步请求&#xff0c;从【api_endpoint】获取数据&#xff0c;并在成功时更新页面上的【target_element】 在Web开发中&#xff0c;使用AJAX&#xff08;Asynchronous JavaScript and XML&#xff0c;异步JavaScript和XML&#xff09;可以实现在不刷新整个页面…...

【AI绘画教程】Stable Diffusion 1.5 vs 2

在本文中&#xff0c;我们将总结稳定扩散 1 与稳定扩散 2 辩论中的所有要点。我们将在第一部分中查看这些差异存在的实际原因&#xff0c;但如果您想直接了解实际差异&#xff0c;您可以跳下否定提示部分。让我们开始吧&#xff01; Stable Diffusion 2.1 发布与1.5相比&#x…...

纯前端小游戏,4096小游戏,有音效,Html5,可学习使用

// 游戏开始运行create: function(){this.fieldArray [];this.fieldGroup this.add.group();this.score 0;//4096 增加得分this.bestScore localStorage.getItem(gameOptions.localStorageName) null ? 0 : localStorage.getItem(gameOptions.localStorageName);for(var …...

ROS、pix4、gazebo、qgc仿真ubuntu20.04

一、ubuntu、ros安装教程比较多&#xff0c;此文章不做详细讲解。该文章基于ubuntu20.04系统。 pix4参考地址&#xff1a;https://docs.px4.io/main/zh/index.html 二、安装pix4 1. git clone https://github.com/PX4/PX4-Autopilot.git --recursive 2. bash ./PX4-Autopilot…...

qt 国际化语言,英文和中文切换

1、把需要翻译转换的内用用tr()包含&#xff0c;比如&#xff1a; label->setText("hello word"); 2、在 .pro 文件中添加 TRANSLATIONS lang_en.ts \ lang_zn.ts 3、利用lupdate 工具提取…...

机器学习入门【经典的CIFAR10分类】

模型 神经网络采用下图 我使用之后发现迭代多了之后一直最高是正确率65%左右&#xff0c;然后我自己添加了一些Relu激活函数和正则化&#xff0c;现在正确率可以有80%左右。 模型代码 import torch from torch import nnclass YmModel(nn.Module):def __init__(self):super(…...

01 安装

安装和卸载中&#xff0c;用户全部切换为root&#xff0c;一旦安装&#xff0c;普通用户也能使用 初期不进行用户管理&#xff0c;全部用root进行&#xff0c;使用mysql语句 1. 卸载内置环境 检查是否有mariadb存在&#xff0c;存在走a部分卸载 ps axj | grep mysql ps ajx |…...

AI 模型本地推理 - YYPOLOE - Python - Windows - GPU - 吸烟检测(目标检测)- 有配套资源直接上手实现

Python 运行 - GPU 推理 - windows 环境准备python 代码 环境准备 FastDeploy预编译库下载 conda config --add channels conda-forge && conda install cudatoolkit11.2 cudnn8.2 pip install fastdeploy_gpu_python-0.0.0-cp38-cp38-win_amd64.whlpython 代码 impo…...

全国媒体邀约,主流媒体到场出席采访报道

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 全国媒体邀约&#xff0c;确保主流媒体到场出席采访报道&#xff0c;可以带来一系列的好处&#xff0c;这些好处不仅能够增强活动的可见度&#xff0c;还能对品牌或组织的长期形象产生积…...

计算机视觉8 图像增广

图像增广&#xff08;image augmentation&#xff09;是通过对训练图像进行一系列随机改变&#xff0c;从而产生相似但又不同的训练样本的技术。 图像增广有以下两个主要作用&#xff1a; 扩大训练数据集的规模&#xff1b;随机改变训练样本可以降低模型对某些属性的依赖&#…...

Transformer中的自注意力是怎么实现的?

在Transformer模型中&#xff0c;自注意力&#xff08;Self-Attention&#xff09;是核心组件&#xff0c;用于捕捉输入序列中不同位置之间的关系。自注意力机制通过计算每个标记与其他所有标记之间的注意力权重&#xff0c;然后根据这些权重对输入序列进行加权求和&#xff0c…...

LabVIEW鼠标悬停在波形图上的曲线来自动显示相应点的坐标

步骤 创建事件结构&#xff1a; 打开LabVIEW&#xff0c;创建一个新的VI。 在前面板上添加一个Waveform Graph控件。 在后面板上添加一个While Loop和一个事件结构&#xff08;Event Structure&#xff09;。 配置事件结构&#xff0c;选择Waveform Graph作为事件源&#xf…...

操作系统发展简史(Unix/Linux 篇 + DOS/Windows 篇)+ Mac 与 Microsoft 之风云争霸

操作系统发展简史&#xff08;Unix/Linux 篇&#xff09; 说到操作系统&#xff0c;大家都不会陌生。我们天天都在接触操作系统 —— 用台式机或笔记本电脑&#xff0c;使用的是 windows 和 macOS 系统&#xff1b;用手机、平板电脑&#xff0c;则是 android&#xff08;安卓&…...

钡铼分布式 IO 系统 OPC UA边缘计算耦合器BL205

深圳钡铼技术推出的BL205耦合器支持OPC UA Server功能&#xff0c;以服务器形式对外提供数据。符合IEC 62541工业自动化统一架构通讯标准&#xff0c;数据可以选择加密&#xff08;X.509证书&#xff09;、身份验证方式传送。 安全策略支持basic128rsa15、basic256、basic256s…...

实现了一个心理测试的小程序,微信小程序学习使用问题总结

1. 如何在跳转页面中传递参数 &#xff0c;在 onLoad 方法中通过 options 接收 2. radio 如何获取选中的值&#xff1f; bindchange 方法 参数e, e.detail.value 。 如果想要获取其他属性&#xff0c;使用data-xx 指定&#xff0c;然后 e.target.dataset.xx 获取。 3. 不刷…...

vue是如何进行监听数据变化的?vue2和vue3分别是什么?vue3为什么要更换?

Vue如何进行监听数据变化的&#xff1f; Vue.js 通过其响应式系统来监听数据变化。这个系统允许你声明式地将数据和 DOM 绑定&#xff0c;一旦数据发生变化&#xff0c;相关的 DOM 将自动更新。Vue 使用以下机制来实现数据的监听和响应&#xff1a; 响应式数据&#xff1a;在 …...

数据结构day3

一、思维导图 二、 #include "seqlist.h"#include<myhead.h> int main(int argc, const char *argv[]) {//创建一个顺序表SeqListPtr L list_create();if(NULL L){return -1;}//调用添加函数list_add(L,123);list_add(L,435);list_add(L,856);list_add(L,65…...

免费的数字孪生平台助力产业创新,让新质生产力概念有据可依

关于新质生产力的概念&#xff0c;在如今传统企业现代化发展中被反复提及。 那到底什么是新质生产力&#xff1f;它与哪些行业存在联系&#xff0c;我们又该使用什么工具来加快新质生产力的发展呢&#xff1f;今天我将介绍一款为发展新质生产力而量身定做的数字孪生工具。 新…...

mtsys2 编译 qemu 记录

参考链接 下载 MSYS2 MSYS2 MSYS2 换源 进入目录\msys64\etc\pacman.d&#xff0c; 在文件mirrorlist.msys的前面插入 Server http://mirrors.ustc.edu.cn/msys2/msys/$arch在文件mirrorlist.mingw32的前面插入 Server http://mirrors.ustc.edu.cn/msys2/mingw/i686在…...

发布任务做任务赚钱网站/站内推广方式有哪些

1.__proto__和prototypeJS中的原型链已经是一个老生常谈的问题&#xff0c;毕竟也是JS 这门语言的特色之一了。首先“万物皆对象“&#xff0c;虽然这句话一直有争议&#xff0c;但是有它的道理的&#xff0c;null类型这些的争论这里就不说了。对象中有个属性__proto__&#xf…...

西宁高端网站建设/网络测试

转自&#xff1a;http://www.cnblogs.com/lhb25/p/useful-jquery-tips-and-tricks.html 今天&#xff0c;我们将分享一些很有用的技巧和窍门给 jQuery 开发人员。jQuery 是最好的 JavaScript 库之一&#xff0c;用于简化动画&#xff0c;事件处理&#xff0c;支持 Ajax 和 HTML…...

静态网站制作模板/福州网站优化

[get的过去式和过去分词]I got some fish at the market. Have they got back yet?与have 连用: have got: (1)占有,拥有(现在时): Ive got a new laptop. Have you got a coin? Shes got a nice smile.(2)表示义务、责任:Ive got to go now. What have you got to do today?…...

网站建设策划方案如何写/小程序seo推广技巧

问题&#xff1a;RadioButton中使用android:gravity"center"使其图片文字居中&#xff0c;在我的华为荣耀7手机上居中显示了&#xff0c;但在HUAWEI G606-T00却显示在右侧了。 解决&#xff1a;在RadioButton中设置属性 android:paddingLeft"0dp" 或者 and…...

桂林视频网站制作/绍兴seo网站管理

目录 01 百度的技术牌 一是降本增效的底层逻辑 二是智能应用的落地路径 02 阿里的整合牌 03 腾讯的生态牌 04 市场的新拐点 05 写在最后 2018年初的时候&#xff0c;工信部印发了《工业互联网发展行动计划&#xff08;2018-2020 年&#xff09;》&#xff0c;如同向整个…...

帝国cms做网站/百度seo标题优化软件

密码算法和协议&#xff1a;四大类 对称加密&#xff1a;用于加密任意大小的数据块数据内容&#xff0c;加密方和解密方使用的是同一个密码 公钥加密&#xff1a;&#xff08;非对称加密&#xff09;加密和解密使用的是不同的密码&#xff0c;有公钥和私钥&#xff0c;密…...