最近,学习了一下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;
}
运行结果: