[转] linux epoll 问题小结

1,server端的fd不需要设置et模式

我们在创建socket成功后会有个listenfd,listenfd = socket(AF_INET, SOCK_STREAM, 0)

然后会把这个fd加入epoll wait队列中,网上很多没有经过验证的代码是这样写的:

ev.data.fd = listenfd;

ev.events=EPOLLIN|EPOLLET;

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

这样会导致服务器在并发处理客户端连接时,丢失部分连接,或者说丢失epoll事件

表现为:采用netstat查看网络,会看到Recv-Q大于0,但是程序执行不到accept代码段。

这个也和具体内核有关系,但是最好代码方面不要这么写。

正确的写法为:ev.events=EPOLLIN;   这里不用指定EPOLLET

2,如果采用et模式,读的时候要读完数据

3,要处理发送,接收函数的返回值和错误码。如:read,recv,send,write,errno

write如果返回-1,可能是写缓冲满,需要等待out事件再写入

 

下面贴个基本模型,代码不能运行,仅参考

struct _sgc_epoll_socketserver_config_s{

char * local_addr;// 监听的本地地址

int port;// 监听的端口

int epfd;// epoll 文件描述符

size_t buffer_size;// 缓冲区大小,单位byte

int maxclients;// 最多允许连接的客户端数

sgc_epoll_client_new_handler_t handler_new;// 有新连接处理函数

sgc_epoll_client_msg_handler_t hander_read;// 读取数据

sgc_epoll_client_close_hander_t hander_close;// 断开

};

 

 

/**

 * 启动socket服务

 */

void sgc_serversocket_start(sgc_epoll_socketserver_config_t *config)

{

int epfd, listenfd,connfd,readyfds,i,sockfd, clients, nreadbytes;

int flag=1,len=sizeof(int);

int cfg_sndbuff = 1024 * 1024;// 1M

int cfg_revbuffer = 1024 * 1024; // 1M

 

struct epoll_event ev, events[config->maxclients];

struct sockaddr_in clientaddr;

struct sockaddr_in serveraddr;

socklen_t clilen = sizeof(struct sockaddr_in);

 

if (config->handler_new == NULL ||

config->hander_read == NULL){

sgc_log_error("config error hand is NULL\n");

exit(1);

}

 

char * buffer = (char *)malloc(config->buffer_size);

memset(buffer, 0, config->buffer_size);

 

epfd = epoll_create(config->maxclients);

config->epfd = epfd;

 

listenfd = socket(AF_INET, SOCK_STREAM, 0);

 

if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, len))

sgc_log_error("set sockopt SO_REUSEADDR error\n");

if (setsockopt(listenfd, SOL_SOCKET, SO_SNDBUF, &cfg_sndbuff, len))

sgc_log_error("set sockopt SO_SNDBUF error\n");

if (setsockopt(listenfd, SOL_SOCKET, SO_SNDBUF, &cfg_revbuffer, len))

sgc_log_error("set sockopt SO_SNDBUF error\n");

 

setnonblocking(listenfd);

 

ev.data.fd = listenfd;

ev.events=EPOLLIN;

 

// 监听端口

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

bzero(&serveraddr, sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

char *local_addr= config->local_addr;

inet_aton(local_addr,&(serveraddr.sin_addr));

 

serveraddr.sin_port=htons(config->port);

 

if (bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr)))

{

sgc_log_error("bind fail %s:%d\n", config->local_addr, config->port);

exit(1);

}

 

sgc_log_debug("start listen %s:%d backlog:%d\n", local_addr, config->port, LISTEN_BACKLOG);

if (listen(listenfd, LISTEN_BACKLOG))

{

sgc_log_error("listen %s:%d max:%d error\n", config->local_addr, config->port, config->maxclients);

exit(1);

}

 

clients = 0;

for (;;) {

readyfds=epoll_wait(epfd,events,config->maxclients,EPOLL_WAIT_TIMEOUT);

 

if (readyfds < 0){

sgc_log_error("epoll_weit errno:%d %s\n", errno, strerror(errno));

}else if (readyfds > 0)

sgc_log_debug("readyfds:%d errno:%d err:%s\n", readyfds, errno, strerror(errno));

 

for(i=0;i<readyfds;++i)

{

// 新用户连接

if(events[i].data.fd==listenfd)

{

if (clients >= config->maxclients){

sgc_log_debug("clients full,close:%s socket fd:%d clients:%d\n", inet_ntoa(clientaddr.sin_addr), listenfd, clients);

continue;

}else{

clients++;

connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);

if(connfd<0){

sgc_log_error("accept error connfd<0 fd=%d errno:%d %s\n", connfd, errno, strerror(errno));

exit(1);

}

setnonblocking(connfd);

ev.data.fd=connfd;

ev.events=EPOLLIN|EPOLLET;

 

sgc_log_debug("new clients:%d, num:%d %s\n", connfd, clients, inet_ntoa(clientaddr.sin_addr));

config->handler_new(connfd, &clientaddr, config);

 

epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

}

}else if(events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。

{

if ((sockfd = events[i].data.fd) < 0){

sgc_log_error("EPOLLIN fd < 0\n");

continue;

}

 

// 一次读完缓冲区

memset(buffer, 0, config->buffer_size);

while((nreadbytes = recv(sockfd, buffer, config->buffer_size, 0)) > 0)

{

sgc_log_debug("read fd:%d len:%d\n", sockfd, nreadbytes);

config->hander_read(sockfd, buffer, nreadbytes);

 

memset(buffer, 0, config->buffer_size);

}

sgc_log_debug("read finish nreadbytes:%d fd:%d errno:%d, err:%s\n", nreadbytes, sockfd, errno, strerror(errno));

if (nreadbytes < 0){

if (errno == ECONNRESET){

EPOLL_CLOSE_CODE("connrset")

}

}else if (nreadbytes == 0){

EPOLL_CLOSE_CODE("normal")

}

}else{// 其它异常情况,关闭

sockfd = events[i].data.fd;

EPOLL_CLOSE_CODE("exception")

}

}

}

}

 

posted @ 2018-07-06 14:30  陈晓涛  阅读(317)  评论(0编辑  收藏  举报