c – 如何在多个连接之间进行epoll切换?
我正在使用epoll,我认为这是TCP套接字的典型方式(主要基于
this example,但略微适应C);绑定到端口的一个主侦听套接字,每个新的连接套接字(来自accept())也会在为recv()准备好时添加警报.我创建了一个测试脚本,基本上用连接和发送/接收来锤击它.当任何一个客户端连接时,它将完美无缺地工作.
但是,添加第二个同时测试客户端将导致其中一个挂起并失败.经过几天的调试,我终于决定将它正在使用的套接字ID吐出到一个文件中,我对我发现的东西感到困惑. 当一个脚本启动时,我只得到一个流,在这种情况下,6.然而,当第二个脚本启动时,我得到一个7的流7.只是7.它保持在7,完全与第二个客户端通信,完全忽略第一个,直到第一个达到超时并关闭. (然后,当客户端2重新连接时,它会获得ID 6.) 值得注意的是,此测试脚本不使用持久连接,它会在一些消息来回传递后断开连接并重新连接(为了更准确的模拟).但即使这样,客户端1也会被忽略.如果我将超时设置得足够高以至于客户端2实际上有时间退出,它仍然不会继续与客户端1一起恢复,因为它等待的只是有点丢失. 这是一种正常的行为,对于epoll(或一般的套接字)来说,当一个新任务出现时完全放弃先前的任务?我必须指定一些选项吗? 编辑:这是我可以显示的代码;我不一定期待“这就是你做错了”,更多的是“这些是一些会破坏/修复类似情况的东西”. #define EVENTMODE (EPOLLIN | EPOLLET | EPOLLRDHUP | EPOLLHUP) #define ERRCHECK (EPOLLERR | EPOLLHUP | EPOLLRDHUP) //Setup event buffer: struct epoll_event* events = (epoll_event*)calloc(maxEventCount,sizeof(event)); //Setup done,main processing loop: int iter,eventCount; while (1) { //Wait for events indefinitely: eventCount = epoll_wait(pollID,events,maxEventCount,-1); if (eventCount < 0) { syslog(LOG_ERR,"Poll checking error,continuing..."); continue; } for (iter = 0; iter<eventCount; ++iter) { int currFD = events[iter].data.fd; cout << "Working with " << events[iter].data.fd << endl; if (events[iter].events & ERRCHECK) { //Error or hangup: cout << "Closing " << events[iter].data.fd << endl; close(events[iter].data.fd); continue; } else if (!(events[iter].events & EPOLLIN)) { //Data not really ready? cout << "Not ready on " << events[iter].data.fd << endl; continue; } else if (events[iter].data.fd == socketID) { //Event on the listening socket,incoming connections: cout << "Connecting on " << events[iter].data.fd << endl; //Set up accepting socket descriptor: int acceptID = accept(socketID,NULL,NULL); if (acceptID == -1) { //Error: if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { //NOT just letting us know there's nothing new: syslog(LOG_ERR,"Can't accept on socket: %s",strerror(errno)); } continue; } //Set non-blocking: if (setNonBlocking(acceptID) < 0) { //Error: syslog(LOG_ERR,"Can't set accepting socket non-blocking: %s",strerror(errno)); close(acceptID); continue; } cout << "Listening on " << acceptID << endl; //Add event listener: event.data.fd = acceptID; event.events = EVENTMODE; if (epoll_ctl(pollID,EPOLL_CTL_ADD,acceptID,&event) < 0) { //Error adding event: syslog(LOG_ERR,"Can't edit epoll: %s",strerror(errno)); close(acceptID); continue; } } else { //Data on accepting socket waiting to be read: cout << "Receive attempt on " << event.data.fd << endl; cout << "Supposed to be " << currFD << endl; if (receive(event.data.fd) == false) { sendOut(event.data.fd,streamFalse); } } } } 编辑:代码已被修改,删除边缘触发确实会阻止epoll锁定到一个客户端.客户端无法接收数据仍存在问题;正在进行调试以查看它是否是同一个问题或其他问题. 编辑:在不同的诉讼中似乎是同样的错误.它确实尝试在第二个套接字上接收,但是进一步的日志记录报告它实际上几乎每次都会触发EWOULDBLOCK.有趣的是,日志报告的活动比保证的要多得多 – 超过150,000行,当我预计大约有60,000行时.删除所有“将阻塞”行会将其减少到我期望的数字……并且看,结果行创建完全相同的模式.将边缘触发放回原位可以阻止阻挡行为,显然可以防止它在没有明显原因的情况下尽可能快地旋转车轮.仍然没有解决原来的问题. 编辑:为了掩盖我的基础,我想我会在发送方做更多的调试,因为挂起的客户端显然在等待它永远不会得到的消息.但是,我可以确认服务器为它处理的每个请求发送响应;挂起的客户端的请求完全丢失了,因此从未回复过. 我还确保我的接收循环读取,直到它实际上命中EWOULDBLOCK(这通常是不必要的,因为我的消息头的前两个字节包含消息大小),但它没有改变任何东西. ‘Nother EDIT:我应该澄清一下,这个系统使用请求/回复格式,接收,处理和发送都是一次性完成的.正如您可能猜到的,这需要读取接收缓冲区,直到它为空,这是边沿触发模式的主要要求.如果收到的消息不完整(应该永远不会发生),服务器基本上会向客户端返回false,虽然从技术上讲,错误仍然允许客户端继续进行另一个请求. 调试已确认挂起的客户端将发出请求,并等待响应,但该请求永远不会触发epoll中的任何内容 – 它会在连接第二个客户端后完全忽略第一个客户端. 我也接受了接受后立即取消的尝试;在十万次尝试中,它还没有准备好一次. 更多编辑:很好,很好 – 如果有一件事可以让我进入任意任务,那就是质疑我的能力.所以,在这里,一切都必须出错的功能: bool receive(int socketID) { short recLen = 0; char buff[BUFFERSIZE]; FixedByteStream received; short fullSize = 0; short diff = 0; short iter = 0; short recSoFar = 0; //Loop through received buffer: while ((recLen = read(socketID,buff,BUFFERSIZE)) > 0) { cout << "Receiving on " << socketID << endl; if (fullSize == 0) { //We don't know the size yet,that's the first two bytes: fullSize = ntohs(*(uint16_t*)&buff[0]); if (fullSize < 4 || recLen < 4) { //Something went wrong: syslog(LOG_ERR,"Received nothing."); return false; } received = FixedByteStream(fullSize); } diff = fullSize - recSoFar; if (diff > recLen) { //More than received bytes left,get them all: for (iter=0; iter<recLen; ++iter) { received[recSoFar++] = buff[iter]; } } else { //Less than or equal to received bytes left,get only what we need: for (iter=0; iter<diff; ++iter) { received[recSoFar++] = buff[iter]; } } } if (recLen < 0 && errno == EWOULDBLOCK) { cout << "Would block on " << socketID << endl; } if (recLen < 0 && errno != EWOULDBLOCK) { //Had an error: cout << "Error on " << socketID << endl; syslog(LOG_ERR,"Connection receive error: %s",strerror(errno)); return false; } else if (recLen == 0) { //Nothing received at all? cout << "Received nothing on " << socketID << endl; return true; } if (fullSize == 0) { return true; } //Store response,since it needs to be passed as a reference: FixedByteStream response = process(received); //Send response: sendOut(socketID,response); return true; } 如您所见,它在遇到错误后无法循环.我可能不会使用C语言,但是我已经编写了足够长的时间来检查这些错误,然后再寻求帮助. bool sendOut(int socketID,FixedByteStream &output) { cout << "Sending on " << socketID << endl; //Send to socket: if (write(socketID,(char*)output,output.getLength()) < 0) { syslog(LOG_ERR,"Connection send error: %s",strerror(errno)); return false; } return true; } 怎么样EWOULDBLOCK呢?就像我的主板融化一样 – 我会解决它.但它还没有发生,所以我不打算解决它,我只是确定我知道它是否需要修复. 不,process()不对套接字做任何事情,它只接受并返回一个固定长度的char数组.同样,这个程序与一个客户端完美配合,而不是两个或更多. 最后编辑:经过更多调试后,我找到了问题的根源.我会继续回答自己. 解决方法
1)不要使用EPOLLET.它更复杂.
2)在接收或读取功能中,确保在获得EWOULDBLOCK后不再调用读取或接收.回去等待epoll命中. 3)不要试图查看数据或测量有多少数据.请尽快阅读. 4)在关闭它之前从epoll集中删除套接字,除非你是肯定的,没有对底层连接端点的其他引用. 它真的很简单.如果你做正确的四件事,你就不会有问题.最有可能的是,你拙劣2. 另外,当你去发送时,你如何应对’EWOULDBLOCK’?你的sendOut功能是什么样的? (有很多正确的方法可以做到,但也有很多错误的方法.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |