timeval结构体详解
一、timeval概述
timeval是Linux系统中的一个时间结构体,它由<time.h>
头文件定义。该结构体包含两个字段——秒数和微秒数。它广泛用于Linux系统中的许多接口,例如select、pselect、gettimeofday、settimeofday等。timeval的使用便捷性,以及对时间的高度精确度,使其成为Linux系统中常用的时间结构体之一。
二、timeval数据类型
timeval的定义如下:
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
其中,tv_sec
表示从1970年1月1日0时0分0秒至今所经过的秒数,它是一个time_t
类型的整数;tv_usec
表示微秒数,它是一个suseconds_t
类型的整数。
我们可以通过以下方式对timeval变量进行初始化:
struct timeval tv = {0};
tv.tv_sec = 10; //设置秒数为10
tv.tv_usec = 500000; //设置微秒数为500000
三、timeval的精度
timeval结构体的精度可以达到微秒级别。gettimeofday()函数便是利用timeval来实现高精度的时间获取。 下面是一个使用gettimeofday()函数获取当前时间的示例代码:
struct timeval tv;
gettimeofday(&tv, NULL);
printf("Current time: %ld.%06ld\n", tv.tv_sec, tv.tv_usec);
在以上代码中,我们使用gettimeofday()
函数获取当前时间,将结果存储在tv
结构体中,最后打印输出。
四、timeval在select函数中的应用
select()
函数是Linux系统中用于等待文件描述符状态变化的一个系统调用。该函数的第1个参数nfds
是待检测的最大文件描述符加1;第2个参数readfds
是待读取的文件描述符的集合;第3个参数writefds
是待写入的文件描述符的集合;第4个参数exceptfds
是待检测的异常文件描述符的集合;第5个参数timeout
是超时时间。
timeout参数可以用timeval结构体来表示。它指定select函数等待的最长时间。当timeout为NULL时,select()
函数将一直阻塞,直到有一个待读、待写或异常的文件描述符就绪。当timeout不为NULL且指定的时间到达时,select()
函数将立即返回。
下面是一个使用select函数实现高精度定时器的示例程序:
#include <sys/select.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main() {
fd_set set;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
struct timeval tv = {0};
tv.tv_sec = 5; //设置定时时间为5秒
int ret = select(STDIN_FILENO + 1, &set, NULL, NULL, &tv);
if (ret == -1) {
perror("select");
exit(EXIT_FAILURE);
} else if (ret == 0) {
printf("Timeout occurred!\n"); //超时
} else {
if (FD_ISSET(STDIN_FILENO, &set)) {
printf("Data is available now.\n"); //有数据可读
}
}
return 0;
}
在以上代码中,我们使用select()
函数实现一个高精度定时器。将标准输入的文件描述符添加到文件描述符集合中,设置超时时间为5秒。当超时时间到达时,select()
函数将返回0,表示超时。当有数据可读时,select()
函数将返回1。
五、timeval在网络编程中的应用
timeval结构体在网络编程中也有广泛的应用,它可以用来设置各类超时时间,例如网络I/O操作的超时时间。 下面是一个使用select函数实现socket I/O超时的示例程序:
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockaddr.sin_port = htons(8888);
int ret = connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (ret < 0) {
perror("connect");
exit(EXIT_FAILURE);
}
char buf[1024] = {0};
struct timeval timeout = {0};
timeout.tv_sec = 5; //设置超时时间为5秒
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
while (1) {
ret = recv(sockfd, buf, sizeof(buf), 0); //recv函数默认是阻塞的
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { //超时
printf("Timeout occurred!\n");
continue;
}
perror("recv");
exit(EXIT_FAILURE);
} else if (ret == 0) {
printf("Connection closed.\n"); //连接关闭
break;
} else {
printf("Received %d bytes of data.\n", ret); //接收到数据
buf[ret] = '\0';
printf("%s\n", buf);
}
}
close(sockfd);
return 0;
}
在以上代码中,我们使用setsockopt()
函数将SO_RCVTIMEO选项设置为5秒超时。如果recv函数在5秒内没有读取到数据,recv()
函数将返回-1,此时我们需要检查errno是否为EAGAIN或EWOULDBLOCK,以判断是否超时。如果没有超时,我们可以继续尝试读取数据。
六、总结
timeval结构体在Linux系统编程中扮演着非常重要的角色,它广泛应用于文件描述符状态判断、高精度定时器、网络I/O超时等场景。掌握timeval结构体的使用,有助于我们写出更加高效、可靠的Linux系统程序。