epoll实现多路IO转接思路
lfd = socket();
bind();
listen();
//开始epoll监听
int epfd = epoll_create(1024); //监听红黑树的树根
struct epoll_event tep, ep[1024]; // tep用来设置单个fd属性,ep是epoll_wait()传出的满足监听事件的数组
tep. events = EPOLLIN; //初始化lfd的监听属性,每个加到树上的fd都要初始化监听属性
tep.data.fd = lfd
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep );//将lfd添加到监听红黑树上
while(1)
{
ret = epoll_wait(epfd, ep, 1024, -1); //实施监听,ret表示满足监听事件的总个数
for(i = 0; i<ret; i++)//把满足的事件都加到了ep数组中,ret就是总个数,就是循环上限
{ //要去判断这个事件是lfd还是cfd产生的
if(ep[i].data.fd == lfd)//lfd满足读事件,有新的客户端发起连接请求
{
cfd = Accept();
//要将新加的加入到红黑树中,首先初始化监听属性,把fd放到tep这个结构体中,放到数组中
tep.events = EPOLLIN;
tep.data.fd = cfd; //初始化cfd的监听属性
epoll_ctl(epfd, EPLOO_ADD, cfd, &tep);
}else
{
//不是lfd,那就是cfd,那就处理cfd的读事件,有客户端写数据
n = read(ep[i].data.fd, buf, sizeof(buf));
if(n == 0) //表示对端关闭
{
//要把这个节点从树上摘下来
epoll_ctl(epfd, EPOLL_CTL_DEL,cfd, &tep,NULL);
close(ep[i].data.fd); //客户端关闭了,服务器端也可以关闭了
}else if(n>0)//大于0表示已经read完了
{
toupper();
write(ep[i].data.fd, buf, n);
}
}
}
}
epoll函数实现的多路IO转接
#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/epoll.h>
#include<errno.h>
#include<ctype.h>
#include<string.h>
#include "wrap.h"
#define MAXLINE 8192
#define SERV_PORT 8000
#define OPEN_MAX 5000
int main(int argc, int argv[])
{
int i, listenfd, connfd, sockfd;
int n,num = 0;
ssize_t nready, edf, res;
char buf[MAXLINE], str[INET_ADDRSTRLEN];
socklen_t clilen;
struct sockaddr_in servaddr, cliaddr;
struct epoll_event tep, ep[OPEN_MAX];//tep是epoll_ctl的参数,ep[]是epoll_wait的参数
//ep[]是结构体数组,放的是tep这样的一个个的小结构体
//-------------------------------------------------------开始socket
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr));
Listen(listenfd, 20);
//-------------------------------------------epoll监听
//创建epoll模型(红黑树根节点
efd = epoll_create(OPEN_MAX);//OPEN_MAX是预估的节点数
if(epd == -1)
perr_exit("epoll_create error");
//先处理lfd, 放到tep结构体中,再将结构体挂到树上
tep.data.fd = listenfd;
tep.events = EPOLLIN;
res = epoll_ctl(efd, EPOLL_CTL_ADD, listefd, &tep)//结构体tep挂到以efd为根节点的树上
if(res == -1)
perr_exit("epoll_ctl error");
//------------------------------------------有了树根和第一个lfd,开始阻塞监听
while(1)
{
nready = epoll_wait(efd, ep, OPEN_MAX, -1);//ep是数组,-1表示永久阻塞,open_max为数组容量,返回的是满足监听事件的总个数⭐ep是不用自己去维护的!吗?
if(nready == -1)
perr_exit("epoll_wait error");
for(i = 0; i<nready; i++)
{
if(!(ep[i].events & EPOLLIN))//如果不是读事件,就继续
continue;
if(ep[i].data.fd == listenfd)//判断满足事件的fd是不是lfd
{//是,就给分配cfd
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (struct addr*)cliaddr, &clilen);
printf("received from %s at port %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));//是网络给了一个专门函数,端口就还是用简单的
printf("cfd %d -----client %d\n", connfd, ++num);
//把cfd加到tep结构体中,再挂到树上
tep.events = EPOLLIN;
tep.data.fd = connfd;
res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);
if(res == -1)
perr_exit("epoll_ctl error");
}else//是cfd
{
sockfd = ep[i].data.fd;
n = Read(sockfd, buf, MAXLINE);
if(n == 0)//客户端关闭连接
{
res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);//从树上摘下
if(res == -1)
perr_exit("epoll_ctl error");
Close(sockfd);
printf("client[%d] closed connection \n",sockfd);
}else if(n < 0)
{
perror("read < 0 error:");
res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
Close(sockfd);
}else//正常
{
for(i = 0; i < n; i++)
{
buf[i] = toupper(buf[i]);
}
Write(STDOUT_FILENO, buf, n);
Write(sockfd, buf, n);
}
}
}
}
Close(listenfd);
Close(efd);
return 0;
}
//所以那个ep数组是不用自己维护的==jin好