SYNOPSIS
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */#include <sys/select.h>
#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.outSending '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>