非阻塞socket与epoll

来源:互联网 发布:cn域名 群晖 备案 编辑:程序博客网 时间:2024/05/16 06:05
 阻塞socket
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
对于文件操作read,fread函数调用会将线程阻塞
对于socket,accept与recv,recvfrom函数调用会将线程阻塞
对于socket,accept与recv,recvfrom函数调用会将线程阻塞
为了避免整个线程被阻塞后挂起,所以在阻塞模式下,往往需要采用多线程技术。
一个进程中可并发的线程总数是有限的,在处理大量客户端socket连接(比如上万client socket)通过线程并发处理socket并不方便,效率也不高。


非阻塞socket
非阻塞调用指调用立刻返回。
在阻塞模式下,accept与recv,recvfrom函数调用会立刻返回。
在nonblocking状态下调用 accept函数,如果没有客户端socket连接请求,那么accept函数返回-1,同时errno值为EAGIN或者EWOUDBLOCK,这两个哄定义都为整数11.
在nonblocking状态下调用recv,recvfrom函数,如果没有数据,函数返回-1,同时errno值为11,如果socket已经关闭,函数返回0。
在nonblocking状态下对一个已经关闭的socket调用send函数,将引发一个SIGPIPE信号,进程必须捕捉这个信号,因为SIGPIPE系统默认的处理方式是关闭进程。
fcntl函数调用
fcntl函数可以将文件或者socket描述符设置为阻塞或者非阻塞状态。
int fcntl(int fd,int cmd,.../*arg*/);
参数fd为要设置的文件描述符或者socket
参数cmd,F_GETFl为得到的目前状态,F_SETFL为设置状态
宏定义O_NONBLOCK代表非阻塞,0代表阻塞.
返回值为描述符当前状态


用以下方法将socket设置为非阻塞方式 
int flags = fcntl(socket, F_GETFL, 0); 
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
用以下方法将socket设置为非阻塞方式 
int flags = fcntl(socket, F_GETFL, 0); 
fcntl(socket, F_SETFL, flags | O_NONBLOCK);


将非阻塞的设置回阻塞可以用


int flags = fcntl(socket, F_GETFL, 0); 
fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);




fcntl函数调用设置非阻塞例子
int opts=fcntl(st,F_GETFL);
if(opts<0)
{
ptintf("fcntl failed %s\n",strerror(errno));
}
opts=opts|O_NONBLOCK;
if(fcntl(st,F_SETFL,opes)>0)
{
printf("fcntl failed %s\n",strerror(errno));
}


epoll的系统调用:
epoll_create
epoll_Greate用来创建一个epoll文件描述符。
int epoll_create(int size);
创建一个epoll的句柄
参数size指定epoll所指定的最大句柄数
函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。
在用完句柄之后,需要用close()来关闭这个创建出来的epoll句柄。
epoll_ctl
epoll_ctl用来添加/修改/删除需要侦听的文件描述符及其事件
int epoll_ctl(int intepfd,int op,int fd,struct epoll_event *event);
参数epfd是epoll_Greate()的返回值
EPOLL_CTL_ADD:注册新的fd到epfd中
EPOLL_CTL_MOD:修改已经注册的fd的监听事件
EPOLL_CTL_DEL:从epfd中删除一个fd;
参数fd是需要监听的socket描述符
参数event通知内核需要监听什么事件

epoll_wait

epoll_wait接收发生在被侦听的描述符上的,用户感兴趣的IO事件。
每次添加/修改/删除/文件描述符都需要调用epoll_atl,所以要尽量少的调用epoll_ctl。
int cpoll_wait(int cpfd,struct cpoll_cvcnts,int maxevents,int timeout);
参数cpfd是cpoll_Greate()的返回值
参数events是一个epoll_events*的指针,当epoll_wait这个函数操作成功以后,epoll_events里面将储存所有的读写事件。
参数maxevents是当需要监听的所有socket句柄数
参数timeout是epoll_wait的超时,为0 的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,正整数表示等这么长时间
一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一条线程的话,则可以用0来保证主循环的效率。




epoll_wait返回之后应该是一个循环,遍历所有的事件



struct epoll_event结构如下:
typedef union epoll_data
{
void *ptr;
int fd;
_uint32_t u32l
_uint64_t u64;
}epoll_data_t;
struct epoll_event
{
_uint32_t events;
epoll_data_t data;
}


events定义
EPOLLIN:表示对应的文件描述符可以读(包括对端socket正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂断
EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的
EPOLLONESHOT:只监听一次时间,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要把这个socket加入到EPOLL队列里


关于ET、LT两种工作模式:
LT(Level Triggered)是缺省的工作方式,并且同时支持block和no-block socket


在LT模式中,内核通知一个文件描述符是否就绪了,然后可以对这个就绪的fd进行IO操作。
如果你不作任何操作,内核还是会继续通知你的,所以这种模式编程出错可能性要小一点
0 0
原创粉丝点击