epoll 在客户端断开连接时循环
epoll loops on disconnection of a client
我正在尝试使用 epoll
来实现套接字服务器。我有 2 个线程执行 2 个任务:
- 侦听传入连接
- 在屏幕上写入客户端正在发送的数据。
对于我的测试,我将客户端和服务器放在同一台计算机上,运行 3 或 4 个客户端。服务器工作正常,直到我没有通过发出CTRL-C
来杀死其中一个客户端:一旦我这样做,服务器就会开始循环并以非常快的速度打印来自其他客户端的数据。奇怪的是,
- 客户端每 2 秒发送一次数据,但服务器的速率更高
-
epoll_wait
也应该在其中一个客户端断开连接时打印一些东西,因为它也在检查 EPOLLHUP 或 EPOLLERR
。 -
epoll_wait
打印前应该等一会儿,因为我给了他 3000 毫秒的超时。
你能帮忙吗?可能是我以错误的方式将 epoll 描述符传递给另一个线程吗?我无法理解,因为代码看起来与周围的许多示例相似。
多谢
锰
// server.cpp
#include <iostream>
#include <cstdio>
#include <cstring>
extern "C" {
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <pthread.h>
}
#define MAX_BACKLOG 10
void* readerthread(void* args){
int epfd = *((int*)args);
epoll_event outwait[10];
while(true){
int retpw = epoll_wait( epfd, outwait,20, 3000 );
if( retpw == -1 ){
printf("epoll error %mn");
}else if( retpw == 0 ){
printf("nothing is ready yetn");
continue;
}else{
for( int i=0;i<retpw;i++){
if( outwait[i].events & EPOLLIN ){
int fd = outwait[i].data.fd;
char buf[64];
if( -1 == read(fd,buf,64) ){
printf("error reading %mn");
}
printf("%sn",buf);
}else{
std::cout << "other event" << std::endl;
}
}
}
}
}
int main(){
int epfd = epoll_create(10);
if( -1 == epfd ){
std::cerr << "error creating EPOLL server" << std::endl;
return -1;
}
pthread_t reader;
int rt = pthread_create( &reader, NULL, readerthread, (void*)&epfd );
if( -1 == rt ){
printf("thread creation %mn");
return -1;
}
struct addrinfo addr;
memset(&addr,0,sizeof(addrinfo));
addr.ai_family = AF_INET;
addr.ai_socktype = SOCK_STREAM;
addr.ai_protocol = 0;
addr.ai_flags = AI_PASSIVE;
struct addrinfo * rp,* result;
getaddrinfo( "localhost","59000",&addr,&result );
for( rp = result; rp != NULL; rp = rp->ai_next ){
// we want to take the first ( it could be IP_V4
// or IP_V6 )
break;
}
int sd = socket( AF_INET, SOCK_STREAM, 0 );
if(-1==sd ){
std::cerr << "error creating the socket" << std::endl;
return -1;
}
// to avoid error 'Address already in Use'
int optval = 1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if( -1==bind( sd, result->ai_addr, result->ai_addrlen ) ){
printf("%mn");
std::cerr << "error binding" << std::endl;
return -1;
}
while(true){
std::cout << "listen" << std::endl;
if( -1== listen(sd, MAX_BACKLOG ) ){
std::cerr << "listen didn't work" << std::endl;
return -1;
}
std::cout << "accept" << std::endl;
sockaddr peer;
socklen_t addr_size;
int pfd = accept( sd, &peer ,&addr_size );
if( pfd == -1 ){
std::cerr << "error calling accept()" << std::endl;
return -1;
}
epoll_event ev;
ev.data.fd = pfd;
ev.events = EPOLLIN;
std::cout << "adding to epoll list" << std::endl;
if( -1 == epoll_ctl( epfd, EPOLL_CTL_ADD, pfd, &ev ) ){
printf("epoll_ctl error %mn");
return -1;
}
}
}
// end of server.cpp
// client.cpp
#include <iostream>
#include <cstring>
#include <cstdio>
extern "C"{
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
}
int main(){
const char* servername = "localhost";
const char* serverport = "59000";
struct addrinfo server_address;
memset( &server_address, 0, sizeof(struct addrinfo) );
server_address.ai_family = AF_INET;
server_address.ai_socktype = SOCK_STREAM;
server_address.ai_protocol = 0; // any protocol
server_address.ai_flags = 0;
struct addrinfo * result, * rp;
int res = getaddrinfo( servername, serverport, &server_address, &result );
if( -1 == res ){
std::cout << "I cannot getaddress " << servername << std::endl;
return -1;
}
int fd = socket( server_address.ai_family
, server_address.ai_socktype
, server_address.ai_protocol );
if( -1 == fd ){
printf("I cannot open a socket %mn");
return -1;
}
for( rp = result; rp != NULL; rp = rp->ai_next ){
std::cout << "************" << std::endl;
if( -1 == connect( fd, rp->ai_addr, rp->ai_addrlen ) ){
close(fd);
}else{
std::cout << "connected" << std::endl;
break;
}
}
if( rp == NULL ){
std::cerr << "I couldn't connect server " << servername << std::endl;
}
while(true){
sleep(2);
pid_t me = getpid();
char buf[64];
bzero( buf,sizeof(buf));
sprintf( buf,"%ld",me );
write(fd,buf,sizeof(buf));
printf("%sn",buf);
}
}
// end of client.cpp
客户端断开连接由文件描述符上的 EOF 条件发出信号。系统将 EOF 视为文件描述符"可读"的状态。但是,当然,无法读取EOF条件。这是循环的来源。 epoll
就像断开连接的客户端的文件描述符始终可读一样。您可以通过检查read
何时返回 0 字节读取来检测您具有 EOF 条件。
处理 EOF 条件的唯一方法是以某种方式close
文件描述符。根据事物的确切进展方式,这可能是 shutdown(sockfd, SHUT_RD)
,shutdown(sockfd, SHUT_RDWR)
或close(sockfd);
。
除非您知道出于任何原因需要shutdown(2)
调用,否则我建议您使用 close
。当然,您应该记住在close
epoll
之前告诉文件描述符不再感兴趣。我不确定如果你不这样做会发生什么,但一种可能性是epoll
会出错。另一个是,epoll
将神秘地开始报告具有相同数值的新文件描述符的事件,然后再将其添加到epoll
应该关注的列表。
侧干净关闭的套接字将变得可读,read(2)
将返回0
,您必须检查这一点。正如现在编码的那样 - 级别触发的轮询 - epoll_wait(2)
每次都会返回,而无需等待告诉您仍未读取流的结束。
或者,您可以切换到边缘触发的轮询(EPOLLET
)并对EPOLLRDHUP
做出反应。
- 如何循环打印顶点结构
- 如何在C++中从两个单独的for循环中添加两个数组
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 正在尝试了解输入验证循环
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 循环后如何继续阅读
- 在 for 循环中使用 lambda 函数连接信号插槽
- Qt的事件循环线程是安全的还是原子的?处理"队列连接"时如何同步?
- 连接循环准确的Systemc编码样式,具有未达到的编码样式
- 如何将基本的qt连接转换为while循环
- C++类和相互连接的对象形成一个循环
- C++ OpenMP:将 for 循环拆分为静态偶数块,并在最后连接数据
- 在不关闭连接的情况下,在循环中增强asio加密
- 线程如何运行循环直到连接
- 建议使用一个循环来连接两个不带strcat的字符串的逻辑
- Winsock -客户端断开,关闭套接字循环/最大连接数
- boost::asio系统的无休止循环:第一次连接后出现9个错误
- listen()是否连续运行,或者我是否需要循环它以保持在套接字上接收连接
- epoll 在客户端断开连接时循环
- 如何连接char* while循环?c++