EPOLLOUT例子 EPOLLOUT触发条件

下面的代码都在ET下工作

群里小伙伴没搞懂EPOLLOUT 再写2个例子; 2个例子都是回声服务器代码;

关于EPOLLET的基础 , 以及EPOLLIN|EPOLLOUT|EPOLLET 一起注册的例子: EPOLLET简单例子

 

EPOLLOUT 的说明:

LT模式下:

与select 一致. 只要可写就一直触发

 

在ET模式下:

socket 一般情况下需要非阻塞的, 与EAGAIN 这个错误 配合使用. 

如果是默认的阻塞socket, 不是不可以,而是你需要做额外的事, 每一次的read/write , 你都需要去getsockopt( sock,SO_ERROR,...)

直到获取 EAGAIN 算是正常读/写. 否则, 你完蛋了. 这个socket将不再收到 epoll 的触发条件了


对于EPOLLIN : 如果状态改变了[ 比如 从无到有],那么只要输入缓冲区可读就会触发

对于EPOLLOUT: 如果状态改变了[比如 从满到不满],只要输出缓冲区可写就会触发;

EPOLLOUT那么具体什么时间点触发呢 

1. EPOLL_CTL_ADD或EPOLL_CTL_MOD 时 , 如果输出缓冲区状态改变了

2. 不论注册方式是 EPOLLIN | EPOLLOUT | EPOLLET 还是 EPOLLOUT | EPOLLET 只要含EPOLLOUT

只要状态改变就能触发.

     状态改变到底是什么?   简单理解:

     EPOLLOUT:  满->不满 

     EPOLLIN   :  空->不空

     有没有发现 , 都跟边缘有关

 

3.对于 send / write 需要依靠 EPOLLOUT 触发才调用吗 ? 什么时候需要 注册上 EPOLLOUT ?

   不需要.  如果要 send / write 那么就直接调用, 如果返回值 > 0  , 证明数据已经复制到发送缓冲区中.一切正常.

    如果 send / write 返回 < 0 且 errno == EAGAIN . 此时说明发送缓冲区满了. 那么需要把 剩余的字节保存起来,

    然后注册上 EPOLLOUT , 直到epoll_wait 返回 , 说明发送缓冲区可写, 再把 之前保存起来的数据 发送,

    如果此时 write 返回 > 0  那就把EPOLLOUT 取消掉. 

    简单来说 :  1. 直接发送  2. 看返回值, 没发送完才挂上EPOLLOUT  3. 发送完就把EPOLLOUT 取消掉
 

 

下面2个例子都是关于EPOLLOUT,

第一个例子利用EPOLL_CTL_MOD来触发EPOLLOUT,这种方式不太好,需要利用一次系统调用epoll_wait来触发;

第2个例子在读完时, 手动调用一次发送, 如果发送遇到 EAGAIN / EWOULDBLOCK,才把EPOLLOUT注册上去,等待

EPOLLOUT事件发生再去发送, 如果不需要发送则把EPOLLOUT关闭;

 

另外有关于下面例子中 util.h 头文件就包含了一些常用头文件,所以就不贴了;

再次注意, 我没有对监听socket设置EPOLLET , 如果你设置了那么需要 while(1) accept().. if(errno==EAGAIN)break; 

否则会漏掉客户端连接的.

 

另外注意不论是write, send ..等这些个函数,都是把数据复制到socket输出缓冲区中 .并不是发送到对端的意思;

还有close(fd) 会附带着 epoll_ctl ( ... , EPOLL_CTL_DEL, ...); 自动从epoll中清除

 

第一个关于EPOLLOUT的例子:

这个例子使用 EPOLL_CTL_MOD 来主动触发EPOLLOUT事件 ;

当然这种方式需要让下一次epoll_wait返回一次来触发,不是特别好;

[ 当 EPOLL_CTL_ADD或EPOLL_CTL_MOD 时 , 如果socket输出缓冲区没满就能触发一次. ] 

下面这个例子唯一需要注意的就 3行代码:

                    //把EPOLLOUT一起注册上去
                    ev.events = evts[i].events | EPOLLOUT;
                    ev.data.ptr = pData;
                    
                    //利用EPOLL_CTL_MOD将触发EPOLLOUT的特性
                    epoll_ctl(epfd,EPOLL_CTL_MOD,pData->fd,&ev);
#include "util.h"

#define  BUFF_SIZE 1024
#define  MAX_EVENTS 1024



static int setnonblock(int fd , int nonblock){
    int flag = fcntl(fd,F_GETFL,0);
    if(nonblock)
        return fcntl(fd,F_SETFL,flag|O_NONBLOCK);
    else
        return fcntl(fd,F_SETFL,flag&~O_NONBLOCK);
}

//每个套接字关联的信息
typedef struct _ev_data
{
    int fd; //socket
    char * buffer; //缓冲区
    int nread; //读入字节长度
    int start_write_index; //记录下一次写的位置
    int epoll_fd; // 属于哪个epoll
} ev_data;

void free_event(ev_data * );

void free_event(ev_data * pEv){ //释放内存用的
    if(!pEv)
        return;
    if(pEv->buffer){
        free(pEv->buffer);
    }
    free(pEv);
}

//输出函数
void write_handler(ev_data *ptr)
{
    static struct epoll_event ev ={0,{0}};

    if(!ptr || !ptr->buffer){
        printf("write_handler error , ptr is empty!\n");
        return;
    }

    //需要写入socket输出缓冲区的长度
    int left = ptr->nread - ptr->start_write_index;

    //从哪里开始写
    char *pbuffer = ptr->buffer + ptr->start_write_index;
    
    //返回值
    int nwriten = -1;
    // 总共写入的字节数
    int write_bytes = 0;
    //sock输出缓冲区是否满了
    int ful

你可能感兴趣的:(帮别人写作业,unixc,EPOLLOUT,EPOLLET)