程序简介:这是一个运用epoll系列函数进行IO复用的服务器模型。它是目前UNIX与LINUX平台上效率最高,最受欢迎的IO复用传输模型。
上代码:
#include "my_unp.h"
int main(void)
{
int listenfd, connfd, sockfd, epfd;
int i, maxi, nfds;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr;
struct sockaddr_in servaddr;
//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
struct epoll_event ev, events[256];
//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大
epfd = Epoll_create(256);
//创建用于TCP协议的套接字
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
//把socket和socket地址结构联系起来
Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
//开始监听LISTENQ端口
Listen(listenfd, LISTENQ);
//设置与要处理的事件相关的文件描述符和事件
ev.data.fd = listenfd;
ev.events = EPOLLIN|EPOLLET;
//注册epoll事件
Epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd,&ev);
maxi = 0;
while(1)
{
//等待epoll事件的发生
//返回需要处理的事件数目nfds,如返回0表示已超时。
nfds = Epoll_wait(epfd, events, 20, 500);
//处理所发生的所有事件
for(i=0; i < nfds; ++i)
{
//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
if(events[i].data.fd == listenfd)
{
connfd = Accept(listenfd,(SA*)&cliaddr, &clilen);
printf("connection from %s, port %d.\n",
Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buf, sizeof(buf)),
ntohs(cliaddr.sin_port));
//设置用于读操作的文件描述符和事件
ev.data.fd = connfd;
ev.events = EPOLLIN|EPOLLET;
//注册事件
Epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
}
//如果是已经连接的用户,并且收到数据,那么进行读入。
else if(events[i].events & EPOLLIN)
{
sockfd = events[i].data.fd;
if ( sockfd < 0 )
continue;
n = read(sockfd, buf, MAXLINE);
if ( n < 0)
{
// Connection Reset:你连接的那一端已经断开了,
//而你却还试着在对方已断开的socketfd上读写数据!
if (errno == ECONNRESET)
{
Close(sockfd);
events[i].data.fd = -1;
}
else
error_quit("read error");
}
//如果读入的数据为空
else if ( n == 0 )
{
Close(sockfd);
events[i].data.fd = -1;
}
else
{
//设置用于写操作的文件描述符和事件
ev.data.fd = sockfd;
ev.events = EPOLLOUT|EPOLLET;
//注册事件
Epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
}
//如果有数据发送
else if(events[i].events & EPOLLOUT)
{
sockfd = events[i].data.fd;
Writen(sockfd, buf, n);
//设置用于读操作的文件描述符和事件
ev.data.fd = sockfd;
ev.events = EPOLLIN|EPOLLET;
//注册事件
Epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
}
return 0;
}