select 和 epoll的區別
epoll都是用來監聽套接字上是否有事件發生,簡單來講,select是輪詢方式,而epoll是觸發方式的,用回撥把資訊賦給event結構體。
select:輪詢檢查檔案描述符集合,實現方法如下:
fd_set fdRead;
//將檔案描述符集合清零
FD_ZERO(&fdRead);
//在檔案描述rd中增加檔案描述符iSocket,可以加多個
FD_SET(iSocket,&fdRead);
//select ,設定recv超時時間,寫的類似
int iRet = select(iSocket+ 1, &fdRead, NULL, NULL, &tvTimeOut);
switch(iRet)
{
case 0:
printf("Time out \n");
return -1;
case -1:
printf("Select Error\n");
return -2;
default:
if (FD_ISSET(iSocket,&fdRead))//測試iSocket是否在描述符集合中
{
iRecvLen = recv(iSocket,pcRecvBuf, iBufLen, 0);
if (iRecvLen == -1)
{
printf("Recverror\n");
return -3;
}
else if(0 == iRecvLen)
{
printf("Socketclose \n");
return -4;
}
else
{
break;
}
}
}
如果檔案描述符中有多個socket,便會輪詢檢查。
epoll:epoll的使用,首先通過epoll_create建立一個epoll物件,得到一個epoll的描述符,我們需要監聽哪個fd上的事件通過epoll_ctl,最後 epoll_wait會返回發生了事件的數目,具體的事件在引數events中。
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<sys/ioctl.h>
#include<sys/epoll.h>
#define MAX_EVENTS10
#define LISTENQ 12
int main()
{
int n = 0;
int listen_sock;
int conn_sock;
int nfds;
int epollfd;
int iRet = -1;
socklen_t len;
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];
char buf[32] = "tongzhilin";
/*create listen socket */
listen_sock = socket(AF_INET,SOCK_STREAM, 0);
int iSocketOpt= 1;
setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,(void *)&iSocketOpt, sizeof(int));
/*bind*/
struct sockaddr_instServerAddrIn;
stServerAddrIn.sin_family = AF_INET;
stServerAddrIn.sin_port = htons(6666);
stServerAddrIn.sin_addr.s_addr =htonl(INADDR_ANY);
iRet = bind(listen_sock,(structsockaddr*)&stServerAddrIn, sizeof(stServerAddrIn));
if(iRet < 0)
{
printf("bind error\n");
return -3;
}
/*listen*/
iRet = listen(listen_sock, LISTENQ);
if(iRet < 0)
{
printf("listenerror\n");
return -3;
}
/*epoll*/
epollfd = epoll_create(10);
if(epollfd == -1)
{
perror("epoll_create");
return -1;
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
//EPOLL_CTL_ADD:註冊;EPOLL_CTL_MOD修改;EPOLL_CTL_DEL:刪除
if(epoll_ctl(epollfd, EPOLL_CTL_ADD,listen_sock, &ev) == -1)
{
perror("epoll_ctl:listen_sock");
return -1;
}
while(1)
{
printf("Wait..\n");
nfds = epoll_wait(epollfd, events,MAX_EVENTS, -1);
if(nfds == -1)
{
perror("epoll_pwait");
return -1;
}
for (n = 0; n < nfds; ++n)
{
if(events[n].data.fd ==listen_sock)
{
conn_sock =accept(listen_sock, (struct sockaddr *)&stServerAddrIn, &len);
if(conn_sock == -1)
{
perror("accept");
return -1;
}
//非阻塞
int ulflag = 1;
ioctl(listen_sock,FIONBIO, &ulflag);
ev.events = EPOLLIN| EPOLLET;
ev.data.fd =conn_sock;
//EPOLL_CTL_ADD:註冊;EPOLL_CTL_MOD修改;EPOLL_CTL_DEL:刪除
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1)
{
perror("epoll_ctl:conn_sock");
return -1;
}
}
else
{
send(events[n].data.fd,buf, sizeof(buf), 0);
}
}
}
return 0;
}
總結:select如果同時建立的連線很多,但是隻有少數的有事件發生,這種情況下,效率就會很低。epoll的實現中就避免了該問題,對於要監管的每個fd都會有回撥函式,當該fd上發生事件時,會呼叫對應的回撥函式。這樣,在連線很多,少數事件發生的情況下,依舊會效率很高。當然,如果連線很多,大部分連線都有事件時,兩者的效率應該是差不多的。