首页 > 解决方案 > 在使用C语言的socket编程中,如何设置服务器接受客户端建立连接的时间限制?

问题描述

我正在开发一个涉及 1 个客户端和 1 个服务器的应用程序。

我希望服务器只监听 5 秒钟的连接。如果客户端未尝试建立连接,则服务器应停止侦听并返回错误消息。如果客户端尝试建立连接,那么服务器应该接受连接。

如果客户端没有尝试建立连接,服务器将永远监听。我想让服务器只听5秒,怎么实现呢?

这是服务器端的输出——服务器永远在等待客户端:

图片

void reception(){
    int sockfd, connfd, len; 
    struct sockaddr_in servaddr, cli; 

    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd == -1) { 
        printf("socket creation failed...\n"); 
        exit(0); 
    } 
    else
        printf("Socket successfully created..\n"); 
    bzero(&servaddr, sizeof(servaddr)); 

    servaddr.sin_family = AF_INET; 
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    servaddr.sin_port = htons(PORT); 

    if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { 
        printf("socket bind failed...\n"); 
        exit(0); 
    } 
    else
        printf("Socket successfully binded..\n"); 

    if ((listen(sockfd, 5)) != 0) { 
        printf("Listen failed...\n"); 
        exit(0); 
    } 
    else
        printf("Server listening..\n"); 
    len = sizeof(cli); 

    connfd = accept(sockfd, (SA*)&cli, &len); 

    /*
        Some function should be added at this point to 
        stop the server from listening after 5 seconds

    */
    
    if (connfd < 0) { 
        printf("server acccept failed...\n"); 
        exit(0); 
    } 
    else
        printf("server acccept the client...\n"); 

    receive(connfd); 
    close(sockfd); 

}

标签: socketssetsockopttime-limitinggetsockopt

解决方案


You are using the listening socket in blocking mode, so accept() will block the calling thread until a client connects. To use a timeout, call select() or (e)poll() first to wait for the listening socket to enter a readable state indicating a connection is pending BEFORE you then call accept().

For example:

...

fd_set rds;
FD_ZERO(&rds);
FD_SET(sockfd, &rds);

struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;

int res;
if ((res = select(sockfd+1, &rds, NULL, NULL, &timeout)) <= 0)
{
    if (res < 0)
        printf("select failed...\n"); 
    else
        printf("Time out...\n"); 
    exit(0); 
}

connfd = accept(sockfd, (SA*)&cli, &len); 
...
...

struct pollfd pfd;
pfd.fd = sockfd;
pfd.events = POLLIN;
pfd.revents = 0;

int res;
if ((res = poll(&pfd, 1, 5000)) <= 0)
{
    if (res < 0)
        printf("poll failed...\n"); 
    else
        printf("Time out...\n"); 
    exit(0); 
}

connfd = accept(sockfd, (SA*)&cli, &len); 
...
int epfd = epoll_create(1);
if (epfd < 0)
{
    printf("epoll_create failed...\n"); 
    exit(0);
}

struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) < 0)
{
    printf("epoll_ctl failed...\n"); 
    exit(0);
}

struct epoll_event event;
int res;
if ((res = epoll_wait(epfd, &event, 1, 5000)) <= 0)
{
    if (res < 0)
        printf("epoll_ctl failed...\n"); 
    else
        printf("Time out...\n"); 
    exit(0);
}

close(epfd);

connfd = accept(sockfd, (SA*)&cli, &len); 
...

Either way, note that there is a very small race condition created by these scenarios. If the client connects and then disconnects before accept() is called, accept() may block waiting for a new pending connection, or it may return a valid file descriptor that fails on subsequent I/O calls. There is no guarantee one way or the other.

To solve the race condition, you can either:

  • put the listening socket into non-blocking mode. If select() reports the listening socket has a pending connection, but accept() is not able to accept it right away, then you can act accordingly.

  • use accept() on a listening socket in blocking mode, as you originally were, but use alert() to schedule accept() to be interrupted if it doesn't exit before 5 seconds have elapsed. See this answer for an example.


推荐阅读