我在服务器程序上使用了epoll,但是遇到了一个问题。我做了一个简单的测试来重现此问题。服务器在接收客户端数据后关闭了套接字,然后客户端将重新连接服务器并继续发送数据。一段时间(约数十秒),新连接发送数据后,epoll_wait不再通知EPOLLIN事件。但是在epoll_wait超时后,我可以从新客户端的sockt fd接收数据。 epoll会丢失一些事件吗? ET和LT模式都进行了测试。
服务器代码:
#include <iostream>
#include <thread>
using namespace std;
#include <sys/epoll.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
inline string get_time_for_ms()
{
timeval tmval;
gettimeofday(&tmval,nullptr);
struct tm tml;
localtime_r(&tmval.tv_sec,&tml);
char buffer[30] = { 0 };
snprintf(buffer,30,"%04d-%02d-%02d %02d:%02d:%02d:%03ld", tml.tm_year+1900, tml.tm_mon+1, tml.tm_mday, tml.tm_hour, tml.tm_min, tml.tm_sec, tmval.tv_usec/1000);
return string(buffer);
}
int fd_=-1;
int client_fd_=-1;
int cnt=0;
void register_event(int fd,int oper)
{
epoll_event env;
env.data.fd=fd;
env.events=EPOLLIN;
epoll_ctl(fd_,oper,fd,&env);
cout<<get_time_for_ms()<<" epoll_ctl:"<<fd<<" thread:"<<this_thread::get_id()<<endl;
}
void recv_data(int fd)
{
while(true)
{
char buf[1024]={0};
int data_len=recv(fd,buf,1024,MSG_NOSIGNAL);
if(data_len<=0)
{
break;
}
else
{
cnt+=data_len;
cout<<get_time_for_ms()<<" epoll in recv len:"<<data_len<<",total len:"<<cnt<<endl;
}
}
}
void epoll_wait_handler()
{
while(true)
{
const static int event_max=5;
epoll_event envs[event_max]={0};
int ret=epoll_wait(fd_,envs,event_max,15000);
if(ret>0)
{
for(int i=0;i<ret;++i)
{
if(envs[i].events&EPOLLIN)
{
client_fd_=-1;
recv_data(envs[i].data.fd);
register_event(envs[i].data.fd,EPOLL_CTL_DEL);
shutdown(envs[i].data.fd,SHUT_RDWR);
close(envs[i].data.fd);
}
else if(envs[i].events&(EPOLLERR|EPOLLHUP))
{
cout<<"epoll error:"<<envs[i].data.fd<<" thread:"<<this_thread::get_id()<<endl;
}
else
{
cout<<"epoll_wait error:........"<<envs[i].data.fd<<endl;
this_thread::sleep_for(chrono::seconds(120));
}
}
}
else
{
printf("\033[31m%s%s thread:%lu\n\033[0m",get_time_for_ms().c_str()," epoll_wait timeout",this_thread::get_id());
this_thread::sleep_for(chrono::seconds(3));
if(client_fd_>0)
{
recv_data(client_fd_);
cout<<"recv return thread:"<<this_thread::get_id()<<endl;
this_thread::sleep_for(chrono::seconds(30));
shutdown(client_fd_,SHUT_RDWR);
close(client_fd_);
client_fd_=-1;
}
else
{
cout<<"not recv return thread:"<<this_thread::get_id()<<endl;
}
}
}
}
void server_listen()
{
int listen_fd_=socket(PF_INET,SOCK_STREAM,0);
int opt=1;
setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port = htons(7100);
inet_pton(AF_INET, "0.0.0.0",&addr.sin_addr);
if(bind(listen_fd_,(sockaddr*)&addr,sizeof(addr))==-1)
{
close(listen_fd_);
cout<<"bind error"<<endl;
}
listen(listen_fd_,128);
cout<<"bind success"<<endl;
while(true)
{
sockaddr_in saddr;
int ilen = sizeof(saddr);
int soc = accept(listen_fd_, (sockaddr *)&saddr, (socklen_t*)&ilen);
if(soc>0)
{
cout<<"accept new connect:"<<soc<<endl;
int old=fcntl(soc,F_GETFL);
int newfd=old|O_NONBLOCK;
fcntl(soc,F_SETFL,newfd);
client_fd_=soc;
register_event(soc,EPOLL_CTL_ADD);
}
}
}
int main()
{
fd_=epoll_create(5);
for(int i=0;i<1;++i)
{
thread t{epoll_wait_handler};
t.detach();
}
server_listen();
}
客户代码
#include <iostream>
#include <thread>
using namespace std;
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/time.h>
int fd_=-1;
int cnt=0;
inline string get_time_for_ms()
{
timeval tmval;
gettimeofday(&tmval,nullptr);
struct tm tml;
localtime_r(&tmval.tv_sec,&tml);
char buffer[30] = { 0 };
snprintf(buffer,30,"%04d-%02d-%02d %02d:%02d:%02d:%03ld", tml.tm_year+1900, tml.tm_mon+1, tml.tm_mday, tml.tm_hour, tml.tm_min, tml.tm_sec, tmval.tv_usec/1000);
return string(buffer);
}
void send_data()
{
const char *data="123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
int s_len = send(fd_,data, strlen(data),MSG_NOSIGNAL);
if(s_len>0)
{
cnt+=s_len;
cout<<get_time_for_ms()<<" send data:"<<s_len<<" total:"<<cnt<<endl;
}
}
void connect_server(bool nw);
void recv_data()
{
while(true)
{
char buf[1024]={0};
int data_len=recv(fd_,buf,1024,MSG_NOSIGNAL);
if(data_len<=0)
{
cout<<"recv error:"<<fd_<<endl;
close(fd_);
fd_=-1;
connect_server(false);
}
else
{
cout<<"recv successd:"<<buf<<endl;
}
}
}
void connect_server(bool nw)
{
int fd=socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port = htons(7100);
inet_pton(AF_INET,"172.16.5.47",&addr.sin_addr);
if (connect(fd, (sockaddr *)&addr, sizeof(addr))<0)
{
cout<<"connect error"<<endl;
return;
}
fd_=fd;
if(nw)
{
thread t{recv_data};
t.detach();
}
send_data();
}
int main()
{
connect_server(true);
this_thread::sleep_for(chrono::hours(1));
}