网络通信与reactor原理总结

网络编程基本流程图如下:

在网络通信中io会有阻塞和非阻塞形式,阻塞一般出现在网络线程中,可以通过fcntl将网络套接字设置成非阻塞模式,默认创建的套接字为阻塞模式

如果套接字设置成非阻塞模式,在解析力啊read write就会表现出非阻塞模式

非阻塞和阻塞io主要差异,在数据未到达时,非阻塞体现在当有数据是立刻返回,反之阻塞

具体如下图所示:

阻塞io模型+多线程方式:

每一个线程处理一个fd连接

优势:处理及时; 缺陷:线程利用率低,线程的数量有限的

io多路复用(网络线程),用一个线程来监测多个io事件

水平触发的时候,io函数既可以阻塞也可以非阻塞形式的,因为在数据在没有接受完成时,会不断的发送过来

边缘触发的时候,io函数只能是非阻塞的形式

这里就引出io多路复用的epoll,在Linux下多路复用io接口select/poll的增强版本

epoll重要数据结构:

struct eventpoll {
 ...
 struct rb_root rbr; // 管理epoll监听的事件
 struct list_head rdllist; // 保存着 epoll_wait 返回满⾜条件的事件
 ...
};
 
struct epitem {
 ...
 struct rb_node rbn; // 红⿊树节点
 struct list_head rdllist; // 双向链表节点
 struct epoll_filefd ffd; // 事件句柄信息
 struct eventpoll *ep; // 指向所属的eventpoll对象
 struct epoll_event event; // 注册的事件类型
 ...
};
struct epoll_event {
 __uint32_t events;
 epoll_data_t data; // 保存 关联数据
};
typedef union epoll_data {
 void *ptr;
 int fd;
 uint32_t u32;
 uint64_t u64; }epoll_data_t;

主要函数:

epoll_create系统调用

int epoll_create(int size);

size参数高速内核在epoll这个对象会处理事件大致数量,而不是能够处理的事件的最大数。

在现有的linux版本中,size参数已经没有意义

返回值:epoll对象句柄,在之后针对给epoll操作需要通过该句柄进行标识该epoll对象

epoll_ctl系统调用

int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);

epoll_ctl向epoll对象添加、修改或删除事件

返回:0表示执行成功,-1表示错误,根据errno错误码判断错误类型。

op类型:

EPOLL_CTL_ADD 添加新的事件到epoll中

EPOLL_CTL_MOD 修改epoll中的事件

EPOLL_CTL_DEL   删除epoll中 的事件

event.events取值:

EPOLLIN  表示该连接上有数据可读(tcp连接远端主动关闭连接,也是可读时间,因为需要处理发送来的FIN包)

EPOLLOUT  表示该连接上可写发送(主动向上游服务器发起非阻塞tcp连接,连接建立成功事件相当于可写事件)

EPOLLRDHUP 表示tcp连接的远端关闭或半关闭连接

EPOLLPRI  表示连接上有紧急数据需要读

EPOLLERR  表示连接发生错误

EPOLLHUP  表示连接被挂起

EPOLLET  表示触发方式设置为边缘触发,系统默认为水平触发

EPOLLONESHOT 表示该事件只处理一次,下次需要处理时需要重新加入epoll

epoll_wait系统调用

int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

收集epoll监控的事件中已经发生的事件,如果epoll中没有任何一个事件发生,则最多等待timeout毫秒后返回

返回:表示当前发生的事件个数

返回0  表示本次没有事件发生

返回-1 表示出现错误,需要检查errno错误码判断错误类型

附加:

events这个数据需要在用户态分配内存,内核负责把就绪事件复制到该数组中,

maxevents 表示本次可以返回的最大事件数目,一般设备为events数组的长度

timeout 表示在没有检测到事件发生时最多等待的时间;如果设置为0,检测到rdllist为空立刻返回;

如果设置为-1,一直等待

记录

所有添加到epoll中的事件都会与网卡驱动程序建立回调关系,相应的事件发生时会调用这里的回调方法(ep_poll_callback),它会把这样的事件放在rdllist双向链表中

reactor模型:

组成方式:非阻塞io+io多路复用

特征:基于事件循环,以事件驱动或者事件回调的方式来实现业务逻辑

理解:具体的网络连接转化成事件的处理,一个事件循环检测多个io事件的处理

int listenfd = socket();
bind(listenfd, addr, sizeof(addr));
listen(listenfd);
int epfd = epoll_create(0);
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd); // acceptor
while (1)  //事件循环
{
    struct epoll_event events[1024];
    int nevents = epoll_wait(epfd, events, 1024, -1); // 数据准备阶段
    for (int i = 0; i < nevents; i++)
    {
        epoll_event *e = events[i];
        if (e->data.fd == = listenfd)  //建⽴
        {
            // fd = socket ()
            epoll_ctl(epfd, EPOLL_CTL_ADD, fd);
        }
        else
        {
            if (e->events & EPOLLIN)
            {
                // FIN包 客户端主动断开连接
                // 数据的到达
                read(e->data.fd, buff);
                if (buff == FIN包)
                {
                    close(fd)
                    return
                }
                decode();
                compute();
                encode();
                epoll_ctl(epfd, EPOLL_CTL_MOD, events | EPOLLOUT);
            }

            if (e->events & EPOLLOUT)
            {
                // 数据的发送完毕
                write(e->data.fd, buff, size);
            }

            if (e->events & EPOLLERR)
            {
                // close 连接断开
            }
        }
    }
}

在redis中有体现reactor模式,在6.0版本中添加了多线程处理机制

reactor模式还有

单reactor模型+任务队列+线程池 添加线程池处理,对于业务进行解耦合,异步处理运行更高效

多reactor多进程模式 nginx采用多进程运行方式,多进程监听listen一个端口,进程间共享一把锁,每个进程都有一个reactor模式,进程获得了锁的同时也获得了对这个连接的处理

多reactor+消息队列+线程池 在多业务场景中使用较多 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值