Laxmi Kadariya
Laxmi Kadariya

Reputation: 1103

using TCP Keep-Alives in server to get rid of idle client

I have a server to collect Tcp data from different clients to a certain port. I have a scenario that whenever the client creates tcp connection and remain idle for more than let's say 30 min then I need to close the connection.

I have learned about TCP keep alive to track that the peer is dead or not and Mostly I found examples used in client side. Similarly can I used in the server side to poll the connection whether it is active or not?

Further In linux sysctl.conf , there is a configuration file to edit the values. This seems that the whole tcp connection is destroyed after certain inactivity. I am in need such that certain connection form the device are destroyed after certain time inactivity but not the whole tcp port connection closed.

I am using ubuntu to create the server to collect tcp connection. Can I use TCP Keep-Alives in server code to find the inactive client and close the particular client? or is there any other way in server side to implement such feature?

and while going through the web it is mentioned that

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

this getsockopt is for the main tcp connection and setting here seems the setting is for whole connection to the server.

However what I need is for the specific client. I have the event server code as here client_fd is accepted and now I need to close this client_fd if next data through this client is not received within certain time.

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);
    }
}

Upvotes: 3

Views: 1568

Answers (2)

Ravindra babu
Ravindra babu

Reputation: 38910

I have implemented below mechanism to detect idle status on Socket IO activity.

My Socket is wrapped in some class like UserConnection. This class has one more attribute lastActivtyTime. Whenever I get a read on write on this Socket, I will update this attribute.

I have one more background Reaper thread, which will iterate through all UserConnection objects and check for lastActivtyTime. If current time - lastActivtyTime is greater than configured threshold parameter like 15 seconds, I will close the idle connection.

In your case, when you are iterating through all UserConnections, you can check client_id and your threshold of 30 minutes inactivity to close idle connection.

Upvotes: -1

Steffen Ullrich
Steffen Ullrich

Reputation: 123320

TCP keep alives are not to detect idle clients but to detect dead connections, i.e. if a client crashed without closing the connection or if the line is dead etc. But if the client is only idle but not dead the connection is still open. Any attempts to send an empty packet (which keep-alive packets are) to the client will result in an ACK from the client and thus keep alive will not report a dead connection.

To detect idle clients instead use either timeouts for read (SO_RCVTIMEO) or use a timeout with select, poll or similar functions.

Upvotes: 2

Related Questions