杂记:C语言给accept/listen设置超时的方法(真实有效)

1,209 阅读2分钟

背景

我的服务器是两段式访问确认机制,第二次等待客户端连接的accept操作会一直阻塞,直到连接出现或出错为止。但是我不希望它因为客户端出现某些不明问题而一直挡在那里,所以希望设置阻塞时间来自动释放资源。

思路

根据网上的方案,推荐用select函数来设置超时。 这个函数是通过检查缓冲区是否有数据来判断客户端连接是否到来。 如果我们在确定了客户端连接已经到来之后再执行accept来接受客户端请求的话,就防止了阻塞。

实践

根据网上的代码演示,大致为以下写法:

struct fd_set rdfs;

while(1){
    struct timeval  tv = {5 , 0};   // 设置5秒时间

    FD_ZERO(&rdfs);
    FD_SET(sockfd, &rdfs);

    if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪
    {
          recv() /  recvfrom()    //  从socket读取数据
    }
}

以上代码转自:http://www.dtmao.cc/java/76933.html

实测无效。

根据测试发现,该方法设置的超时很奇怪,无论我是否主动让客户端连接延迟回复,select都会阻塞设定的时间(5s),有点像是在select函数中加了个sleep(5s)。这显然不是我想要的。

不过经过测试发现select函数还是可以正确检测到当客户端请求到来后缓冲区变化的。所以可以通过自己设置超时计时,配合select检测的方式实现真正的超时检测。

最终实现

代码如下:

    time_t time = getCurTick();
    while (1) {
        struct fd_set rdfs;
        struct timeval  tv = { 0 , 0 };   // 设置0秒时间,使得select函数本身不会阻塞等待

        FD_ZERO(&rdfs);
        FD_SET(inNode->MySocket, &rdfs);

        if (select(inNode->MySocket + 1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪检查
        {
            cSocket = accept(inNode->MySocket, (struct socketaddr*)&c_addr, 0);//检查到了客户端请求,接收请求后退出。
            break;
        }
        if (getCurTick() - time > 5 && time > 0) //检查当前时间距离开始计时的时间是否超过5s
        {
            //超时清理资源,退出
            printf("client timeout 5s\n");
            closesocket(inNode->MySocket);
            ListRemoveNode(LIST_CONNECT_TCP, inNode);
            return -4;
        }
    }

注: struct fd_set rdfs; struct timeval tv = { 0 , 0 };
FD_ZERO(&rdfs); FD_SET(inNode->MySocket, &rdfs);

这些必须设置,比如如果设置为NULL的话select检测会无效。