linux下IO模及其特点及select

ftp实现

模拟FTP核心原理:客户端连接服务器后,向服务器发送一个文件。文件名可以通过参数指定,服务器端接收客户端传来的文件(文件名随意),如果文件不存在自动创建文件,如果文件存在,那么清空文件然后写入。

功能要求:

1.项目基于tcp连接进行编写

2.  客户端命令行传参,传入ip、port、文件路径,实现把指定目录下的文件发送到服务器

3.  服务器接收并放到指定文件路径

linux下IO模及其特点

场景假设

假设妈妈有一个孩子,孩子在房间里睡觉,妈妈需要及时获知孩子是否醒了,如何做?

1.  进到房间陪着孩子一起睡觉,孩子醒了会吵醒妈妈:不累,但是不能干别的了

2.  时不时进房间看一下:简单,空闲时间还能干点别的,但是很累

3.  妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈:互不耽误

一、Linux下四种模型的特点: 

阻塞式IO    非阻塞式IO    信号驱动IO(了解)     IO多路复用(帮助TCP实现并发)

1、阻塞式IO(BIO)

特点:简单、常用、效率低

● 当程序调用某些接口时,如果期望的动作无法触发,那么进程会进入阻塞态(等待状态,让出CPU的调度),当期望动作可以被触发了,那么会被唤醒,然后处理事务。

● 重点理解相对于进程而言的影响;

● 阻塞I/O模式是最普遍使用的I/O模式,大部分程序使用的都是阻塞模式的I/O 。

● 前面学习的很多读写函数在调用过程中会发生阻塞。

阻塞I/O 模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。
缺省情况下(及系统默认状态),套接字建立后所处于的模式就是阻塞I/O 模式。
学习的读写函数在调用过程中会发生阻塞相关函数如下:
•读操作中的read、recv、recvfrom
     读阻塞--》需要读缓冲区中有数据可读,读阻塞解除
•写操作中的write、send
     写阻塞--》阻塞情况比较少,主要发生在写入的缓冲区的大小小于要写入的数据量的情况下,写操作不进行任何拷贝工作,将发生阻塞,一旦缓冲区有足够的空间,内核将唤醒进程,将数据从用户缓冲区拷贝到相应的发送数据缓冲区。 
注意:sendto没有写阻塞
 1)无sendto函数的原因:
sendto不是阻塞函数,本身udp通信不是面向连接的,udp无发送缓冲区,即sendto没有发送缓冲区,send是有发送缓存区的,即sendto不是阻塞函数。
 2)UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在缓冲区满的情况,在UDP套接字上进行写操作永远不会阻塞。
•其他操作:accept、connect

udp与tcp缓存区  仅作为了解

UDP通信没有发送缓存区, 它不保证数据的可靠性。因此,UDP通信是将数据尽快发送出去,不关心数据是否到达目标主机. 但是UDP有接受缓存区, 因为数据发送过快, 如果接收缓存区内数据已满, 则继续发送数据, 可能会出现丢包

丢包出现原因:  接收缓存区满       网络拥堵, 传输错误    

相比之下,TCP是一种面向连接的传输协议,它需要保证数据的可靠性和顺序性。TCP有发送缓存区和接收缓存区, 如果发送频率过快, 且内容小于发送缓存区的大小 , 可能会导致多个数据的粘包。如果发送的数据大于发送缓存区, 可能会导致拆包

UDP不会造成粘包和拆包,  TCP不会造成丢包

UDP是基于数据报文发送的,每次发送的数据包,在UDP的头部都会有固定的长度, 所以应用层能很好的将UDP的每个数据包分隔开, 不会造成粘包。

TCP是基于字节流的, 每次发送的数据报,在TCP的头部没有固定的长度限制,也就是没有边界,那么很容易在传输数据时,把多个数据包当作一个数据报去发送,成为了粘包,或者传输数据时, 要发送的数据大于发送缓存区的大小,或者要发送的数据大于最大报文长度, 就会拆包;

TCP不会丢包,因为TCP一旦丢包,将会重新发送数据包。(超时/错误重传)

TCP: 

UDP: 

2、非阻塞式IO(NIO)

     特点:可以处理多路IO;需要轮询,浪费CPU资源

•当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:“当我请求的I/O 操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返回一个错误给我。”
•(引导着让大家说出来)当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做polling)。---轮询
•应用程序不停的polling 内核来检查是否I/O操作已经就绪。这将是一个极浪费CPU 资源的操作。
•这种模式使用中不普遍。

如何设置非阻塞

1)  通过函数自带参数设置

2)  通过设置文件描述符属性fcntl (file control)

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
功能:
   获取/改变文件属性(linux中一切皆文件)
文件描述符:stdin 0、stdout 1stderr 2
	参数:fd:文件描述符
         cmd: 操作功能选项 (可以定义个变量,通过vi -t F_GETFL 来找寻功能赋值 )
          F_GETFL:获取文件描述符的原有的状态信息 
           //不需要第三个参数,返回值为获取到的属性
          F_SETFL:设置文件描述符的状态信息 - 需要填充第三个参数
         //需要填充第三个参数  O_RDONLY, O_RDWR ,O_WRONLY ,O_CREAT
          O_NONBLOCK 非阻塞   O_APPEND追加
          O_ASYNC 异步        O_SYNC  同步 
          F_SETOWN:    可以用于实现异步通知机制。
          //当文件描述符上发生特定事件时(例如输入数据到达),内核会向拥有该  文件描述符的进程发送 SIGIO 信号(异步),以便进程能够及时处理这些事件。
第三个参数:由第二个参数决定,set时候需要设置的值,get时候填0
arg:文件描述符的属性     ----------同上参数,一般填0
        

返回值: 特殊选择:根据功能选择返回 (int 类型)   
            其他:  成功0   失败: -1;
设置流程:
    int flag;//文件状态的标志 
    flag = fcntl(fd, F_GETFL); //读 
    flag |= O_NONBLOCK;//改  O_NONBLOCK = 0x00004000
    fcntl(fd, F_SETFL, flag);//写

3、信号驱动IO(异步IO模型  非重点)

特点:异步通知模式,需要底层驱动的支持

操作系统中的同步与异步
在操作系统中,特别是在Linux中,同步和异步是描述I/O操作方式的两个概念。它们主要区分在于操作完成的通知方式和程序执行的流程。

同步(Synchronous):

同步I/O操作是指在执行I/O操作时,程序必须等待操作完成才能继续执行。在同步操作中,程序提交一个I/O请求后,操作系统会阻塞该程序,直到请求操作完成。此时,程序才能继续执行后续的代码。因此,同步操作会导致程序执行流程暂停,直至I/O操作完成。
同步I/O的例子:read(), write(), recv(), send() 等。

异步(Asynchronous):
异步I/O操作是指程序在发起I/O请求后,无需等待操作完成,可以继续执行其他任务。当异步I/O操作完成时,程序会通过某种方式(如回调函数、事件通知、信号等)得到通知。因此,异步操作使程序执行流程得以继续,而不必等待I/O操作完成。

●  通过信号方式,当内核检测到设备数据后,会主动给应用发送信号SIGIO。

SIGIO
文件描述符准备就绪, 可以开始进行输入/输出操作.

● 应用程序收到信号后做异步处理即可。

● 应用程序需要把自己的进程号告诉内核,并打开异步通知机制。

● 标准模板

//将APP进程号告诉驱动程序
fcntl(fd, F_SETOWN, getpid());

//使能异步通知
int flag;
flag = fcntl(fd, F_GETFL);
flag |= O_ASYNC;  //也可以用FASYNC标志
fcntl(fd, F_SETFL, flag);

signal(SIGIO, handler);

signal信号处理相关函数

头文件: #include <signal.h>

        typedef void (*sighandler_t)(int);

        sighandler_t   signal(int signum, sighandler_t handler)

功能:信号处理函数(注册信号)

参数: int signum:要处理的信号(要修改的信号)

           sighandler_t handler: 函数指针: void(*handler)(int) (修改的功能:)

           handler:------void handler(int num) 自定义的信号处理函数指针

返回值: 成功:设置之前的信号处理方式

失败:   SIG_ERR

用非阻塞方式监听鼠标的数据

查看自己使用的鼠标:/dev/input    

检查鼠标设备:sudo cat /dev/input/mouse0

 注意:执行的时候需要加sudo

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
int fd;
#define N 64
char buf[N] = {0};
void handler(int sig)
{
    int ret;
    ret = read(fd, buf, N);
    if (ret < 0)
    {
        perror("READ ERR.");
        return;
    }
    else
    {
        printf("len= %d\n", ret);
    }
}
int main(int argc, char const *argv[])
{

    fd = open("/dev/input/mouse0", O_RDONLY);
    if (fd < 0)
    {
        perror("open err");
        return -1;
    }
    // 将APP进程号告诉驱动程序
    fcntl(fd, F_SETOWN, getpid());
    // 使能异步通知
    int flag;
    flag = fcntl(fd, F_GETFL);
    flag |= O_ASYNC; // 也可以用FASYNC标志
    fcntl(fd, F_SETFL, flag);

    signal(SIGIO, handler);

    while (1)
    {
        printf("-----------\n");
        sleep(1);
    }
    close(fd);

    return 0;
}

4.IO多路复用

4.1、IO多路复用场景假设

假设妈妈有三个孩子,分别不同的房间里睡觉,需要及时获知每个孩子是否醒了,如何做?

1.  挨个房间跑

4.2、IO多路复用机制 

 I/O多路复用  -  帮助TCP实现并发服务器

1.  进程中若需要同时处理多路输入输出 ,在使用单进程和单线程的情况下, 可使用IO多路复用处理多个请求;

2.  IO多路复用不需要创建新的进程和线程, 有效减少了系统的资源开销。

场景就比如服务员给50个顾客点餐,分两步:

       顾客思考要吃什么(等待客户端数据发送)

       顾客想好了,开始点餐(接收客户端数据)

要提高效率有几种方法? 

1.   安排50个服务员   (类似于多进程/多线程实现服务器连接多个客户端,太占用资源)

2.  哪个顾客想好了吃啥, 那个顾客来柜台点菜 (类似IO多路复用机制实现并发服务器)

实现IO多路复用的方式:  select   poll   epoll

基本流程是:

1. 先构造一张有关文件描述符的表;  

2. 清空表   

3. 将你关心的文件描述符加入到这个表中;   

4. 调用select函数。 

5. 判断是哪一个或哪些文件描述符产生了事件(IO操作);   

6. 做对应的逻辑处理;

● 使用I/O多路复用技术。其基本思想是:

○ 先构造一张有关描述符的表,然后调用一个函数。

○ 当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。

○ 函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

基本流程:
1. 先构造一张有关文件描述符的表(集合、数组);    
2. 将你关心的文件描述符加入到这个表中;          
3. 然后循环调用一个函数。 select / poll          
4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候
该函数才返回(阻塞)。                            
5. 判断是哪一个或哪些文件描述符产生了事件(IO操作);
6. 做对应的逻辑处理;

4.3、select :用于监测是哪个或哪些文件描述符产生事件;

#include<sys/select.h>   
#include<sys/time.h>   
#include<sys/types.h>   
#include<unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
   功能:select用于监测是哪个或哪些文件描述符产生事件; 
   参数:
    nfds:监测的最大文件描述个数(文件描述符从0开始,这里是个数,记得+1)
(这里是个数,使用的时候注意,与文件中最后一次打开的文件描述符所对应的值的关系是什么?)
         readfds:  读事件集合; // 键盘鼠标的输入,客户端连接都是读事件
         writefds: 写事件集合;  //NULL表示不关心
         exceptfds:异常事件集合;  //NULL 表示不关心
         timeout:   设为NULL,等待直到某个文件描述符发生变化;
                       设为大于0的值,有描述符变化或超时时间到才返回。
        超时时间检测:如果规定时间内未完成函数功能,返回一个超时的信息,我们可以根据该信息设定相应需求;
   如果设置了超时检测时间:&tv
      select返回值:
         <0 出错
        >0 表示有事件产生;
        ==0 表示超时时间已到;

 struct timeval 
 {
      long    tv_sec;      /* seconds */以秒为单位,指定等待时间
      long    tv_usec;    /* microseconds */以毫秒为单位,指定等待时间
 };
 void FD_CLR(int fd, fd_set *set);//将fd从表中清除
 int  FD_ISSET(int fd, fd_set *set);//判断fd是否在表中
 void FD_SET(int fd, fd_set *set);//将fd添加到表中
 void FD_ZERO(fd_set *set);//清空表1

select特点:

1. 一个进程最多只能监听1024个文件描述符 (32位)   [64位为 2048]

2. select被唤醒之后要重新轮询(0-1023)一遍驱动,效率低(消耗CPU资源)

       3. select每次会清空未响应的文件描述符,每次都需要拷贝用户空间的表到内核空间,效率低,开销较大

   (0~3G是用户态,3G~4G是内核态,两个状态来回切换  拷贝是非常耗时,耗资源的)

select机制(辅助理解): 

              1. 头文件检测1024个文件描述符  0-1023

              2. 在select中0~2存储标准输入、标准输出、标准出错    

        3. 监测的最大文件描述个数为fd+1(如果fd = 3,则最大为 4) :  //因为从0开始的    

              4. select只对置1的文件描述符感兴趣                  假如事件产生,select检测时 , 产生的文件描述符会保持1,未产生事件的会置0; 

         5. select每次轮询都会清空表(置零的清空)   //需要在select前备份临时表

练习1: 如何通过select实现 响应鼠标事件同时响应键盘事件

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>

//响应鼠标的时候, 打印鼠标事件 
//输入键盘的时候, 打印键盘内容

int main(int argc, char const *argv[]) 
{
    //1.打开鼠标文件
    int fd = open("/dev/input/mouse0",O_RDONLY);
    if(fd < 0)
    {
        perror("open is err:");
        return -1;
    }
    //1.创建文件描述符的表
    fd_set readfds,tempfds;
    //2.清空表
    FD_ZERO(&readfds);
    //3.添加关心的文件描述符
    FD_SET(0,&readfds);
    FD_SET(fd,&readfds);
    
    int maxfd = fd;

    char buf[128];
    while(1)
    {
        tempfds = readfds;
        //4.select检测   阻塞
        select(maxfd+1,&tempfds,NULL,NULL,NULL);

        if(FD_ISSET(0,&tempfds))
        {
          //1.键盘
          fgets(buf,sizeof(buf),stdin);
          if(buf[strlen(buf)-1] == '\n')
             buf[strlen(buf)-1] = '\0';
           printf("key: %s\n",buf);
        }
        if(FD_ISSET(fd,&tempfds))
         {
            //2.鼠标
            int ret = read(fd,buf,sizeof(buf));
            buf[ret] = '\0';
            printf("mouse: %s\n",buf);
         }

    }
    close(fd);
    return 0;
}


练习:select实现客户端服务器全双工通信并发服务器的建立

在tcp的服务器端, 有两类文件描述符

监听的文件描述符
1.只需要有一个
2.不负责和客户端通信, 负责检测客户端的连接请求, 检测到之后调用accept就可以建立新的连接

通信的文件描述符
1.负责和建立连接的客户端通信
2.如果有N个客户端和服务器建立了新的连接, 通信的文件描述符就有N个,每个客户端和服务器都对应一个通信的文件描述符

总结select实现IO多路复用特点

1. 一个进程最多只能监听1024个文件描述符 (千级别)
2. select被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低(消耗CPU资源);
3. select每次会清空表,每次都需要拷贝用户空间的表到内核空间,效率低(一个进程0~4G,0~3G是用户态,3G~4G是内核态,拷贝是非常耗时的);
4.跨平台

(1)客户端              

/*客户端创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
// #include "head.h"
enum type_t
{
    login, //登录
    chat,  //发送信息
    quit,  //退出
};
typedef struct mag_t
{
    int type;       //功能
    char name[32];  //ip
    char text[128]; //内容
} MSG_t;
int main(int argc, char const *argv[])
{
    if (argc < 3)
    {
        printf("plase input <ip><port>");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd);
    //2.填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;//协议族
    saddr.sin_port = htons(atoi(argv[2]));//端口
    saddr.sin_addr.s_addr = inet_addr(argv[1]);//IP
    MSG_t msg; //消息包
    socklen_t len = sizeof(saddr); //结构体大小
    int num=0;//交互次数
    pid_t pid = fork();//创建父子进程
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0) //子进程接收消息
    {
        while (1)
        {
            //接受信息
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, &len) < 0)
            {
                perror("recvfrom err");
                return -1;
            }
            printf("ip:%s 状态:%d 内容:%s\n", msg.name, msg.type, msg.text);
        }
    }
    else //父进程发送消息
    {
        while (1)
        {
            strncpy(msg.name, "xiaoyang", 8);//客户端昵称
            //发送信息
            memset(msg.text, 0, sizeof(msg.text)); //清空数组内容
            printf("发送内容:");
            fgets(msg.text, sizeof(msg.text), stdin); //从终端获取内容存放到数组中
            if (strncmp(msg.text, "quit", 4) == 0)    //输入quit退出客户端
            {
                msg.type = quit;//退出状态
                sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);
                exit(0);
            }
            if (msg.text[strlen(msg.text)] == '\0')
            {
                msg.text[strlen(msg.text) - 1] = '\0';
            }
            if (num == 0) //第一次登入
            {
                msg.type == login;//登录状态
            }
            else
            {
                msg.type = chat;//交互状态
            }
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);//发送信号
        }
    }
    close(sockfd);
    return 0;
}

(2)  服务器

/*服务器创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
enum type_t
{
    login,//登录
    chat,//发送信息
    quit,//退出
};
typedef struct mag_t
{
    int type;//功能
    char name[32];//ip
    char text[128];//内容
} MSG_t;
MSG_t msg;
//链表节点结构体
typedef struct node_t
{
    struct sockaddr_in addr;//ip地址
    struct node_t *next;//链表下一个地址
}list_t;
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        printf("plase input <ip><port>\n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    sockfd = socket(AF_INET,SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd);
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;            
    saddr.sin_port = htons(atoi(argv[1])); 
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    socklen_t len = sizeof(saddr); //结构体大小
    //bind绑定ip和端口
    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind success\n");
    // char buf[128] = {0};
    while (1)
    {
        //接收信息
        if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, &len) < 0)
        {
            perror("recvfrom err");
            return -1;
        }
        switch (msg.type)
        {
        case login:
            Loginrecv();break;
        case chat:
            Chatrecv();break;
        case quit:
            Quitrecv();break;
        }
        
        //发送信息
        printf("server:");
        fgets(msg.text, sizeof(msg.text), stdin); //从终端获取内容存放到数组中
        if (strncmp(msg.text, "quit", 4) == 0) //输入quit退出客户端
        {
            break;
        }
        if (msg.text[strlen(msg.text)] == '\0')
        {
            msg.text[strlen(msg.text) - 1] = '\0';
        }
        sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);
    }
    close(sockfd);
    return 0;
}
void Chatrecv()//chat 型
{
        // printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),msg.text);
        printf("ip:%s 状态:chat 内容:%s\n", msg.name,msg.text);

}
void Loginrecv()//login 型 首次链接
{
    

}
void Quitrecv()//quit 退出
{
    //接收信息
        if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, &len) < 0)
        {
            perror("recvfrom err");
            return -1;
        }
        // printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),msg.text);
        printf("ip:%s 状态:chat 内容:%s\n", msg.name,msg.text);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Discourse 如何在 header 上添加 HTML

虽然现在大部分网站都开始支持使用 CDN 的网站校验了。 但还有些网站在你需要他们提供服务的时候要求使用 header 的 meta 数据校验。 Discourse 是可以轻松的实现上面的功能的。 添加方法 选择你的 Discourse 网站下的自定义。 然后在左侧选择你需要添加的主题。 为了方便…

AD9371 官方例程 NO-OS 主函数 headless 梳理(一)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

【Java 进阶篇】JSTL 详解

Java JSTL&#xff08;JavaServer Pages Standard Tag Library&#xff09;是用于简化在 JSP 页面上的开发工作的 Java 标签库。它提供了在 JSP 页面上使用的标准标签&#xff0c;可以帮助开发人员更轻松地访问和操作数据&#xff0c;而无需编写大量的 Java 代码。Java JSTL 是…

工业相机基本知识理解:工业相机IO接口,功耗和供电方式

I-input 相机接收外部信号&#xff0c;可用于触发相机&#xff08;硬触发&#xff09;&#xff0c;也可用于定制不同的 功能&#xff0c;例如使用不同信号宽度来改变相机的曝光时间。主要用于现场设 备控制相机使用&#xff0c;常常配合各种传感器使用 O-output 相机输出信号&a…

antd-vue + vue3 实现a-table动态增减行,通过a-from实现a-table行内输入验证

一、效果图 图一&#xff1a;校验效果 二、主要代码 注意&#xff1a; 1、form 与 table 绑定的是同一个数据 tableSource 并且是一个数据&#xff08;ElementUI 需要 对象包数组&#xff09; 2、form用的是 name 绑定 -> :name"[index, vlan_id]" 3、form-i…

【分布式事务】深入探索 Seata 的四种分布式事务解决方案的原理,优缺点以及在微服务中的实现

文章目录 前言一、XA 模式1.1 XA 模式原理1.2 XA 模式的优缺点及应用场景1.3 Seata XA 模式在微服务中的实现 二、AT 模式2.1 Seata AT 模式原理2.2 AT 模式的脏写问题和写隔离3.3 AT 模式的优缺点3.4 Seata AT 模式在微服务中的实现 三、TCC 模式3.1 TCC 模式原理3.2 Seata 的…

内窥镜项目

★ 手持pad内窥镜项目 项目描述&#xff1a;3D电子内窥镜软件项目是一个基于BS&#xff08;浏览器服务器&#xff09;架构的项目&#xff0c;旨在实现对内窥镜设备的远程控制和高级功能操作。该项目允许操作员使用平板电脑手动触摸屏上的按钮、外部按键或脚踏板 来控制内窥镜设…

初阶JavaEE(15)(Cookie 和 Session、理解会话机制 (Session)、实现用户登录网页、上传文件网页、常用的代码片段)

接上次博客&#xff1a;初阶JavaEE&#xff08;14&#xff09;表白墙程序-CSDN博客 Cookie 和 Session 你还记得我们之前提到的Cookie吗&#xff1f; Cookie是HTTP请求header中的一个属性&#xff0c;是一种用于在浏览器和服务器之间持久存储数据的机制&#xff0c;允许网站…

【C++】类和对象(一):什么是面向对象,访问限定符有哪些,类定义细节,结构体和类的关系。

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

机组 指令系统

机器指令 机器指令&#xff1a;每一条机器语言的语句 指令系统&#xff1a;全部机器指令的集合 指令的一般格式 指令由操作码和地址码两部分组成 操作码 作用&#xff1a;指明该指令要完成的操作 位数&#xff1a;反映机器的操作种类&#xff0c;即机器允许的指令条数 …

瞅瞅 Opencv:扫描图像

扫描图像查询表 一、概述二、图像矩阵如何存储在内存中?三、高效的方式四、迭代器(安全)方法五、带引用返回的动态地址计算六、核心功能七、性能差异 一、概述 让我们考虑一种简单的色彩还原方法。通过使用unsigned char C和c类型进行矩阵项存储&#xff0c;一个像素通道可以…

音视频技术开发周刊 | 318

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 日程揭晓&#xff01;速览深圳站大会专题议程详解 LiveVideoStackCon 2023 音视频技术大会深圳站&#xff0c;保持着往届强大的讲师阵容以及高水准的演讲质量。两天的参会…

git commit规范提交

Git每次提交代码时&#xff0c;都要写Commit Message&#xff08;提交说明&#xff09;&#xff0c;通常情况下&#xff0c;Commit Message应该清晰明了&#xff0c;说明本次提交的目的和具体操作等。然而笔者工作多年来发现&#xff0c;有些公司对Commit Message没有明确的要求…

wpf Grid布局详解 `Auto` 和 `*` 是两种常见的设置方式 行或列占多个单元格,有点像excel里的合并单元格。使其余的列平均分配剩余的空间

比如只有行的界面 <Window x:Class"GenerateTokenApp.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/exp…

SpringCloudAlibaba——Sentinel

Sentinel也就是我们之前的Hystrix&#xff0c;而且比Hystrix功能更加的强大。Sentinel是分布式系统的流量防卫兵&#xff0c;以流量为切入点&#xff0c;从流量控制、流量路由、熔断降级等多个维度保护服务的稳定性。 Sentinel采用的是懒加载&#xff0c;这个接口被访问一次&a…

企业级低代码开发,科技赋能让企业具备“驾驭软件的能力”

科技作为第一生产力&#xff0c;其强大的影响力在各个领域中都有所体现。数字技术&#xff0c;作为科技领域中的一股重要力量&#xff0c;正在对传统的商业模式进行深度的变革&#xff0c;为各行业注入新的生命力。随着数字技术的不断发展和应用&#xff0c;企业数字化转型的趋…

SpringBoot自动装配 Spring相关 常用设计模式 双亲委派 MongoDB Redis 适配器模式与策略模式

SpringBoot自动装配 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 Spring相关 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 常用设计模式 双亲委派 Java虚拟机定义了三个主要的类加载器: 1、启动类加载器 2、扩展类加载器 …

《网络协议》02. 物理层 · 数据链路层 · 网络层

title: 《网络协议》02. 物理层 数据链路层 网络层 date: 2022-08-31 22:26:48 updated: 2023-11-08 06:58:52 categories: 学习记录&#xff1a;网络协议 excerpt: 物理层&#xff08;数据通信模型&#xff0c;信道&#xff09;、数据链路层&#xff08;封装成帧&#xff0c…

CSDN中: Markdown编辑器使用说明

Markdown编辑器使用说明 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一…

Android UI 开发·界面布局开发·案例分析

目录 ​编辑 1. 线性布局&#xff08;LinearLayout&#xff09; 2. 相对布局&#xff08;RelativeLayout&#xff09; 3. 表格布局&#xff08;TableLayout&#xff09; 4. 帧布局&#xff08;FrameLayout&#xff09; 5. 网格布局&#xff08;GridLayout&#xff0…