在Reactor基础上实现webserver

前面已经介绍过Reactor封装epoll,在前面的基础上实现一个简单的webserver

Reactor封装epoll实现高并发

Reactor关注事件管理的特点使io变得更加灵活,可以实现网络io层与协议层相互独立。也就是说,业务相关处理对网络io影响不大,这就提高了代码复用。

下面具体介绍怎么在Reactor基础上实现webserver

增加业务相关接口

首先我们需要提供两个接口为webserver与客户端(浏览器)之间做数据传输。

int http_request(struct conn *c);    // 对应recv
int http_response(struct conn *c);    // 对应send

 http_request()在io接收数据的接口调用,用于接收数据。

int recv_cb(int fd)
{
    int count = recv(fd, conn_list[fd].rbuffer, BUFFER_LEN, 0);

    if (count == 0)
    {
        close(fd);

        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

        return 0;
    }
    else if (count < 0)
    {
        close(fd);

        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        return 0;
    }

    conn_list[fd].rlength = count;

    // printf("recv:%s\n", conn_list[fd].rbuffer);


    http_request(&conn_list[fd]);    // 添加请求接口
    set_event(fd, EPOLLOUT, 0);


    return count;
}

 http_response()在io发送时调用,用于发送数据。

int send_cb(int fd)
{
    http_response(&conn_list[fd]);    // 增加返回结果接口

    int count = 0;

    // 状态机检测,下面会讲到
    

    return count;
}

这两个接口本质上是对数据的处理,属于业务层面,对于网络io层,增加了一个状态机用于准备响应头、发送文件内容和完成传输

增加状态机

struct conn
{
    int fd;

    char rbuffer[BUFFER_LENGTH];
    int rlength;

    char wbuffer[BUFFER_LENGTH];
    int wlength;

    RCALLBACK send_callback;

    union
    {
        RCALLBACK recv_callback;
        RCALLBACK accept_callback;
    } r_action;

    int status;    // 增加状态机
};
  • 0:准备响应头
  • 1:发送内容
  • 2:发送完成 

这个状态机怎么使用呢?

有几个时机需要注意:

在业务接口http_request接收到数据时:初始化对应state=0代表开始准备消息头

printf("request:%s\n", c->rbuffer);
memset(c->wbuffer, 0, BUFFER_LENGTH);
c->wlength = 0;
c->status = 0;    // 初始化为0

在业务接口http_response被调用时:

if (c->status == 0)
    {
        // 准备 HTTP 响应头

        c->status = 1;
    }
    else if (c->status == 1)
    {
        // 准备文件内容

        c->status = 2;
    }
    else if (c->status == 2)
    {
        // 完成传输

        c->status = 0;
    }

 当http_response执行完,send_cb会检查状态,这里就可以根据不同的状态执行不同的处理,而不是简单的recv,send之间循环。

if (conn_list[fd].status == 1)    // 准备消息头完毕,需要准备内容,继续关注EPOLLOUT
    {
        count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
        set_event(fd, EPOLLOUT, 0);
    }
    else if (conn_list[fd].status == 2)    // 准备内容完毕,准备结束,继续关注EPOLLOUT
    {
        set_event(fd, EPOLLOUT, 0);
    }
    else // conn_list[fd].status == 0    结束,开始关注EPOLLIN
    {
        if (conn_list[fd].wlength != 0)
        {
            count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
        }
        set_event(fd, EPOLLIN, 0);
    }

 加入状态机确保了每个连接的状态被正确跟踪,并且在适当的时间点执行相应的操作。

总结

可以看出,对网络io相关的处理改动非常小,这就是Reactor的灵活性带来的好处。网络io层与协议层(业务层)分离,在业务变更时只需要去改动业务相关接口即可,不需要修改网络io层。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值