当本地关闭套接字时,poll()无法检测到事件?

时间:2011-02-18 09:29:48

标签: linux sockets

我正在开发一个将TCP / IP客户端程序移植到嵌入式ARM-Linux控制器板上的项目。客户端程序最初是用epoll()编写的。但是,目标平台已经很老了;唯一可用的内核是2.4.x,不支持epoll()。所以我决定在poll()中重写I / O循环。

但是当我测试代码时,我发现poll()没有按照我的预期行事:当TCP / IP客户端套接字在本地由另一个线程关闭时,它不会返回。我写了一个非常简单的代码来做一些测试:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <poll.h>

struct pollfd   fdList[1];

void *thread_runner(void *arg)
{
    sleep(10);
    close(fdList[0].fd);
    printf("socket closed\n");
    pthread_exit(NULL);
}

int main(void)
{
    struct  sockaddr_in hostAddr;
    int sockFD;
    char buf[32];
    pthread_t handle;

    sockFD = socket(AF_INET, SOCK_STREAM, 0);
    fcntl(sockFD,F_SETFL,O_NONBLOCK|fcntl(sockFD,F_GETFL,0));

    inet_aton("127.0.0.1",&(hostAddr.sin_addr));
    hostAddr.sin_family = AF_INET;
    hostAddr.sin_port  = htons(12345);
    connect(sockFD,(struct sockaddr *)&hostAddr,sizeof(struct sockaddr));

    fdList[0].fd = sockFD;
    fdList[0].events = POLLOUT;

    pthread_create(&handle,NULL,thread_runner,NULL);

    while(1) {
        if(poll(fdList,1,-1) < 1) {
            continue;
        }
        if(fdList[0].revents & POLLNVAL ) {
            printf("POLLNVAL\n");
            exit(-1);
        }
        if(fdList[0].revents & POLLOUT) {
            printf("connected\n");
            fdList[0].events = POLLIN;
        }
        if(fdList[0].revents & POLLHUP ) {
            printf("closed by peer\n");
            close(fdList[0].fd);
            exit(-1);
        }
        if(fdList[0].revents & POLLIN) {
            if( read(fdList[0].fd, buf, sizeof(buf)) < 0) {
                printf("closed by peer\n");
                close(fdList[0].fd);
                exit(-1);
            }
        }
    }
    return 0;
}

在这段代码中,我首先创建一个TCP客户端套接字,设置为非阻塞模式,添加到poll(),并在另一个线程中关闭()套接字。结果是:当套接字关闭时,永远不会打印“POLLNVAL”。

这是poll()的预期行为吗?如果我选择select()而不是poll()来帮助它吗?

1 个答案:

答案 0 :(得分:9)

是的,这是预期的行为。您可以通过在套接字上使用shutdown()而不是close()来解决此问题。

参见例如http://www.faqs.org/faqs/unix-faq/socket/第2.6节

编辑:预期的原因是poll()和select()对其中一个fd上发生的事件做出反应。 close()删除fd,它根本不存在,因此它不能有任何与之关联的事件。