在服务器中使用TCP Keep-Alives来摆脱空闲客户端

时间:2015-12-25 09:23:08

标签: linux sockets ubuntu tcp

我有一个服务器来从不同的客户端收集 Tcp数据到某个端口。我有一个场景,每当客户端创建tcp连接并保持空闲超过让我们说30分钟,那么我需要关闭连接。

我已经了解了 TCP keep alive 来跟踪对等体是否已经死亡,而且我发现客户端使用的示例大多数情况。同样,我可以在服务器端使用它来轮询连接是否处于活动状态?

此外,在 linux sysctl.conf 中,有一个用于编辑值的配置文件。这似乎是在某些不活动之后整个tcp连接被破坏。我需要这样,设备的某些连接会在一段时间不活动后被销毁,但不会关闭整个tcp端口连接。

我正在使用 ubuntu 创建服务器以收集tcp连接。我可以在服务器代码中使用 TCP Keep-Alives 来查找非活动客户端并关闭特定客户端吗?或者在服务器端有没有其他方法来实现这样的功能?

并且在浏览网页时提到了

(getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen)

这个getsockopt用于主tcp连接,设置似乎是设置是与服务器的整个连接。

然而,我需要的是特定的客户。我有事件服务器代码 此处 client_fd 已被接受,现在如果在一定时间内未收到通过此客户端的下一个数据,我需要关闭此client_fd。

void event_server(EV_P_ struct ev_io *w, int revents) {
    int flags;
    struct sockaddr_in6 addr;
    socklen_t len = sizeof(addr);

    int client_fd;

    // since ev_io is the first member,
    // watcher `w` has the address of the 
    // start of the _sock_ev_serv struct
    struct _sock_ev_serv* server = (struct _sock_ev_serv*) w;
    server->socket_len = len;

    for (;;) {
        if ((client_fd = accept(server->fd, (struct sockaddr*) &addr, &len)) < 0) {
            switch (errno) {
            case EINTR:
            case EAGAIN:
                break;
            default:
                zlog_info(_c, "Error accepting connection from client \n");
                //perror("accept");
            }
            break;
        }
        char ip[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, &addr.sin6_addr, ip, INET6_ADDRSTRLEN);
        char *dev_ip = get_ip(ip);
        server->device_ip = dev_ip;

        zlog_debug(_c,"The obtained ip is %s and dev_ip is %s", ip, dev_ip);

        /** check for the cidr address for config_ip **/
        char *config_ip;
        config_ip = get_config_ip(dev_ip, _client_map);
        zlog_debug(_c,"The _config ip for dev_ip:%s is :%s", dev_ip, config_ip);
        if (config_ip == NULL) {
            zlog_debug(_c,"Connection attempted from unreigistered IP: %s", dev_ip);
            zlog_info(_c, "Connection attempted from unregistered IP : %s", dev_ip);
            AFREE(server->device_ip);            
            continue;
        }

        json_t *dev_config;
        dev_config = get_json_object_from_json(_client_map, config_ip);
        if (dev_config==NULL) {
            zlog_debug(_c,"Connection attempted from unreigistered IP: %s", dev_ip);
            zlog_info(_c, "Connection attempted from unregistered IP : %s", dev_ip);
            AFREE(server->device_ip);
            continue;
        }

        if ((flags = fcntl(client_fd, F_GETFL, 0)) < 0 || fcntl(client_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
            zlog_error(_c, "fcntl(2)");
        }


        struct _sock_ev_client* client = malloc(sizeof(struct _sock_ev_client));
        client->device_ip = dev_ip;
        client->server = server;
        client->fd = client_fd;

        // ev_io *watcher = (ev_io*)calloc(1, sizeof(ev_io));
        ev_io_init(&client->io, event_client, client_fd, EV_READ);
        ev_io_start(EV_DEFAULT, &client->io);
    }
}

2 个答案:

答案 0 :(得分:1)

TCP keep alives不会检测空闲客户端,而是检测死连接,即如果客户端在没有关闭连接的情况下崩溃或者线路已经死亡等等。但是如果客户端只是空闲但没有死,则连接仍然是打开的。任何尝试向客户端发送空数据包(保持活动数据包)将导致来自客户端的ACK,因此保持活动状态不会报告死连接。

要检测空闲客户端,请使用超时读取(SO_RCVTIMEO)或使用select,poll或类似功能的超时。

答案 1 :(得分:-1)

我已经实现了以下机制来检测Socket IO活动的空闲状态。

我的Socket包含在某些类UserConnection中。该类还有一个属性lastActivtyTime。每当我对此Socket进行读写操作时,我都会更新此属性。

我还有一个后台Reaper线程,它将迭代所有UserConnection个对象并检查lastActivtyTime。 如果当前时间 - lastActivtyTime大于配置的阈值参数(如15秒 ),我将关闭空闲连接。

在您的情况下,当您遍历所有UserConnections时,您可以检查 client_id 以及 30分钟不活动的阈值 关闭空闲连接。