Linux高级进程通信

目录

第1关:socket之本地通信

任务描述

相关知识

创建 socket 流程

命名 socket

其他接口

编程要求

答案:

第2关:命名管道

任务描述

相关知识

命名管道的定义

命名管道的创建

命名管道的访问

命名管道的访问

编程要求

答案:

第3关:消息队列

任务描述

相关知识

消息队列的基本概念

消息队列的创建

发送消息

接收消息

控制函数

编程要求

答案 :


第1关:socket之本地通信

任务描述

试想这样的场景:

你有事情要告知同一个科室的同事 A ,但是你没有同事 A 的微信,你需要找同一个科室的同事 B 获取同事 A 的微信,然后给同事 A 发消息。

整个事件的过程如下:

你向同事 B 询问同事 A 的微信号;

同事 B 得到同事 A 的允许后将同事 A 的微信号告诉你;

你添加同事 A 为微信好友;

同事 A 确认并通过好友认证。

以本地 socket 通信代替整个事件的过程:创建 socket->链接socket->向对端发送消息->接收对端发过来的消息。

本关任务:

建立一个命名 socket,指定地址为本地 socket 文件;

连接 socket 成功后,发送消息到服务端;

接收从服务端来的消息。

相关知识

在 Linux 中,套接字 socket 通信是非常常见的通信方式。通信过程中采用三次握手,四次挥手的通信流程。

通常套接字 socket 通信是运用在网络上两台主机上,通信双方采用网络地址和端口号作为通信地址来进行通信。服务端绑定此地址,然后开始监听此地址;客户端连接此地址即可。

在本地通信中,socket 进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。Socket 通信在另外的实训中已有介绍,对此不太熟悉的学员可以自行查阅下。

创建 socket 流程

创建 socket 采用系统调用 socket ,原型如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
int socket(int domain, int type, int protocol);

参数详解如下:

domain:参数指定协议族,对于本地套接字来说,其值须被置为 AF_UNIX ;

type:参数指定套接字类型,可被设置为 SOCK_STREAM (流式套接字)或 SOCK_DGRAM (数据报式套接字);对于本地套接字来说, SOCK_STREAM 是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道; SOCK_DGRAM 相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,但由于其在本地通信,不通过外界网络,这些情况出现的概率很小。

protocol :参数指定具体协议,protocol 字段应被设置为 0 。

返回值为生成的套接字描述符。

命名 socket

本地套接字的通信双方均需要具有本地地址,其中本地地址需要明确指定,指定方法是使用struct sockaddr_un类型的变量。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
struct sockaddr_un {sa_family_t     sun_family;     /* AF_UNIX */char    sun_path[UNIX_PATH_MAX];        /* 路径名 */
};

socket 根据此命名创建一个同名的 socket 文件,客户端连接的时候通过读取该 socket 文件连接到 socket 服务端。

应用示例如下:
 

//name the server socket   
server_addr.sun_family = AF_UNIX;  
strcpy(server_addr.sun_path,"/tmp/UNIX.domain");  
server_len = sizeof(struct sockaddr_un);  
client_len = server_len;  
其他接口

其他接口,比如服务端的listenaccept,客户端的connect等均没有区别。

编程要求

在本实训中,后台存在一个用于与客户端之间通信的服务端,学员需要实现的代码位于客户端。

本关的编程任务是完成客户端的代码编写,补全右侧代码片段localsocket_testBeginEnd中间的代码,具体要求如下:

首先建立一个命名 socket ,指定地址为./socket_test;

连接 socket 成功后,发送消息(发送内容为 localsocket_test 中的参数 buffer)到服务端;

接收从服务端来的消息。关闭 socket ;

如果接收成功返回 0,接受失败返回 -1。

答案:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>int localsocket_test(const char *buffer, char *recv_buff, int length)
{/*********begin***********/struct sockaddr_un server_addr;//创建socketint sockfd=socket(AF_UNIX,SOCK_STREAM,0);//设置服务器地址server_addr.sun_family=AF_UNIX;strncpy(server_addr.sun_path,"./socket_test",sizeof(server_addr.sun_path)-1);//连接socketconnect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));//发送消息到服务端write(sockfd,buffer,strlen(buffer));//send(sockfd,buffer,strlen(buffer),0);也可//接收从服务端发来的消息read(sockfd,recv_buff,length-1);//recv(sockfd,recv_buff,length-1,0);也可//关闭socketclose(sockfd);return 0;/**********end************/
}

第2关:命名管道

任务描述

试想这样的场景:

你想请工程部来建筑一个别墅,但是你没有工程部的联系方式,恰好你的好友 Mike 与工程部非常熟,你打电话给你的好友,希望可以与工程部经理进行三方通话并传递别墅需求。

整个事件的过程如下:

你给好友打电话告知你的别墅需求;

好友让你等下,他把工程部经理加进来进行三方通话;

好友把工程部经理加入三方通话;

你把别墅需求直接告知工程部经理;

谈话结束,你挂断电话;

工程部经理也挂断电话。

以命名管道代替整个事件的过程,创建命名管道->打开命名管道->等待对端打开命名管道->发送消息->关闭命名管道。

本关任务:

检测命名管道文件是否存在,如若不存在则创建道;

以阻塞的方式打开命名管道;

通过命名管道发送消息;

关闭命名管道。

相关知识

在 Linux 中,命名管道是一种比较常见的进程间通信方式。相对于其他类型管道来说,命名管道弥补了通信的双方进程需要有共同祖先的弊端,这给我们在不相关的的进程之间交换数据带来了可能性。

命名管道的定义

命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为和其他管道类似。

由于 Linux 中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,使用也非常方便,同时我们也可以像平常的文件名一样在命令中使用。

命名管道的创建

创建命名管道的函数原型如下:

#include <sys/types.h>  
#include <sys/stat.h>  
int mkfifo(const char *filename, mode_t mode);  

参数详解如下:
 

filename:命名管道文件的路径,类似"/tmp/my. fifo ";

mode:命名管道文件的模式,表明执行此文件的权限,类似"0666"。
 

返回值:

若成功则返回 0,否则返回 -1,错误原因存于 errno 中。

命名管道的访问

mkfifo 函数只是创建一个 FIFO文件 ,要使用命名管道首先要将其打开,与打开其他文件一样, FIFO文件 也可以使用 open 调用来打开。

open 函数的相关知识这里就不做赘述了,但是这里要强调四点:

不能以 O_RDWR 模式打开 FIFO文件 进行读写操作,而其行为也未明确定义,因为如果以读/写方式打开一个管道,进程就会读回自己的输出,同时我们通常使用 FIFO 只是为了单向的数据传递

传递给 open 调用的是 FIFO 的路径名,而不是正常的文件。

当使用 O_NONBLOCK 时,打开 FIFO文件 来读取的操作会立刻返回,但是若还没有其他进程打开 FIFO文件 来读取,则写入的操作会返回 ENXIO 错误代码;

当没有使用 O_NONBLOCK 时,打开 FIFO 来读取的操作会等到其他进程打开 FIFO文件 来写入才正常返回。同样地,打开 FIFO文件 来写入的操作会等到其他进程打开 FIFO 文件 来读取后才正常返回。

核心代码如下:

参数详解如下:

filename:命名管道文件的路径,类似"/tmp/my. fifo ";

mode:命名管道文件的模式,表明执行此文件的权限,类似"0666"。

返回值:

若成功则返回 0,否则返回 -1,错误原因存于 errno 中。

命名管道的访问

mkfifo 函数只是创建一个 FIFO文件 ,要使用命名管道首先要将其打开,与打开其他文件一样, FIFO文件 也可以使用 open 调用来打开。

open 函数的相关知识这里就不做赘述了,但是这里要强调四点:

不能以 O_RDWR 模式打开 FIFO文件 进行读写操作,而其行为也未明确定义,因为如果以读/写方式打开一个管道,进程就会读回自己的输出,同时我们通常使用 FIFO 只是为了单向的数据传递

传递给 open 调用的是 FIFO 的路径名,而不是正常的文件。

当使用 O_NONBLOCK 时,打开 FIFO文件 来读取的操作会立刻返回,但是若还没有其他进程打开 FIFO文件 来读取,则写入的操作会返回 ENXIO 错误代码;

当没有使用 O_NONBLOCK 时,打开 FIFO 来读取的操作会等到其他进程打开 FIFO文件 来写入才正常返回。同样地,打开 FIFO文件 来写入的操作会等到其他进程打开 FIFO 文件 来读取后才正常返回。

核心代码如下:

/*客户端*/
...... 
int res = mkfifo(fifo_name, 0777);
char buffer[1024] = {0};
strncpy(buffer, "this is client.", strlen("this is client."));
if (res == 0)
{int pipe_fd = open(fifo_name, O_WRONLY);
int bytes_write= write(pipe_fd, buffer, bytes_read); 
if(0 < bytes_write)
{
printf("send message successful.
");
}
......
}
/*服务端*/
...... 
int res = mkfifo(fifo_name, 0777);
char buffer[1024] = {0};
if (res == 0)
{int pipe_fd = open(fifo_name, O_RDONLY);
int bytes_read = read(pipe_fd, buffer, PIPE_BUF);  
if(0 < bytes_read)
{
printf("receive: %s
", buffer);
}
......
}

测试步骤:

分别运行./server./client

运行结果:

send message successful.receive : this is client.
编程要求

在本实训中,存在一个服务端用于与客户端之间的通信。

本关的编程任务是完成客户端的代码编写,补全右侧代码片段namepipe_commuBeginEnd中间的代码,具体要求如下:

检测命名管道文件./my_fifo是否存在,如若不存在则创建命名管道文件;

可读、阻塞的方式打开命名管道;

通过命名管道发送消息(namepipe_commu 的参数 buffer );

关闭命名管道。

答案:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>void namepipe_commu(const char *buffer)
{/***********begin***********/const char *fifo_path="./my_fifo";//检测命名管道文件./my_fifo是否存在int fd=open(fifo_path,O_WRONLY);//以可读、阻塞的方式打开命名管道if (write(fd,buffer,strlen(buffer))==-1){//通过命名管道发送消息(namepipe_commu的参数buffer)close(fd);return;}/***********end***********/
}

第3关:消息队列

任务描述

试想这样的场景:

你辅助你学弟的专业学习,你把相关的书籍不定期的放在图书馆的 1-2-58 书架上,学弟每次学完一本后会去拿之后的书籍。

整个事件的过程如下:

你打开书架;

每隔一段时间,你放上一本书;

学弟打开书架;

每学完一本书后,学弟再取走一本书。

以消息队列代替整个事件的过程,创建消息队列>发送消息->对方取走消息->删除消息队列。

本关任务:

创建消息队列;

每隔 1s 发送一则消息,共发送三条,分别为"C", "Linux", "Makefile";

最后发送一条"End"消息;

等待 10s ,删除消息队列。

相关知识

在 Linux 中,每种通讯方式应用的主流场景不同,在异步处理或者不需要很高的实时性的场景下,比较频繁的是消息队列。

消息队列的基本概念

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。

消息队列中的每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题

消息队列与命名管道一样,每个数据块都有一个最大长度的限制。在 Linux 系统中,使用宏 MSGMAX 和 MSGMNB 来限制一条消息的最大长度和一个队列的最大长度。

消息队列是链表队列,它通过内核提供一个struct msqid_ds *msgque[MSGMNI]向量维护内核的一个消息队列列表,因此 Linux 系统支持的最大消息队列数由 msgque 数组大小来决定,每一个 msqid_ds 表示一个消息队列,并通过msqid_ds.msg_first、msg_last 维护一个先进先出msg链表队列。

发送一个消息到该消息队列时,把发送的消息构造成一个 msg 结构对象,并添加到msqid_ds.msg_first、msg_last 维护的链表队列;当接收消息的时候也是从**msg链表队列尾部查找到一个 msg_type 匹配的 msg 节点,从链表队列中删除该 msg 节点,并修改** msqid_ds 结构对象的数据。

消息队列的创建

创建命名管道的函数原型如下:

参数详解如下:

key:消息队列关联的键,比如 1024;

msgflg:消息队列的建立标志和存取权限;使用 IPC_CREAT 时,如果内核中没有此队列,则创建队列;当 IPC_EXCL 和 IPC_CREAT 一起使用时,如果队列已经存在,则失败。

返回值:

成功执行时,返回消息队列标识值;失败返回 -1。

发送消息
#include <sys/msg.h>
int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);  

参数详解如下:

msgid:msgget 函数返回的消息队列标识符;

msg_ptr:一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针 msg_ptr 所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型;

struct my_message
{    
long int message_type;    
/* The data you wish to transfer*/    
}; 

msg_sz:msg_ptr 指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,不包括长整型消息类型成员变量的长度

msgflg:用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情,一般设为 0。
返回值:

如果调用成功,消息数据的一分副本将被放到消息队列中,并返回 0,失败时返回 -1。

接收消息
#include <sys/msg.h>
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);   
参数详解如下: msgid,msg_ptr,msg_st 和 msgflg 的作用和函数 msgsnd 的一样。

msgtype:实现一种简单的接收优先级。如果 msgtype 为 0,就获取队列中的第一个消息。如果它的值大于 0,将获取具有相同消息类型的第一个信息。如果它小于 0,就获取类型等于或小于 msgtype 的绝对值的第一个消息;

返回值:

失败时返回 -1。

控制函数
#include <sys/msg.h>
int msgctl(int msgid, int command, struct msgid_ds *buf);  

参数详解如下:

msgid 的作用和函数 msgsnd 的一样。

command :将要采取的动作,一般使用 IPC_RMID ,用于删除消息队列;

buf :用于删除消息队列时,一般设为 NULL 。

返回值:

成功时返回 0,失败时返回 -1。

编程时使用的核心代码如下:

/*客户端*/
#include <sys/msg.h>
struct msg_st
{long int msg_type;char text[BUFSIZ];
};
int main(int argc, char *argv[])
{......
struct msg_st data;
......
//创建消息队列
int msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
.......
data.msg_type = 6;//指定消息类型
strncpy(data.text, "this is client.", strlen("this is client."));
msgsnd(msgid, (void *)&data, MAX_TEXT, 0);
printf("send message successful.
");
......
}
/*服务端*/
#include <sys/msg.h>
struct msg_st
{long int msg_type;char text[BUFSIZ];
};
int main(int argc, char *argv[])
{......
struct msg_st data;
......
//创建消息队列
int msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
......
msgtype = 6;//指定接收消息的类型
memset(&data, 0, sizeof(struct msg_st));
msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0);
printf("receive: %s
", data.text);
......
//删除消息队列
msgctl(msgid, IPC_RMID, NULL);
}

测试步骤:

分别运行./server./client

运行结果:
 

send message successful.receive : this is client.

编程要求

在本实训中,存在一个服务端用于与客户端之间的通信。

本关的编程任务是完成发送方的代码编写,补全右侧代码片段mq_commuBeginEnd中间的代码,具体要求如下:

创建 key 为 0x1234 的消息队列;

每隔 1s 发送一则消息,共发送三条,分别为"C", "Linux", "Makefile",消息类型均为 66 ;

最后发送一条"End"消息;

等待 10s ,删除消息队列。

答案 :
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>struct msgbuf
{long mytype;char bookname[100];
};void mq_commu (void)
{/*********Begin*********///创建key为0x1234key_t key=0x1234;//创建消息队列int msgid=msgget(key,IPC_CREAT|0666);struct msgbuf message;message.mytype=66;//发送三条消息strcpy(message.bookname,"C");msgsnd(msgid,&message,sizeof(struct msgbuf)-sizeof(long),0);sleep(1);//等待一秒strcpy(message.bookname,"Linux"); msgsnd(msgid,&message,sizeof(struct msgbuf)-sizeof(long),0);sleep(1);//等待一秒strcpy(message.bookname,"Makefile");msgsnd(msgid, &message,sizeof(struct msgbuf)-sizeof(long),0);sleep(1);//等待一秒//最后发送一条"End"消息strcpy(message.bookname,"End");msgsnd(msgid,&message,sizeof(struct msgbuf)-sizeof(long),0);/**********End**********/
}

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

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

相关文章

五月加仓比特币

作者&#xff1a;Arthur Hayes Co-Founder of 100x. 编译&#xff1a;Liam 编者注&#xff1a;本文略有删减 (以下内容仅代表作者个人观点&#xff0c;不应作为投资决策的依据&#xff0c;也不应被视为参与投资交易的建议或意见&#xff09;。 从四月中旬到现在&#xff0c;当你…

flask框架的初步认识

flask框架的初步认识 这是一个轻量级的网页框架&#xff0c;在运行后&#xff0c;就相当于服务器&#xff0c;当用户输入URL就会触发对应的事件调用方法&#xff0c;返回给用户一个网页文件&#xff0c;并通过自动识别html标签&#xff0c;来为用户呈现对应的样式和效果&#…

小红书达人置换合作推广怎么做?

小红书作为国内领先的生活方式分享平台&#xff0c;已成为品牌与消费者沟通的重要桥梁。达人置换合作推广&#xff0c;即品牌与小红书上的意见领袖&#xff08;KOL&#xff09;合作&#xff0c;通过他们的影响力推广产品&#xff0c;已成为品牌营销的重要手段。本文伯乐网络传媒…

wc文件统计功能 xargs network 静态IP

wc命令&#xff0c;word count&#xff0c;文件统计功能 # wc [选项] 文件名称 选项说明&#xff1a; -l : 统计总行数 -w : word&#xff0c;总单词数 -c : 统计总字节数 案例&#xff1a;统计/根目录下一共有多少个文件 # ls / | wc -l 案例&#xff1a;用户在计算机中有…

【光速上手 Hydra 】一行代码自动跑多次实验,Hydra 中的 Multirun 参数如何使用?

Hydra 是一个开源的 Python 框架&#xff0c;简化了研究和其他复杂应用的开发。其关键特性是能够通过组合动态地创建一个分层次的配置&#xff0c;并通过配置文件和命令行进行覆盖。Hydra 的名称来源于其能够运行多个类似的作业 - 就像一个有多个头的九头蛇一样。 主要特性&am…

TikTok 正式起诉美国政府;全新 iPad Pro 将搭载苹果 M4 芯片丨 RTE 开发者日报 Vol.199

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」&#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

Spring从零开始学使用系列(四)--Spring框架中的Bean作用域:Singleton与Prototype详解

1. 引言 在复杂的企业级应用开发中&#xff0c;理解和正确使用Spring框架的Bean作用域至关重要。Bean作用域决定了Bean的生命周期&#xff0c;即Bean的创建、存在期及销毁的时机。Spring通过灵活的作用域管理&#xff0c;为开发者提供了强大的工具来优化应用性能和资源利用。本…

机器学习 | 时间序列预测中的AR模型及应用

自回归模型&#xff0c;通常缩写为AR模型&#xff0c;是时间序列分析和预测中的一个基本概念。它们在金融、经济、气候科学等各个领域都有广泛的应用。在本文中&#xff0c;我们将探索自回归模型&#xff0c;它们如何工作&#xff0c;它们的类型和实际例子。 自回归模型 自回…

Rust读写CSV文件 一维Vec类型元素、二维Vec类型元素写入CSV文件

本文主要介绍Rust读写CSV文件方法&#xff0c; Vec类型元素基本操作方法&#xff0c;Rust把一维Vec类型元素、二维Vec类型元素写入CSV文件方法。 实例测试&#xff1a; 要求读“log.csv”文件数据&#xff0c;把“时间”列数据和“次数”列数据写入日志处理结果1.csv文件&…

五一 Llama 3 超级课堂 大完结

首先很感谢上海人工智能实验室和机智流等相关举办单位组织的这个活动&#xff0c;在Llama3发布不多时就让我们可以体验到大模型的进步&#xff0c;回顾整个活动&#xff0c;从内容上看是相当用心的。从A100的提供使用到大模型部署&#xff0c;微调&#xff0c;Agent功能应用和数…

Python中tkinter编程入门1

1 tkinter库简介 tkinter是Python的标准库&#xff0c;用来进行GUI&#xff08;Graphical User Interface&#xff0c;图形用户界面&#xff09;编程。 2 导入tkinter库 tkinter是Python默认的GUI库&#xff0c;因此&#xff0c;IDLE中已经包含了该库&#xff0c;使用时无需…

区块链领域,被问得最多的问题

在区块链领域&#xff0c;被问得最多的问题通常围绕几个核心主题&#xff0c;包括基本概念、技术挑战、应用前景以及它对社会经济的影响。以下是几个经常被提及的问题&#xff1a; 什么是区块链&#xff1f; 人们常常好奇区块链的基本定义&#xff0c;以及它是如何作为一种去中…

【网络编程】HTTPS协议详解

引言 HTTPS是HTTP协议的安全版本&#xff0c;通过使用SSL&#xff08;安全套接层&#xff09;或TLS&#xff08;传输层安全&#xff09;协议加密通信&#xff0c;为数据传输提供了保密性、完整性和身份认证。与传统的HTTP相比&#xff0c;HTTPS能够防止敏感信息被窃取、篡改和…

【HTTP下】总结{重定向/cookie/setsockopt/流操作/访问网页/总结}

文章目录 1.请求头2.cookie理解 3.vim跳转/搜索4.setsockopt被重用的意思 5.流操作5.1定位读取指针5.2ifstram::read() 6.总结6.1 百度搜索框搜索功能字符6.2请求uri请求和响应的第一行都有http版本请求内容里有GET /favicon.ico HTTP/1.1 6.3访问网页Fiddler抓包原理&#xff…

Python进阶之-hashlib详解

✨前言&#xff1a; &#x1f31f;什么是hash&#xff1f; 在计算机科学中&#xff0c;“哈希” (Hash) 是一种算法&#xff0c;它接受输入&#xff08;或者“消息”&#xff09;&#xff0c;并返回一个固定长度的字符串&#xff0c;这个字符串称为输入的 “哈希值” 或者 “…

ASP.NET MVC 4升级迁移到ASP.NET MVC 5

背景&#xff1a;今天针对一个老项目进行框架升级&#xff0c;老项目使用的是MVC 4&#xff0c;现在要升级到MVC5。 备份项目.NET升级4.5以上版本通过Nuget&#xff0c;更新或者直接安装包 包名oldVersionnewVersionMicrosoft.AspNet.Mvc4.0.05.x.xMicrosoft.AspNet.Razor2.0…

树莓派4b测量PM2.5

1.GP2Y1010AU0F粉尘传感器连接图 2. GP2Y1010AU0F工作原理 工作原理 传感器中心有个洞可以让空气自由流过,定向发射LED光,通过检测经过空气中灰尘折射过后的光线来判断灰尘的含量。 3.源代码 main.py # coding=UTF-8 import RPi.GPIO as GPIO from ADC import ADS1015…

上传文件客户端签名直传

上传文件的客户端签名直传是一种常用的将文件直接上传到云存储服务如Amazon S3、阿里云OSS等云服务上的技术手段。这种方法通常用于减轻服务器负担&#xff0c;并提高上传效率。 以下是实现客户端签名直传的基本步骤&#xff1a; 1. 客户端请求上传权限 客户端&#xff08;如…

如何让路由器分配固定网段ip

一.wan和lan wan广域网&#xff0c;负责连接互联网 lan局域网&#xff0c;负责保证一个区域内的设备可以互相通讯&#xff0c;比如wife就是让所有连接设备处于同一网段下 一.问题导入 1.我们平时在虚拟机和实体机通信时 必须让它们位于同一ip网段下。 通过winscp等软件进行…

链表的原理和实现python

为什么需要链表 数组的底层原理是顺序存储&#xff0c;是一块连续的内存空间&#xff0c;有了这块内存空间的首地址&#xff0c;就能直接通过索引计算出任意位置的元素地址。 链表不一样&#xff0c;一条链表并不需要一整块连续的内存空间存储元素。链表的元素可以分散在内存…