徒手撸个http服务器(三)------epoll 实现

epoll_wait(2)

SYNOPSIS

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

DESCRIPTION

epoll_wait()系统调用等待文件描述符epfd引用的epoll实例上的事件. 事件指向的内存区域将包含可用于调用者的事件. 由epoll_wait()返回maxevents. maxevents参数必须大于零.

timeout参数指定epoll_wait()将阻塞的毫秒数. 调用会一直阻塞, 直到以下情况之一出现:

  • 文件描述符递送一个事件
  • 调用被信号中断
  • 超时

注意:超时的间隔将四舍五入到系统时钟粒度, 并且内核调度延迟意味着阻塞间隔可能会少量超出. 指定超时为-1时, 会导致epoll_wait()无限期阻塞. 当然, 指定超时等于零会导致epoll_wait()立即返回, 即使没有事件可用.

struct epoll_event 被定义为:

typedef union epoll_data {
    void           *ptr;
    int             fd;
    uint32_t        u32;
    uint64_t        u64;
} epoll_data_t;

struct epoll_event {
    uint32_t        events;    /* Epoll events */
    epoll_data_t    data;      /* User data variable */
};

RETURN VALUE

成功: epoll_wait() 返回为所请求的I / O准备好的文件描述符数, 如果在请求的超时期间没有文件描述符就绪, 则为零.
错误: 返回-1, 并且设置errno.

ERRORS

  • EBADF epfd 不是合适的文件描述符
  • EFAULT events 所指的内存区域不可通过写权限访问.
  • EINTR 在任何请求的事件发生之前或超时到期之前, 该调用被信号处理程序中断.
  • EINVAL epfd 不是文件描述符. 或maxevents 小于等于0.

以上是实现需要的函数.

具体实现

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

#define MAX_EVENT 50

/* 将套接字设置为非阻塞 */
void setnonblocking(int fileno)
{
    int flags;

    if((flags = fcntl(fileno, F_GETFL)) < 0) {
        perror("fcntl get error");
        exit(EXIT_FAILURE);
    }

    flags |=  O_NONBLOCK;

    if(fcntl(fileno, F_SETFL, flags) < 0) {
        perror("fcntl set error");
        exit(EXIT_FAILURE);
    }
}

void do_something(int sockfd)
{
    char buff[4096];
    char head[] = "HTTP/1.1 200 OK\r\nContent-type: text/html\r\nServer: MyServer/2.3.3 (DebianOS)\r\n\r\n";
    char send[] = "<html><title>test</title><body><h1>This is a Demo Server!!</h1></body></html>";
    read(sockfd, buff, 4096);
    write(sockfd, head, sizeof(head));
    write(sockfd, send, sizeof(send));
    printf("%s", buff);
}

int main(void)
{
    int connfd;
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(2333);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    listen(sockfd, 1024);

    struct epoll_event ev, events[MAX_EVENT];
    int nfds, epollfd;

    if((epollfd = epoll_create1(0)) == -1) {
        perror("epoll_create1 error");
        exit(EXIT_FAILURE);
    }

    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
        perror("epoll_ctl error");
        exit(EXIT_FAILURE);
    }

    while(true) {
        if((nfds = epoll_wait(epollfd, events, MAX_EVENT, -1)) == -1) {
            perror("epoll_wait error");
            exit(EXIT_FAILURE);
        }

        for(int n = 0; n < nfds; ++n) {
            if(events[n].data.fd == sockfd) {
                socklen_t socklen = sizeof(servaddr);
                connfd = accept(sockfd, (struct sockaddr *)&servaddr, &socklen);

                if(connfd == -1) {
                    perror("accept error");
                    exit(EXIT_FAILURE);
                }

                setnonblocking(connfd);

                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = connfd;

                if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) == -1) {
                    perror("epoll_ctl: connfd");
                    exit(EXIT_FAILURE);
                }

            } else {
                do_something(events[n].data.fd);
                close(events[n].data.fd);
                events[n].data.fd = -1;

            }
        }

    }
    return 0;
}

截个图:
看起来还行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值