Thursday, 17 January 2019

Analysis select timeout, read fd not disturb timeout if tv.sec is not disturbed

SYNOPSIS

       /* According to POSIX.1-2001 */
       #include <sys/select.h>
       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>
       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

Timeout working flow:

  • The select last argument is timeout it receive a struct timeval with second & microsect.
  • The select return is based on below value:
    •  return 0 if timeout happen
    •  return number of FD read if one or multiple FD is ready
    •  return -1 if an error is occured and errno is set.
  • If select is waiting for both read fd & timeout and read fd is ready then select will update the remaining time to the timeout variable.
  • So that next time user should pass the same timeout variable without modify, it will continue the time when it stoped instead of starting fresh.
  • Hence select timeout will be continue paralley, ready FD won't affect the tiemout.

Example:

  • Select is waiting for read fd & timeout for 300 second.
  • 100th second read fd is ready then select return 1 as 1 fd  is ready and remaining second 200 is updated in the timeout variable.
  • After process the read fd call the select timeout without modifiy the remaining timeout.
  • Now select continue the remaining timeout 200 second.
  • 200th second select will be timeout and return 0.
  • Hence Timeout = 300 + time taken by read fd (if few millisecond then its ok).

Note: 

  • The select update the remaining time in the timeout value and start the time after process the FD.
  • The time taken by read FD process is not calculate in  the select timeout, suppose if we use alarm then alarm will run simultaneously and should not face this issue.
  • Suppose read FD is taking more then 1 second and more time then select timeout for 300 second won't be timeout exacatly
  • EG: Timeout = 300 + time taken by read fd (if fd is receiving continuously then problem in timeout).

Server program:

/* Analysis select timeout by Velraj.K
 * Check : http://velrajcoding.blogspot.in
 */
#include "stdio.h"
#include "unistd.h"
#include "signal.h"
#include "sys/socket.h"
#include "sys/select.h"
#include "sys/un.h"
#include "string.h"
#include "fcntl.h"
#include <time.h>   /* For localtime */
#define SELECT_TIMEOUT 300
#define TIMESTAMP_BUF_LEN   25
#define TIMESTAMP_FORMAT    "%a %b %d %H:%M:%S %Y"
int get_time_string(char *str_time, unsigned int length, char *time_format)
{
    time_t    time_sec = 0;
    struct tm *time_local = NULL;
    size_t    bytes = 0;
    if ((str_time == NULL) || (time_format == NULL)) {
        printf("Error: The joined time or time format is NULL");
        return 1;
    }
    /* Get value of time in seconds */
    time_sec = time(NULL);
    if (time_sec == -1) {
        printf("Error: Failed to get time in seconds");
        return 2;
    }
    /* Convert a time value to a broken-down local time */
    time_local = localtime(&time_sec);
    if (time_local == NULL) {
        printf("Error: Failed to convert the time value into a broken-down local time");
        return 3;
    }
    /* Format date and time */
    bytes = strftime(str_time, length, time_format, time_local);
    if (bytes == 0) {
        printf("Error: Error in format the data and time");
        return 4;
    }

    return 0;
}
int main() {
    int sock, ret;
    struct sockaddr_un servaddr;
    char buf [20];
    fd_set read_fd_set;
    int fromlen;
    long socketFlags = 0;
    struct timeval timeout;
    char   str_cur_time[TIMESTAMP_BUF_LEN] = {'\0'};
    timeout.tv_sec = SELECT_TIMEOUT;
    timeout.tv_usec = 0;
    // Create Unix domain socket to communicate with client
    if ((sock = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0) {
        perror ("socket failed to open");
        return 1;
    }
    /* Forcefully rebind if already bind and still active.
     * Suppose if already bind and kill the process using ctrl + c, then
     * bind value won't be released by the process. Rebuind will throw error
     * To avoid this error forcefully remove.
     */
    unlink("serversock");
    // Bind socket
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, "serversock");
    if (bind (sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        perror ("socket failed to bind");
        close (sock);
        return 1;
    }
    socketFlags |= O_NONBLOCK;
    if(fcntl(sock, F_SETFL, socketFlags) <0){
        perror ("fcntl failed");
        return 1;
    }
    while (1) {
        FD_ZERO(&read_fd_set);
        FD_SET(sock, &read_fd_set);
        get_time_string(str_cur_time, TIMESTAMP_BUF_LEN, TIMESTAMP_FORMAT);
        printf("\nGoing to select. Will timeout in %d seconds time = %s\r\n", SELECT_TIMEOUT, str_cur_time);
        ret = select (sock + 1, &read_fd_set, NULL, NULL, &timeout);
        printf("Found something\r\n");
        if (ret < 0) { // Error
            perror ("select failed");
            return 1;
        } else if (ret == 0){ // Timeout
            get_time_string(str_cur_time, TIMESTAMP_BUF_LEN, TIMESTAMP_FORMAT);
            printf("Timed out !! current time = %s \r\n", str_cur_time);
            timeout.tv_sec = SELECT_TIMEOUT;
            timeout.tv_usec = 0;
        } else { // Message on FD
           get_time_string(str_cur_time, TIMESTAMP_BUF_LEN, TIMESTAMP_FORMAT);
            printf("FD is read to read so the timeout is tv_sec = %ld tv_usec = %ld time = %s \n", timeout.tv_sec, timeout.tv_usec, str_cur_time);
            if (FD_ISSET(sock, &read_fd_set)){
                printf("FD is set\r\n");
                fromlen = sizeof(servaddr);
                ret = recvfrom(sock, &buf, 20, 0, (struct sockaddr*)&servaddr, (socklen_t*)&fromlen);
                printf("'%s' recieved in buffer of size %d\r\n", buf, ret);
            }
        }
    }
    return 0;
}


Client Program:

/* Analysis select timeout for client by Velraj.K
 * Check : http://velrajcoding.blogspot.in
 */
#include "stdio.h"
#include "unistd.h"
#include "signal.h"
#include "sys/socket.h"
#include "sys/select.h"
#include "sys/un.h"
#include "string.h"
int sock;
struct sockaddr_un servaddr;
char buf [20] = "Hello Server";
void sighandler() {
    int len = -1;
    // Doing all this in a sighandler as a test is maybe alright....
    printf("\nSending 'Hello Server' to server from pid %d..\r\n", getpid());
    len = sendto(sock, &buf, 20, 0, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_un));
    if (len < 0) {
        printf("Error. len = %d\r\n", len);
    }
}

int main() {
    struct sigaction new_action, old_action;
    int len = -1;
    // Setting the signal handler
    // Will send a message to server on SIGUSR1
    new_action.sa_handler = sighandler;
    sigemptyset(&new_action.sa_mask);
    new_action.sa_flags = 0;
    sigaction(SIGUSR1, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN)
        sigaction(SIGUSR1, &new_action, NULL);
    // Create Unix domain socket to communicate with server
    if ((sock = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0) {
        perror ("socket failed to open");
        return 1;
    }
    // Setting server address
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, "serversock");
    while (1) {
        // Do nothing. Just wait for the SIGUSR1 to send a message to server
//    len = sendto(sock, &buf, 20, 0, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_un));
    sleep(1);
    }
    return 0;
}


Output:

Server:
labuser@labuser-virtual-machine:~/velrajk/sample/select$ ./ser
Going to select. Will timeout in 300 seconds time = Thu Jan 17 18:50:18 2019
Found something
FD is read to read so the timeout is tv_sec = 196 tv_usec = 207733 time = Thu Jan 17 18:52:02 2019
FD is set
'Hello Server' recieved in buffer of size 20
Going to select. Will timeout in 300 seconds time = Thu Jan 17 18:52:02 2019
Found something
FD is read to read so the timeout is tv_sec = 120 tv_usec = 832029 time = Thu Jan 17 18:53:17 2019
FD is set
'Hello Server' recieved in buffer of size 20
Going to select. Will timeout in 300 seconds time = Thu Jan 17 18:53:17 2019



Found something
Timed out !! current time = Thu Jan 17 18:55:18 2019
Going to select. Will timeout in 300 seconds time = Thu Jan 17 18:55:18 2019


Client:

labuser@labuser-virtual-machine:~/velrajk/sample/select$ ./a.out
Sending 'Hello Server' to server from pid 11052..
Sending 'Hello Server' to server from pid 11052..

Send signal to client from other console:
ps -aux | grep a.out
kill -s SIGUSR1 <Pid>




No comments:

Post a Comment