day35

epoll模型


epoll模型只有linux系统才有
epoll模型只有从linxu内核2.4版本之后才有
epoll从2.4内核到目前的4.X内核,没有更新的模型了,说明epoll模型本身已经很完美了
 


select的问题:


监视列表无法扩容
监视列表和返回的激活列表混在了一起
效率低下:
select需要自己管理激活的套接字
select查询哪个套接字激活了是一个双重循环,效率低下
select的内核部分,监视的套接字也是一个数组,查询哪个套接字激活了,效率也是低的
poll的问题:
效率低下:
select的内核部分,监视的套接字也是一个数组,查询哪个套接字激活了,效率也是低的
 
epoll彻底结局了select 和 poll的问题
epoll允许自动扩容
epoll的内核部分是以二叉树存储所有的描述符,所在查看哪个描述符激活的时候,效率很高
epoll会把所有激活的描述符,放在一个数组中,直接提供给用户,编程效率高
 

原型:int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
函数调用:int count = epoll_wait(fd,数组,监视的描述符的数量,-1)
功能描述:阻塞并等待监视列表中的描述符激活,并且将所有激活的描述符,放在一个数组中提供给我们
参数解析:
    参数 epfd:监视列表
        epoll要求先创建一个文件,然后将所有的要监视的描述符,写入这个文件中,并监视
    参数 events:用来存放所有激活的描述符的数组
    参数 maxevents:最大的监视描述符的数量
    参数 timeout:阻塞时长,单位为毫秒,
        0:表示不阻塞
        -1:表示常阻塞
返回值:激活的描述符的数量            
 


epoll如何创建监视列表
 

原型:int epoll_create(int size);
调用:int epfd = epoll_create(想要监视的描述符数量)
功能描述:创建一个文件,该文件最多能够监视 size个字节的描述符
参数解析:
    参数 size:监视的描述符的最大值
返回值:返回创建出来的文件的描述符
 
 
原型:int epoll_create1(int flags);
调用:int epfd = epoll_create1(EPOLL_CLOEXEC )
功能描述:创建一个文件,用来监视描述符,该文件能够监视的描述符数量能够自动扩容


epoll如何将被监视的描述符写入文件中

原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
调用:epoll_ctl();
功能描述:操作监视列表,可以删除,可以添加,可以指定监视类型(监视可读,监视可写)
参数解析:
    参数 epfd:监视列表
    参数 op:具体针对监视列表的操作行为
        EPOLL_CTL_ADD:将参数 fd 描述符,添加进入epfd监视列表,并且由参数event决定,以何种形式进行监视
        EPOLL_CTL_DEL:将参数 fd 描述符,从epfd监视列表中移除,此时event参数被忽略,可以直接写NULL
        EPOLL_CTL_MOD:根据参数 event 更改 fd描述符的监视类型
    参数 fd:等待操作的描述符
    参数 event:一个结构体指针,结构如下
        struct epoll_event {
            uint32_t     events;      /* Epoll events */ 监视的事件类型
                EPOLLIN:监视描述符是否可读
                EPOLLOUT:监视描述符是否可写
            epoll_data_t data;        /* User data variable */
        };
        
        data 也是一个结构体,结构如下
        
        typedef union epoll_data {
            void        *ptr;
            int          fd;
            uint32_t     u32;
            uint64_t     u64;
        } epoll_data_t;
        
        其中关键数据是:fd
            epoll_event 这个结构体,在 epoll_wait的时候,传入的是这个结构体的数组
            我们判断哪个描述符激活,就是依靠这个fd来判断的
            
            epoll_ctl函数调用的时候,这个events.data.fd 必须和 参数 fd保持一致
         
 
            

epoll的代码模型
 

int epfd = epoll_create1(EPOLL_CLOEXEC)
 
struct epoll_event event
event.events = EPOLLIN(监视类型)
event.data.fd = 想要监视的描述符
epoll_ctl(epfd,EPOLL_CTL_ADD,想要监视的描述符,&event)
 
struct epoll_event readfds;
while(1){
    int signal_count = epoll_wait(epfd,readfds,监视描述符的数量,-1)
    for(遍历readfds){
            判断哪个描述符激活了{
                执行对应的逻辑cv            
            }
    }
}

 什么是域套接字


域套接字只能用来在本地中,用在进程与进程之间的通信作用
原本最早,套接字也只有域套接字,套接字也只是用来做进程间通信的
直到后来出现了TCP协议,TCP协议使用套接字来实现的进程间通信,我们就把只能做进程间通信的套接字,改名,称为域套接字
 
思维方式暂时回到IPC进程间通信里面
不再需要 ip 和 port
反而需要:保证2个进程访问到同一个套接字文件
 


 域套接字的类型

流式套接字:SOCK_STREAM
报文套接字:SOCK_DGRAM
域套接字同时支持上述2种类型
 


  流式域套接字的操作流程


  流程概要
 

int sock = socket(AF_UNIX / AF_LOCAL,SOCK_STREAM,0)
 
绑定套接字文件:注意域套接字使用的地址信息结构体类型为 struct sockaddr_un
如何查看这个结构体的结构: man 7 unix,查询结果如下
    struct sockaddr_un {
        sa_family_t sun_family;               /* AF_UNIX */    必须写AF_UNIX
        char        sun_path[108];            /* pathname */   域套接字文件的路径名
    };
 
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strpcy(addr.sun_path,"域套接字的路径名");
 
注意:在绑定域套接字文件之前,一定保证该域套接字文件是不存在,域套接字文件最终由bind函数创建并绑定
if(access("域套接字文件路径名",F_OK)==0){
    remove()
    ulink()
}
 
 
 
bind(sock,(struct sockaddr*)&addr,sizeof(addr));//确保域套接字不存在的情况下,bind函数会自动创建域套接字文件并绑定。
如果bind函数发现域套接字文件是存在的,则报错


    创建套接字
 

int sock = socket(AF_UNIX / AF_LOCAL,SOCK_STREAM,0)

  创建域套接字地址信息结构体
 

struct sockaddr_un addr;
该结构体结构如下
    struct sockaddr_un {
        sa_family_t sun_family;               /* AF_UNIX */    必须写AF_UNIX
        char        sun_path[108];            /* pathname */   域套接字文件的路径名
    };


  确保域套接字不存在
 

access函数判断是否存在
    第2个参数:F_OK 表示判断文件是否存在
    返回值:文件如果存在返回0,不存在返回-1
 
 

删除文件
 


int unlink(const char *path);
     删除一个非目录文件
int rmdir(const char *path);
    删除一个目录文件
int remove(const char *pathname);
    删除一个任意文件


  绑定域套接字
 

bind(sock,(struct sockaddr*)&addr,sizeof(addr))


   总结


整个流程,和TCP服务器不一样的地方就2点
第一点:地址信息结构体不一样TCP是 sockaddr_in,域套接字是 sockadd_un
第二点:域套接字在bind之前,一定确保域套接字文件是不存在的
 
服务器

客户端

流式域套接字接收端模型(服务器)
 

 int server = socket()
 
 struct sockaddr_un_t addr;
 if(access() == 0){
     remove() 
 }
 
 bind()
 
 listen
 
 int client = accept()
 
 while(1){
     read()) 
 }
① socket
② 准备套接字地址信息结构体,并确保套接字文件不存在
③ bind(必须有)
④ listen
⑤ accept
⑥ read
流式域套接字发送端模型(客户端)
 

int client = socket()
 
struct sockaddr_un_t addr
 
connect()
 
while(1){
    write()
}
①    socket 创建套接字
②     准备地址信息结构体
③    一定不能有bind,不用确保文件不存在(套接字文件必须存在)
④    connect()    必须有
⑤     write
 


    报文域套接字的操作流程


   流程概要
 

接收端
int sock = socket(AF_UNIX,SOCK_DGRAM,0)
struct sockaddr_un_ t addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,"套接字文件路径名");
// 服务器一定绑定
bind(sock,(addr_t*)&addr,sizeof(addr))
 
while(1){
    recv()
}
 
 
 

发送端
int sock = socket(AF_UNIX,SOCK_DGRAM,0)
struct sockaddr_un_ t addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,"套接字文件路径名");
connect(sock,(addr_t*)&addr,sizeof(addr));//可以有,也可以没有
while(1){
    sendto(sock,数据,数据长度,0,(struct addrsock*)&addr,sizeof(addr));
}


  创建报文域套接字
 

    int sock = socket(AF_UNIX / AF_LOCAL,SOCK_DGRAM,0)


   准备地址信息结构体
 

    struct sockaddr_un_t addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,"套接字文件路径名");


    确保域套接子文件一定不存在(客户端没有这一步,只有服务器)
 

    if(access("域套接字文件",F_OK) == 0){
        remove("域套接字文件")    
    }


    绑定域套接字文(服务器)/链接域套接字文件(客户端,可选)
 

服务器
bind(sock,(struct sockaddr*)&addr,sizeof(addr))
 
客户端
connect(sock,(struct sockaddr*)&addr,sizeof(addr))


   使用read 或者 recv 或者 recvfrom 读取发送端发来的消息 / 使用 sendto发送消息


   模型总结


报文域套接字接收端(服务器)
 

int server = socket()
 
struct sockaddr_un_t addr;
 
if(access()){
    remove
}
 
bind()
 
while(1){
    recv()
}
①     socket()    创建报文域套接字
②     准备地址信息结构体,并充实数据,并确保该套接字文件一定不存在
③     bind()    必须的
④     read / recv / recvfrom


报文域套接字发送端(客户端)
 

int server = socket()
 
struct sockaddr_un_t addr;
 
//connect() 可选
 
while(1){
    sendto()
}
①     socket() 创建报文域套接字
②    准备域套接字地址信息结构体,并充实数据(套接字文件一定要存在)
③    一定不能bind(),connect()可选
④     一定是使用 sendto() 发送数据

安装sqlite3数据库以及sqlite3函数库
 

1:sudo apt install sqlite3 //安装数据库
2:sudo apt install libsqlite3-dev // 安装数据库的函数库


   什么是数据库


一种存放数据的文件,但是该文件拥有特殊的结构
第一层结构:数据库本身
第二层结构体:数据库中存放了若干张表单
每一张表单的字段构成各不相同
第三层结构:一张表单中,所有字段都能存放信息
一组字段中的所有数据,就是一条记录
 
最终,很多很多条件,格式不同的记录,组成了一个完整的数据库


 常用数据库类型


①    sqlite3:一个允许部署在本地的轻量级数据库
特别适合用于嵌入式开发
②     mysql:是一个部署在服务端的,需要网络连接的数据。
如果一定要部署在本地的话,也是需要安装一个mysql服务器的
适合一些应用层程序开发


 如何操作sqlite3数据库


打开数据库


输入指令:sqlite3 数据库名.db,出现以下画面就OK了

  sqlite3数据库中的指令规则


1:显示指令


必须以    .    开头,回车确认输入
.table    :查看当前数据库中所有表单的名字
.schema 表单名:    查看当前数据库中指定的表单中字段的结构
如果没有写表单名,则表示查看所有表单的字段结构
.head(er) on    在查看数据的时候,打开抬头
.mode column    以字段对齐的形式显示数据
.quit 保存并退出数据库


2:操作指令


必须以 ;结尾,然后再键入回车,如果没有 ; 的话,sqlite3不会认定这条指令结束了
如果忘记敲 ; 了,换一行再补一个 ; 也是没有问题的


3:数据库中的指令 大小写无所谓

 创建表单
 

create table 表单名 (if not exists)(
    字段名1    数据类型        约束类型        , 
    ......                                ,
    字段名n    数据类型        约束类型        
);
 
字段名:变量名
数据类型:
整形         INTERGER
浮点型    REAL
字符串    TEXT
约束类型:什么是约束?
在添加数据的时候,必要的为这些数据添加一些取值范围,保证这些数据不会过于离谱,添加取值范围这个事,就是依赖约束实现的
 
①    主键约束    :primary key
什么是键:用于比较大小,从而确定数据存放位置的关键数据。这个数据不能重复,不能修改
被主键约束的字段,成为了表单中的 "键",剩下的都是 "值"
被主键约束的字段,还有一个修饰词: auto increment
功能为:在添加新记录的时候,如果被 auto increment 所修饰的字段,没有填充数据的话,则数据库会根据最近一次填充的数据,自增1后,为当前数据填充
 
②    默认约束: default 默认值
在添加新纪录的时候,如果没有为拥有默认约束的字段,填充数据的话,数据库会选用默认值填充
③     非空约束:not null
在添加新记录的时候,如果没有为 非空约束 所修饰的字段,填充数据的话,则本次记录添加失败
④    检查约束:check(检查条件)
在添加记录的时候,会检查被检查约束的字段所填充的数据,是否满足"检查条件",如果不满足则添加失败
例如:为 字段"成绩" 添加检查约束,约束条件为成绩的取值范围在 0~150分之间
check(成绩>=0 AND 成绩<=150)
再例如:为字段"性别"添加检查约束,要求性别只能是 "男" 或者 "女"
check(性别="男" OR 性别="女")
 

向表单中添加数据
 

insert into 表单名(字段1,字段2,....,字段n) values(数据1,数据2,...,数据 n)
    为表单中添加一条新的记录,但是只填充 字段1 ~ 字段n 这几个字段的数据
    如果有任何字段对应的数据,违反了约束,则添加失败
 


 查看表单中数据
 

select 字段1,字段2,...,字段n from 表单名
    查看指定表单中的指定字段的所有数据
select * from 表单名
    查看指定表单中的所有字段的所有数据
insert 指令和 select 指令可以配合使用
Plain Text
自动换行

insert into 表单1(字段1,字段2,....,字段n) select 字段1,字段2,...,字段n from 表单2
    先查询出表单2中的所有指定字段的数据,再将这些数据,对应的添加到表单1中的每一个字段中去


查看某个特定的数据
 

where子句:
    只要有任何附加的查询条件的时候,在英语语法应该写条件的地方写上 where 条件
 
    比如说,想要查看 姓名为"张三"的所有信息
select * from stu where 姓名="张三"
 


修改表单中指定数据
 

update 表单名 set 字段名=新数据 where条件定位
    例如:将姓名为"张三"的成绩,改成50分
update stu set 成绩=50 where 姓名="张三"


删除表单中的指定数据
 

delete from 表单名 where 条件定位
千万小心:如果不写 where 条件定位的话,会将表单中所有数据全都删除
 
一般来说,为了防止数据的误删,基本上不用delete指令
一般会在表单中添加一个字段叫做 "delete" ,他的值只有0或者非0
查询的时候,只要在最后加上 where delete=0,这样一来 delete=1也就是所谓的被删除的数据,就查不出来了


排序显示所有数据
 

order by 字段名
例如:想要排序显示 成绩
select * from stu order by score 默认按照成绩升序排序
想要降序的话,在最后写上关键字 desc 即可


范围查看
 

limit x offset y
    从第y行开始,显示x条记录,y从0开始
    如果offset不写,那么默认就是从0开始
    
如果说:想要显示,成绩最高的3个学生的信息
select * from stu order by score desc limit 3
 


模糊查询
 

like 子句 % _ (* ?)
使用like的时候,使用 _ 表示1个字符的站位,使用 % 表示 0~n个字符的占位
例如:
    select * from stu where name like "徐_"
        查询姓徐的,并且名字只有1个字的 
    select * from stu where name like "_徐_"
        查询名字3个字的,并且中间那个字是 徐 
    select * from stu where name like "徐%"
        查询姓徐的,名字有几个字无所谓,哪怕只有徐,没有名字也行
    select * from stu where name like "%徐%"
        查询所有名字中带 徐 的


为指定表单添加新的字段
 

alter table 表单名 add column 字段名 数据类型 约束类型


重命名表单名
 

alter table 原先的表单名 rename to 新的表单名


删除表单
 

drop table 表单名


重命名字段名


 

sqlite3 不支持重命名字段名,mysql是支持的
所以,我们如果想重命名字段名,需要用逻辑去实现
 
① 将当前表单名修改了,随便改成什么
② 创建一个新的表单,表单名是原先那个表单的名字
    除了要修改的字段的名字,改成新的字段名之外
    其他所有字段名直接照抄
③ 使用 insert + select 语句,将原先表单中的所有数据,拷贝到新的表单中去
④ 删除已经没有用的表单


删除字段
 

sqlite3 不支持删除字段,mysql是支持
所以,要通过逻辑去实现
① 重命名表单
② 以原先的表单名创建新表单
    不要的字段压根不写,其他字段照抄
③ 使用 insert + select 语句把需要的字段中的所有数据拷贝过去
④ 删除不需要的表单

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值