基于epoll的简单http服务器

最近,学习了一下epoll函数,于是写了一个简单的http服务器。代码如下:

#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <strings.h>
#include <errno.h>

#define MAX_EVENTS 10
#define PORT 8080

//设置socket链接为非阻塞模式
void setNonblocking(int sockfd)
{
    int opts = fcntl(sockfd,F_GETFL);
    if(opts < 0)
    {
        perror("获取文件描述符状态错误");
        exit(1);
    }
    opts = (opts | O_NONBLOCK);
    if(fcntl(sockfd,F_SETFL,opts) < 0)
    {
        perror("socket设置非阻塞模式失败");
        exit(1);
    }

}

int main()
{
    struct epoll_event ev,events[MAX_EVENTS];
    int addrlen;//地址大小
    int listenfd;//监听套接字
    int conn_sock;//通信套接字
    int nfds;//epoll_wait返回满足条件的个数
    int epfd;//epoll_create返回值,返回一个红黑树的树根
    int nread;//用来存储read()返回值,即接收到数据的长度
    int fd;//用来存储满足条件的一个文件描述符
    struct sockaddr_in local;//本地地址
    struct sockaddr_in remote;//客户端地址
    char buf[BUFSIZ];//缓存发送或接收的数据
    int res;//返回值判断
    int i,n;


    //创建监听套接字
    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if(listenfd < 0)
    {
        perror("监听套接字创建失败");
        exit(1);
    }

    //设置监听套接字为非阻塞模式
    setNonblocking(listenfd);

    //初始地址
    bzero(&local,sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htons(INADDR_ANY);
    local.sin_port = htons(PORT);

    //绑定
    res = bind(listenfd,(struct sockaddr*)&local,sizeof(local));
    if(res < 0)
    {
        perror("绑定失败");
        exit(1);
    }

    //监听
    listen(listenfd,10);

    //创建监听事件的根节点,即红黑树树根
    epfd = epoll_create(MAX_EVENTS);
    if(epfd == -1)
    {
        perror("epoll_create调用失败");
        exit(1);
    }
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;

    //往根节点添加事件
    res = epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
    if(res == -1)
    {
        perror("epoll_ctl调用失败");
        exit(1);
    }
    
    //处理
    while(1)
    {
        //返回满足条件的文件描述符
        nfds = epoll_wait(epfd,events,MAX_EVENTS,-1);
        if(nfds == -1)
        {
            perror("epoll_wait调用失败");
            exit(1);
        }
        
        //处理满足条件的事件
        for(i = 0; i < nfds; i++)
        {
            fd = events[i].data.fd;
            if(fd == listenfd)
            {
                while(( conn_sock = accept(listenfd,(struct sockaddr *)&remote,(size_t*)&addrlen)) > 0)
                {
                    setNonblocking(conn_sock);
                    //初始化节点, 设置边缘模式,插入红黑树
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = conn_sock;
                    res = epoll_ctl(epfd,EPOLL_CTL_ADD,conn_sock,&ev);
                    if(res == -1)
                    {
                        perror("epoll_ctl调用失败2");
                        exit(1);
                    }
                }
                
                if(conn_sock == -1)
                {
                    if(errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
                    {
                        perror("accept失败");
                    }
                }
                continue;
            }
            //接收数据
            if(events[i].events & EPOLLIN)
            {
                n = 0;

                while((nread = read(fd, buf + n, BUFSIZ - 1)) > 0)
                {
                    n += nread;
                }
                if(nread == -1 && errno != EAGAIN)
                {
                    perror("read调用失败");
                    exit(1);
                }

                ev.data.fd = fd;
                ev.events = events[i].events | EPOLLOUT;
                res = epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
                if(res == -1)
                {
                    perror("epoll_ctl调用失败3");
                    exit(1);
                }
            }

            if (events[i].events & EPOLLOUT) 
            { 
                sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n Hello World!!!", 15); 
                int nwrite, data_size = strlen(buf); 
                n = data_size; 
                while (n > 0) 
                { 
                    nwrite = write(fd, buf + data_size - n, n); 
                    if (nwrite < n) 
                    { 
                        if (nwrite == -1 && errno != EAGAIN) 
                        { 
                            perror("write error"); 
                        } 
                        break; 
                    } 
                    n -= nwrite; 
                } 
                close(fd); 
            } 
        }
    }

    return 0;
}


运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值