IO多路复用学习(5)reactor网络模型

reactor模型

  • 组成:非阻塞的io + io多路复用
  • 特征:基于事件循环,以事件驱动或事件回调的方式来实现业务逻辑
  • 理解:对io的处理转换为对事件的处理。

单reactor模型

在这里插入图片描述

  • redis

单reactor模型 + 任务队列 + 线程池

在这里插入图片描述

  • skynet

多reactor

多线程

在这里插入图片描述

  • memcached

多进程

在这里插入图片描述

  • nginx

单reactor程序实例

下面的程序注册了accept回调函数,recv回调函数和send回调函数。相应的接收连接事件,读事件,写事件触发时会调用相应的回调函数。服务端收到客户端的数据再发送给客户端。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define MAX_BUFFER_SIZE 5
#define MAX_EPOLL_EVENTS 20
#define EPOLL_LT 0
#define EPOLL_ET 1
#define FD_BLOCK 0
#define FD_NONBLOCK 1

typedef void (*CALLBACK) (int epollfd, int fd, int events, void *arg);
#define BUFFERLEN 1024

struct sockitem {
    int sockfd;
    CALLBACK callback;
    char recvbuffer[BUFFERLEN];
    char sendbuffer[BUFFERLEN];
    int rlength;
    int slength;
};

int set_noblock(int fd);
void recv_callback(int epollfd, int fd, int events, void *arg);

void send_callback(int epollfd, int fd, int events, void *arg)
{
    printf("开始发送数据...\n");
    struct sockitem* si = (struct sockitem*)arg;
    int len = send(fd, si->sendbuffer, si->slength, 0);

    si->callback =recv_callback;
    struct epoll_event ep_event;
    ep_event.events = EPOLLIN | EPOLLET;
    ep_event.data.ptr = si;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ep_event);
    printf("发送数据结束...\n");
}


void recv_callback(int epollfd, int fd, int events, void *arg)
{
    char buffer[MAX_BUFFER_SIZE];
    int ret;
    struct sockitem* si = (struct sockitem*)arg;
    memset(si->recvbuffer, 0, BUFFERLEN);

    int offset = 0;
    printf("带循环的ET读取数据开始...\n");
    int isclosefd = 0;
    while(1) {
        memset(buffer, 0 , MAX_BUFFER_SIZE);
        ret = recv(fd, buffer, MAX_BUFFER_SIZE, 0);
        if (ret == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("循环读取完所有数据!!!\n");
                break;
            }
            epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
            free(si);
            close(fd);
            isclosefd = 1;
            break;
        } else if (ret == 0) {
            printf("客户端主动关闭请求!!!\n");
            epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
            free(si);
            close(fd);
            isclosefd = 1;
            break;
        } else {
            printf("收到消息:%s, 共%d个字节\n", buffer, ret);
            memcpy(si->recvbuffer + offset, buffer, ret);
            offset += ret;
        }

    }
    if (isclosefd == 0) {
        printf("发送接收到的数据:%s\n", si->recvbuffer);
        si->rlength = offset;
        memset(si->sendbuffer, 0, BUFFERLEN);
        memcpy(si->sendbuffer, si->recvbuffer, si->rlength);
        si->slength = si->rlength;
        si->callback = send_callback;

        struct epoll_event ep_event;
        ep_event.events = EPOLLOUT | EPOLLET;
        ep_event.data.ptr = si;
        epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ep_event);
    }
    printf("带循环的ET处理结束!!!\n");
}

void accept_callback(int epollfd, int sockfd, int events, void *arg)
{
        struct sockaddr_in client_addr;
        socklen_t client_addrlen;
        printf("================新一轮accept=================");
        printf("accept()开始...\n");

        printf("开始休眠3秒...\n");
        sleep(3);
        printf("休眠3秒结束!!!\n");

        client_addrlen = sizeof(client_addr);
        int connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addrlen);
        printf("connfd = %d\n", connfd);


        //addfd_to_epoll(epollfd, connfd, epoll_type, block_type);
        struct epoll_event ep_event;
        ep_event.events = EPOLLIN | EPOLLET;

        set_noblock(connfd);
        struct sockitem *si = (struct sockitem *)malloc(sizeof(struct sockitem));
        si->sockfd = connfd;
        si->callback = recv_callback;
        ep_event.data.ptr = si;
    

        epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ep_event);

        printf("accept()结束!!!\n");
}

int set_noblock(int fd)
{
    int old_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);
    return old_flags;
}


void err_exit(char* msg)
{
    perror(msg);
    exit(1);
}

int create_socket(const char* ip, const int port)
{
    struct sockaddr_in server_addr;
    int sockfd, reuse = 1;

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);

    if (inet_pton(PF_INET, ip, &server_addr.sin_addr) == -1) {
        err_exit("inet_pton() error");
    }

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        err_exit("socket() error");
    }

    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        err_exit("bind() error");
    }

    if (listen(sockfd, 5) == -1) {
        err_exit("listen() error");
    }

    return sockfd;
}

int main(int argc, char* argv[])
{
    if (argc < 3) {
        fprintf(stderr, "usage:%s ip_address port_number\n", argv[0]);
        exit(1);
    }

    int sockfd, epollfd, number;

    sockfd = create_socket(argv[1], atoi(argv[2]));
    struct epoll_event events[MAX_EPOLL_EVENTS];

    if ((epollfd = epoll_create1(0)) == -1) {
        err_exit("epoll_create1 error");
    }

    // sockfd: 非阻塞的LT模式
    struct sockitem *si = (struct sockitem *)malloc(sizeof(struct sockitem));
    si->sockfd = sockfd;
    si->callback = accept_callback;

    struct epoll_event ep_event;
    ep_event.data.ptr = si;
    ep_event.events = EPOLLIN;
    set_noblock(sockfd);

    epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ep_event);
    printf("listenfd:%d\n", sockfd);


    while (1) {
        number = epoll_wait(epollfd, events, MAX_EPOLL_EVENTS, -1);
        if (number == -1) {
            err_exit("epoll_wait() error");
        } else {
            printf("number:%d\n", number);
            for (int i = 0; i < number; i++) {
                if (events[i].events & EPOLLIN) {
                    struct sockitem *si = (struct sockitem *)events[i].data.ptr;
                    printf("EPOLLIN si->sockfd:%d\n", si->sockfd);
                    si->callback(epollfd, si->sockfd, events[i].events, si);
                }
                if (events[i].events & EPOLLOUT) {
                    struct sockitem *si = (struct sockitem *)events[i].data.ptr;
                    printf("EPOLLOUT si->sockfd:%d\n", si->sockfd);
                    si->callback(epollfd, si->sockfd, events[i].events, si);
                }
            }
        }
    }

    close(sockfd);
    return 0;
}

运行结果如下:
服务端:
在这里插入图片描述
客户端:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值