在内核中创建epoll,accept创建io,判断是否加入到内核中,每一次将就绪的io切换到用户态
水平触发(LT) :满足IO复用条件即触发
边沿触发 (ET) : 新的IO就绪事件到达即触发
如客户端一次发送数据100个字节数据到服务端,服务端一次读50个字节,
服务器设置为水平触发,则recv执行2次,
服务器设置为边缘触发,则recv执行1次
使用场景: 对于发送的数据包较大,则设置为边缘触发触发,循环读取数据;数据包较小,则设置为水平触发
运行服务端程序,将阻塞在epoll_wait,当客户端连接时,服务端接收客户端buffer,并发送buffer给客户端
int sockfd= socket(AF_iNET,SOCK_STREAM,0);
if(sockfd == -1) {
return -1;
}
struct socketaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);
if(-1 = bind(sockfd,(struct sockaddr*)&servaddr),sizeof(servaddr)){
return -2;
}
//nonblock
int flag = fcntl(sockfd,F_GETFL,0);
flag |= O_NONBLOCK;
fcntl(sockfd,F_SETFL,flag);
listen(sockfd,10);
//epoll
int epfd = epoll_create(1);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sockfd; // 8个字节
epoll_ctl(epfd,EPOLL_CEL_ADD,sockfd,&ev);
struct epoll_event events[1024] = {0}; // events快递员装快递的袋子
struct sockaddr_in clientaddr;
socklen_t len = sizeof(client);
while(1) { // loop
int nready = epoll_wait(epfd,events,1024,-1);// 判读IO有没有就绪事件,快递员多长时间取快递 最后一个参数,-1 一直等待,0,不等待,1,等待一段时间
if(nready < 0 ) continue;
int i = 0;
// set 链接额的客户端 ready 盒子
for(i = 0; i< nready;i++) {
int connfd = events[i].data.fd;
if(sockfd == connfd) {
int clientfd = accept(sockfd,(struct sockaddr *)clientaddr,&len);
if(clientfd <= 0 ) {
continue;
}
printf("clientfd = %d",clientfd);
// EPOLLET 边沿触发
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev);
}
else if (events[i].events & EPOLLIN) {
char buffer[BUFFER_LENGTH] = {0};
int n = recv(connfd,buffer,BUFFER_LENGTH);
if(n > 0) {
// 服务器先接受客户端的buffer,再将buffer发送给客户端
printf("recv : %s\n",buffer);
send(connfd,buffer,n,0);
} else if (n == 0) {
printf("close\n");
// 用户搬走
epoll_ctl(epfd,EPOLL_CTL_DEL,connfd,NULL);
// 如不移除,connfd的值将会一直存在
close(connfd);
}
}
}
}