LT自动挡(水平模式),ET手动挡(epoll垂直模式) 区别
ET 边沿触发的 边沿是EAGAIN错误,属于下降沿触发。
ET的驱动事件依靠socket的sk_sleep等待队列唤醒,这只有在有新包到来才能发生,数据包导致POLLIN,
ACK确认导致 sk_buffer destroy从而导致POLLOUT,
但这不是一对一得关系,是多对一(多个网络包产生一个POLLIN, POLLOUT事件)。
ET模式常见错误:
recv到了合适的长度, 程序处理完毕后就epoll_wait
这时程序可能长期阻塞,因为这时socket的 rev_buffer里还有数据,或对端close了连接,但这些信息都在上次通知了你,你没有处理完,就epoll_wait了
正确做法是:
recv到了合适的长度, 程序处理; 再次recv, 若果是EAGAIN则epoll_wait。
使用ET模式时, 程序自己驱动下,会发生socket被关闭的情况,这时要处理EPIPE信号和返回值。(如果不处理EPIPE那么会导致程序core掉)
总结:
ET 使用准则,只有出现EAGAIN错误才调用epoll_wait。
场景:
短连接,短数据包:
这种情形下测试的结果是ET和LT一样的效率。(所以不要以为用了ET效率就有提高)
测试指令
strace -c -fF ./your_prog
测试结果
后得到信息是 epoll_ctl 消耗的时间比 recv+send 还多。
总结原因:
整个程序是以网络事件来驱动的,所以每个连接都要epoll_ctl3次;
如果程序自己主动recv+send, 不行的时候再网络驱动的话,可以节省这些epoll_ctl开销。
对于长连接,大数据包:
因为 LT模式只能设置当时感兴趣的事件(如果不写数据也设置POLLOUT的话,会导致cpu 100%) ,所以要频繁调用epoll_ctl,内核也要多次操作链表,所以效率会比ET模式低。
# 程序范例
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#define PORT 3457
#define BACKLOG 100
#define BUF_EV_LEN 150
#define MAX_EPOLL_FD 8000
static char *policyXML="<cross-domain-policy><allow-access-from domain=/"*/" to-ports=/"*/"/></cross-domain-policy>";
static char *policeRequestStr="<policy-file-request/>";
int CreateTcpListenSocket();
int InitEpollFd();
void UseConnectFd(int sockfd);
void setnonblocking(int sock);
static int listenfd;
int main()
{
signal(SIGPIPE, SIG_IGN);
/**/pid_t pid;
if((pid = fork()) < 0){
printf("End at: %d",__LINE__);
exit(-1);
}
if (pid){
printf("End at: %d",__LINE__);
exit(0);
}
run();
}
int run(){
int epoll_fd;
int nfds;
int i;
struct epoll_event events[BUF_EV_LEN];
struct epoll_event tempEvent;
int sockConnect;
struct sockaddr_in remoteAddr;
int addrLen;
addrLen = sizeof(struct sockaddr_in);
epoll_fd = InitEpollFd();
if (epoll_fd == -1)
{
perror("init epoll fd error.");
printf("End at: %d",__LINE__);
exit(1);
}
printf("begin in loop./n");
while (1)
{
nfds = epoll_wait(epoll_fd, events, BUF_EV_LEN, 1000);
//sleep(3);
if(nfds>5) printf("connect num: %d/n", nfds);
if (nfds == -1)
{
printf("End at: %d",__LINE__);
perror("epoll_wait error.");
continue;
}
for (i = 0; i < nfds; i++)
{
if (listenfd == events[i].data.fd)
{
//printf("connected success/n");
sockConnect = accept(events[i].data.fd, (struct sockaddr*)&remoteAddr, &addrLen);
if (sockConnect == -1)
{
printf("End at: %d",__LINE__);
perror("accept error.");
continue;
}
setnonblocking(sockConnect);
tempEvent.events = EPOLLIN | EPOLLET;
tempEvent.data.fd = sockConnect;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockConnect, &tempEvent) < 0)
{
perror("epoll ctl error.");
printf("End at: %d",__LINE__);
return -1;
}
}
else
{
UseConnectFd(events[i].data.fd);
}
}
}
printf("---------------------------------/n/n");
}
int CreateTcpListenSocket()
{
int sockfd;
struct sockaddr_in localAddr;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("create socket fail");
printf("End at: %d",__LINE__);
return -1;
}
setnonblocking(sockfd);
bzero(&localAddr, sizeof(localAddr));
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(PORT);
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
unsigned int optval;
//设置SO_REUSEADDR选项(服务器快速重起)
optval = 0x1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, 4);
/*
//设置SO_LINGER选项(防范CLOSE_WAIT挂住所有套接字)
optval1.l_onoff = 0;
optval1.l_linger = 1;
setsockopt(listener, SOL_SOCKET, SO_LINGER, &optval1, sizeof(struct linger));
int nRecvBuf=320*1024;//设置为320K
setsockopt(listener ,SOL_SOCKET, SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
int nSendBuf=1024*1024;//设置为640K
setsockopt(listener ,SOL_SOCKET, SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
*/
if (bind(sockfd, (struct sockaddr*)&localAddr, sizeof(struct sockaddr)) == -1)
{
perror("bind error");
printf("End at: %d",__LINE__);
return -1;
}
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen error");
printf("End at: %d",__LINE__);
return -1;
}
return sockfd;
}
int InitEpollFd()
{
struct rlimit rt;
rt.rlim_max = rt.rlim_cur = MAX_EPOLL_FD;
if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
{
//perror("setrlimit");
printf("<br/> RLIMIT_NOFILE set FAILED: %s <br/>",strerror(errno));
//exit(1);
}
else
{
printf("设置系统资源参数成功!/n");
}
//epoll descriptor
int s_epfd;
struct epoll_event ev;
listenfd = CreateTcpListenSocket();
if (listenfd == -1)
{
perror("create tcp listen socket error");
printf("End at: %d",__LINE__);
return -1;
}
s_epfd = epoll_create(MAX_EPOLL_FD);
ev.events = EPOLLIN;
ev.data.fd = listenfd;
if (epoll_ctl(s_epfd, EPOLL_CTL_ADD, listenfd, &ev) < 0)
{
perror("epoll ctl error");
printf("End at: %d",__LINE__);
return -1;
}
return s_epfd;
}
void UseConnectFd(int sockfd)
{
int buffer_size=256;
char recvBuff[buffer_size+1];
int recvNum = 0;
int buff_size = buffer_size*10;
char *buff=calloc(1,buff_size);
while(1){
//memset(recvBuff,'/0',buffer_size);
recvNum = recv(sockfd, recvBuff, buffer_size, MSG_DONTWAIT);
if ( recvNum< 0) {
if (errno == ECONNRESET || errno==ETIMEDOUT) {//ETIMEDOUT可能导致SIGPIPE
close(sockfd);
}
break;
} else if (recvNum == 0) {
close(sockfd);
break;
}
//数据超过预定大小,则重新分配内存
if(recvNum+strlen(buff)>buff_size)
{
if((buff=realloc(buff,buff_size+strlen(buff)))==NULL)
{
break;
}
}
recvBuff[recvNum]='/0';
sprintf(buff,"%s%s",buff,recvBuff);
//printf("%s/n",recvBuff);
if(recvNum<buffer_size)
{
break;
}
}
if(recvBuff[0]=='0')printf("%s/n",buff);
if(strcmp(buff,policeRequestStr)==0)
{
sendMsg(sockfd, policyXML);
}else if(strlen(buff)>0){
sendMsg(sockfd,buff);
}
free(buff);
//printf("message: %s /n", recvBuff);
}
void setnonblocking(int sock)
{
int opts;
opts=fcntl(sock,F_GETFL);
if(opts<0)
{
perror("fcntl(sock,GETFL)");
printf("End at: %d",__LINE__);
exit(1);
}
opts = opts|O_NONBLOCK;
if(fcntl(sock,F_SETFL,opts)<0)
{
perror("fcntl(sock,SETFL,opts)");
printf("End at: %d",__LINE__);
exit(1);
}
}
//发送消息给某个连接
int sendMsg(int fd,char *msg)
{
if(fd<1) return 0;
while(1){
int l=send(fd,msg,strlen(msg)+1,MSG_DONTWAIT);
if(l<0){
if(errno==EPIPE){
printf(">Send pipe error: %d/n",errno);
printf("%d will close and removed at line %d!/n",fd,__LINE__);
printf(">Send pipe error, %d closed!/n",fd);
return -1;
}
break;
}
if (l <= strlen(msg)+1) {
//printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n", msg, errno, strerror(errno));
//return -1;
break;
}
}
return 1;
}
这里写代码片