使用Linux epoll模型的LT水平触发模式,当socket可写时,会不停的触发socket可写的事件,如何处理?
-----网络流传的腾讯面试题
方法一:每一次需要写入时,将fd加入epoll,轮询到可写并写完数据后,将fd移除epoll管理
既然当socket可写时,会不停触发事件,那么从一开始就将需要写入的fd不加入epoll的EPOLLOUT事件中,当需要写入的时候,再加入epoll管理,轮询fd可写,数据全部写完后,将fd移除epoll管理,避免反复通知
这种方法需要反复添加和删除
方法二:需要写入时,直接调用send或write,当返回错误码EAGAIN时,才将fd加入epoll管理,等待fd可写后写入数据,写入完成后,将fd移出epoll
改进的做法相当于认为socket在大部分时候是可写的,不能写了再让epoll帮忙监控
鄙人根据方法二写的send函数:
int epoll_LT_send(int fd, char *buf, int length) {
int ret = send(fd, buf, length, 0); // 需要写入时,直接调用send
if (ret < 0) { // 当返回错误码EAGAIN时,才将fd加入epoll管理
if(errno == EAGAIN || errno == EWOULDBLOCK) {
int epfd = epoll_create(1);
if (epfd == -1) {
perror("epoll_create");
return -1;
}
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLOUT;
// 将fd加入epoll管理
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
perror("epoll_ctl");
close(epfd);
return -1;
}
ret = 0;
while(1) { // 等待fd可写后写入数据
struct epoll_event events;
int nready = epoll_wait(epfd, &events, 1, -1);
if (nready == -1) {
perror("epoll_wait");
close(epfd);
return -1;
} else if (nready == 0) {
continue;
}
do { // 写入数据
int r = send(fd, buf+ret, (length - ret), 0);
if (r > 0) {
ret += r;
} else if (r == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
close(epfd);
return ret;
} else {
perror("send");
close(epfd);
return -1;
}
}
} while (ret < length);
break;
}
// 写入完成后,将fd移出epoll
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
}
return ret;
} else if (ret == 0) { // disconnect
perror("send");
return -1;
}
return ret;
}