码迷,mamicode.com
首页 > 系统相关 > 详细

《Linux高性能服务器编程》学习总结(十一)——定时器

时间:2018-02-13 15:39:58      阅读:335      评论:0      收藏:0      [点我收藏+]

标签:bytes   好的   client   case   send   limit   eve   head   city   

第十一章 定时器

  这里的定时器主要指的是定时器容器,TCP连接中有保活定时器,为了定期查看TCP连接是否断开,可以用socket选项实现,但是较为麻烦,所以一般都由应用层负责保活,这是定时器的一个运用场景,或者在应用层需要执行一些定时操作,这样就需要一个高效的定时器容器,主要是时间轮和时间堆,当然定时器也可以用SIGALRM信号以及I/O复用实现。

  socket选项中我们使用SO_RCVTIMEO和SO_SNDTIMEO两个选项进行设置,我们来看一个例子:

 1 /*************************************************************************
 2     > File Name: 11-1.cpp
 3     > Author: Torrance_ZHANG
 4     > Mail: 597156711@qq.com
 5     > Created Time: Sat 10 Feb 2018 11:13:03 PM PST
 6  ************************************************************************/
 7 
 8 #include"head.h"
 9 using namespace std;
10 
11 int timeout_connect(const char* ip, int port, int time) {
12     int ret = 0;
13     struct sockaddr_in address;
14     bzero(&address, sizeof(address));
15     address.sin_family = AF_INET;
16     inet_pton(AF_INET, ip, &address.sin_addr);
17     address.sin_port = htons(port);
18 
19     int sockfd = socket(AF_INET, SOCK_STREAM, 0);
20     assert(sockfd >= 0);
21 
22     struct timeval timeout;
23     timeout.tv_sec = time;
24     timeout.tv_usec = 0;
25     socklen_t len = sizeof(timeout);
26     ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
27     assert(ret != -1);
28 
29     ret = connect(sockfd, (struct sockaddr*)&address, sizeof(address));
30     if(ret == -1) {
31         if(errno == EINPROGRESS) {
32             printf("connection timeout, process timeout logic \n");
33             return -1;
34         }
35         printf("error occur when connection to server\n");
36         return -1;
37     }
38     return sockfd;
39 }
40 
41 int main(int argc, char** argv) {
42     if(argc <= 2) {
43         printf("usage: %s ip_address port_number\n", basename(argv[0]));
44         return 1;
45     }
46     const char* ip = argv[1];
47     int port = atoi(argv[2]);
48 
49     int sockfd = timeout_connect(ip, port, 10);
50     if(sockfd < 0) return 1;
51     else return 0;
52 }

技术分享图片

  我们用5-7的服务器,将监听队列改成1,用两个telnet客户端占用监听队列,这样再运行这个客户端后,等待10s出现超时的结果。

  关于SIGALRM信号,我们知道他是当定时器到时时系统给应用程序的通知,所以基于这个信号,我们设计一个基于升序链表的定时器结构,为了引用方便,我们写在头文件中,其按照超时时间升序排序:

  1 /*************************************************************************
  2     > File Name: lst_timer.h
  3     > Author: Torrance_ZHANG
  4     > Mail: 597156711@qq.com
  5     > Created Time: Sat 10 Feb 2018 11:39:56 PM PST
  6  ************************************************************************/
  7 
  8 #ifndef _LST_TIMER_H
  9 #define _LST_TIMER_H
 10 
 11 #include<time.h>
 12 #define BUFFER_SIZE 64
 13 class util_timer;
 14 
 15 struct client_data {
 16     sockaddr_in address;
 17     int sockfd;
 18     char buf[BUFFER_SIZE];
 19     util_timer* timer;
 20 };
 21 
 22 class util_timer {
 23 public:
 24     util_timer() : prev(NULL), next(NULL) {}
 25 public:
 26     time_t expire;
 27     void (*cb_func)(client_data*);
 28     client_data* user_data;
 29     util_timer* prev;
 30     util_timer* next;
 31 };
 32 
 33 class sort_timer_lst {
 34 public:
 35     sort_timer_lst() : head(NULL), tail(NULL) {}
 36     ~sort_timer_lst() {
 37         util_timer* tmp = head;
 38         while(tmp) {
 39             head = tmp -> next;
 40             delete tmp;
 41             tmp = head;
 42         }
 43     }
 44     void add_timer(util_timer* timer) {
 45         if(!timer) return;
 46         if(!head) {
 47             head = tail = timer;
 48             return;
 49         }
 50         if(timer -> expire < head -> expire) {
 51             timer -> next = head;
 52             head -> prev = timer;
 53             head = timer;
 54             return;
 55         }
 56         add_timer(timer, head);
 57     }
 58     void adjust_timer(util_timer* timer) {
 59         if(!timer) return;
 60         util_timer* tmp = timer -> next;
 61         if(!tmp || (timer -> expire < tmp -> expire)) return;
 62         if(timer == head) {
 63             head = head -> next;
 64             head -> prev = NULL;
 65             timer -> next = NULL;
 66             add_timer(timer, head);
 67         }
 68         else {
 69             timer -> prev -> next = timer -> next;
 70             timer -> next -> prev = timer -> prev;
 71             add_timer(timer, timer -> next);
 72         }
 73     }
 74     void del_timer(util_timer* timer) {
 75         if(!timer) return;
 76         if((timer == head) && (timer == tail)) {
 77             delete timer;
 78             head = NULL;
 79             tail = NULL;
 80             return;
 81         }
 82         if(timer == head) {
 83             head = head -> next;
 84             head -> prev = NULL;
 85             delete timer;
 86             return;
 87         }
 88         if(timer == tail) {
 89             tail = tail -> prev;
 90             tail -> next = NULL;
 91             delete timer;
 92             return;
 93         }
 94         timer -> prev -> next = timer -> next;
 95         timer -> next -> prev = timer -> prev;
 96         delete timer;
 97     }
 98     void tick() {
 99         if(!head) return;
100         printf("timer tick\n");
101         time_t cur = time(NULL);
102         util_timer* tmp = head;
103         while(tmp) {
104             if(cur < tmp -> expire) break;
105             tmp -> cb_func(tmp -> user_data);
106             head = tmp -> next;
107             if(head) head -> prev = NULL;
108             delete tmp;
109             tmp = head;
110         }
111     }
112 private:
113     void add_timer(util_timer* timer, util_timer* lst_head) {
114         util_timer* prev = lst_head;
115         util_timer* tmp = prev -> next;
116         while(tmp) {
117             if(timer -> expire < tmp -> expire) {
118                 prev -> next = timer;
119                 timer -> next = tmp;
120                 tmp -> prev = timer;
121                 timer -> prev = prev;
122                 break;
123             }
124             prev = tmp;
125             tmp = tmp -> next;
126         }
127         if(!tmp) {
128             prev -> next = timer;
129             timer -> prev = prev;
130             timer -> next = NULL;
131             tail = timer;
132         }
133     }
134 private:
135     util_timer* head;
136     util_timer* tail;
137 };
138 
139 #endif

  接下来我们看一下其实际应用——处理非活动的连接,在服务器运行时,对于很多已经建立的TCP连接并不是时时刻刻都有数据发送,所以我们对于每个TCP连接都要有一个定时器,一旦其没有数据的状态持续一定的时间就可以认为这是非活动的连接并且将之关闭,目的是节省服务器的资源:

  1 /*************************************************************************
  2     > File Name: 11-3.cpp
  3     > Author: Torrance_ZHANG
  4     > Mail: 597156711@qq.com
  5     > Created Time: Sun 11 Feb 2018 04:23:50 AM PST
  6  ************************************************************************/
  7 
  8 #include"head.h"
  9 #include"lst_timer.h"
 10 using namespace std;
 11 
 12 #define FD_LIMIT 65535
 13 #define MAX_EVENT_NUMBER 1024
 14 #define TIMESLOT 5
 15 
 16 static int pipefd[2];
 17 static sort_timer_lst timer_lst;
 18 static int epollfd = 0;
 19 
 20 int setnonblocking(int fd) {
 21     int old_option = fcntl(fd, F_GETFL);
 22     int new_option = old_option | O_NONBLOCK;
 23     fcntl(fd, F_SETFL, new_option);
 24     return old_option;
 25 }
 26 
 27 void addfd(int epollfd, int fd) {
 28     epoll_event event;
 29     event.data.fd = fd;
 30     event.events = EPOLLIN | EPOLLET;
 31     epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
 32     setnonblocking(fd);
 33 }
 34 
 35 void sig_handler(int sig) {
 36     int save_errno = errno;
 37     int msg = sig;
 38     send(pipefd[1], (char*)&msg, 1, 0);
 39     errno = save_errno;
 40 }
 41 
 42 void addsig(int sig) {
 43     struct sigaction sa;
 44     memset(&sa, 0, sizeof(sa));
 45     sa.sa_handler = sig_handler;
 46     sa.sa_flags |= SA_RESTART;
 47     sigfillset(&sa.sa_mask);
 48     assert(sigaction(sig, &sa, NULL) != -1);
 49 }
 50 
 51 void timer_handler() {
 52     timer_lst.tick();
 53     alarm(TIMESLOT);
 54 }
 55 
 56 void cb_func(client_data* user_data) {
 57     epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data -> sockfd, 0);
 58     assert(user_data);
 59     close(user_data -> sockfd);
 60     printf("close fd %d\n", user_data -> sockfd);
 61 }
 62 
 63 int main(int argc, char** argv) {
 64     if(argc <= 2) {
 65         printf("usage: %s ip_address port_number\n", basename(argv[0]));
 66         return 1;
 67     }
 68     const char* ip = argv[1];
 69     int port = atoi(argv[2]);
 70 
 71     int ret = 0;
 72     struct sockaddr_in address;
 73     bzero(&address, sizeof(address));
 74     address.sin_family = AF_INET;
 75     inet_pton(AF_INET, ip, &address.sin_addr);
 76     address.sin_port = htons(port);
 77 
 78     int listenfd = socket(AF_INET, SOCK_STREAM, 0);
 79     assert(listenfd >= 0);
 80 
 81     ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
 82     if(ret == -1) {
 83         printf("errno is %d\n", errno);
 84         return 1;
 85     }
 86     ret = listen(listenfd, 5);
 87     assert(ret != -1);
 88 
 89     epoll_event events[MAX_EVENT_NUMBER];
 90     int epollfd = epoll_create(5);
 91     assert(epollfd != -1);
 92     addfd(epollfd, listenfd);
 93 
 94     ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
 95     assert(ret != -1);
 96     setnonblocking(pipefd[1]);
 97     addfd(epollfd, pipefd[0]);
 98     
 99     addsig(SIGALRM);
100     addsig(SIGTERM);
101     bool stop_server = false;
102     client_data* users = new client_data[FD_LIMIT];
103     bool timeout = false;
104     alarm(TIMESLOT);
105 
106     while(!stop_server) {
107         int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
108         if((number < 0) && (errno != EINTR)) {
109             printf("epoll failure\n");
110             break;
111         }
112         for(int i = 0; i < number; i ++) {
113             int sockfd = events[i].data.fd;
114             if(sockfd == listenfd) {
115                 struct sockaddr_in client_address;
116                 socklen_t client_addrlength = sizeof(address);
117                 int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
118                 addfd(epollfd, connfd);
119                 users[connfd].address = client_address;
120                 users[connfd].sockfd = connfd;
121 
122                 util_timer* timer = new util_timer;
123                 timer -> user_data = &users[connfd];
124                 timer -> cb_func = cb_func;
125                 time_t cur = time(NULL);
126                 timer -> expire = cur + 3 * TIMESLOT;
127                 users[connfd].timer = timer;
128                 timer_lst.add_timer(timer);
129             }
130             else if((sockfd == pipefd[0]) && (events[i].events & EPOLLIN)) {
131                 int sig;
132                 char signals[1024];
133                 ret = recv(pipefd[0], signals, sizeof(signals), 0);
134                 if(ret == -1) continue;
135                 else if(ret == 0) continue;
136                 else {
137                     for(int i = 0; i < ret; i ++) {
138                         switch(signals[i]) {
139                             case SIGALRM: {
140                                 timeout = true;
141                                 break;
142                             }
143                             case SIGTERM: {
144                                 stop_server = true;
145                             }
146                         }
147                     }
148                 }
149             }
150             else if(events[i].events & EPOLLIN) {
151                 memset(users[sockfd].buf, 0, BUFFER_SIZE);
152                 ret = recv(sockfd, users[sockfd].buf, BUFFER_SIZE, 0);
153                 printf("get %d bytes of client data %s from %d\n", ret, users[sockfd].buf, sockfd);
154 
155                 util_timer* timer = users[sockfd].timer;
156                 if(ret < 0) {
157                     if(errno != EAGAIN) {
158                         cb_func(&users[sockfd]);
159                         if(timer) {
160                             timer_lst.del_timer(timer);
161                         }
162                     }
163                 }
164                 else if(ret == 0) {
165                     cb_func(&users[sockfd]);
166                     if(timer) {
167                         timer_lst.del_timer(timer);
168                     }
169                 }
170                 else {
171                     if(timer) {
172                         time_t cur = time(NULL);
173                         timer -> expire = cur + 3 * TIMESLOT;
174                         printf("adjust timer once\n");
175                         timer_lst.adjust_timer(timer);
176                     }
177                 }
178             }
179             else {}
180         }
181         if(timeout) {
182             timer_handler();
183             timeout = false;
184         }
185     }
186     close(listenfd);
187     close(pipefd[1]);
188     close(pipefd[0]);
189     delete [] users;
190     return 0;
191 }

技术分享图片

  我们可以看到,客户端连接一段时间后没有数据发送,服务器端就自动关闭连接,以节省服务器资源。

  前面提到I/O复用系统调用的最后一个timeval参数时说过这个是用来控制超时时间的,所以我们也可以用这个参数作为定时,但是我们需要不断更新这个定时参数,用一个代码来说明一下:

 1 /*************************************************************************
 2     > File Name: 11-4.cpp
 3     > Author: Torrance_ZHANG
 4     > Mail: 597156711@qq.com
 5     > Created Time: Mon 12 Feb 2018 01:49:52 AM PST
 6  ************************************************************************/
 7 
 8 #include<iostream>
 9 using namespace std;
10 
11 #define TIMEOUT 5000
12 
13 int main() {
14     time_t start = time(NULL);
15     time_t end = time(NULL);
16     int timeout = TIMEOUT;
17 
18     while(1) {
19         printf("the timeout is now %d mil-seconds\n", timeout);
20         start = time(NULL);
21         int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, timeout);
22         if((number < ) && (errno != EINTR)) {
23             printf("epoll failure\n");
24             break;
25         }
26         if(number == 0) {
27             timeout = TIMEOUT;
28             continue;
29         }
30         end = time(NULL);
31         timeout -= (end - start) * 1000;
32         if(timeout <= 0) timeout = TIMEOUT;
33     }
34 }

  前面我们提到的基于升序链表的定时器容器存在着一个问题,就是添加和删除定时器的效率偏低,当定时器很多的时候,每次添加都要从头开始遍历寻找应该插入的位置,所以我们用了一种新的定时器容器——时间轮就很好地解决了这个问题。

  什么是时间轮,这个名字很形象,时间轮就像是一个轮子,我们虚拟一个指针指向轮子上的每一个槽,这个指针每隔一个时间间隔就走到下一个槽,这个时间间隔就是心搏时间,也是时间轮的槽时间si,而每一个槽其实就是一个指向一条定时器链表的头指针,每条链表上的定时器的定时时间相差N*si的整数倍,这个思想和哈希的思想很像。

  1 /*************************************************************************
  2     > File Name: time_wheel_timer.h
  3     > Author: Torrance_ZHANG
  4     > Mail: 597156711@qq.com
  5     > Created Time: Mon 12 Feb 2018 05:13:13 AM PST
  6  ************************************************************************/
  7 
  8 #ifndef _TIME_WHEEL_TIMER_H
  9 #define _TIME_WHEEL_TIMER_H
 10 
 11 #include<time.h>
 12 #include<netinet/in.h>
 13 #include<stdio.h>
 14 
 15 #define BUFFER_SIZE 64
 16 class tw_timer;
 17 
 18 struct client_data {
 19     sockaddr_in address;
 20     int sockfd;
 21     char buf[BUFFER_SIZE];
 22     tw_timer* timer;
 23 };
 24 
 25 class tw_timer {
 26 public:
 27     tw_timer(int rot, int ts) : next(NULL), prev(NULL), rotation(rot), time_slot(ts) {}
 28 public:
 29     int rotation;
 30     int time_slot;
 31     void (*cb_func)(client_data*);
 32     client_data* user_data;
 33     tw_timer* next;
 34     tw_timer* prev;
 35 };
 36 
 37 class time_wheel {
 38 public:
 39     time_wheel() : cur_slot(0) {
 40         for(int i = 0; i < N; ++ i) {
 41             slots[i] = NULL;
 42         }
 43     }
 44     ~time_wheel() {
 45         for(int i = 0; i < N; i ++) {
 46             tw_timer* tmp = slots[i];
 47             while(tmp) {
 48                 slots[i] = tmp -> next;
 49                 delete tmp;
 50                 tmp = slots[i];
 51             }
 52         }
 53     }
 54     tw_timer* add_timer(int timeout) {
 55         if(timeout < 0) return NULL;
 56         int ticks = 0;
 57         if(timeout < SI) {
 58             ticks = 1;
 59         }
 60         else ticks = timeout / SI;
 61         int rotation = ticks / N;
 62         int ts = (cur_slot + (ticks % N)) % N;
 63         tw_timer* timer = new tw_timer(rotation, ts);
 64         if(!slots[ts]) {
 65             printf("add timer, rotation is %d, ts is %d, cur_slot is %d\n", rotation, ts, cur_slot);
 66             slots[ts] = timer;
 67         }
 68         else {
 69             timer -> next = slots[ts];
 70             slots[ts] -> prev = timer;
 71             slots[ts] = timer;
 72         }
 73         return timer;
 74     }
 75     void del_timer(tw_timer* timer) {
 76         if(!timer) return;
 77         int ts = timer -> time_slot;
 78         if(timer == slots[ts]) {
 79             slots[ts] = slots[ts] -> next;
 80             if(slots[ts]) {
 81                 slots[ts] -> prev = NULL;
 82             }
 83             delete timer;
 84         }
 85         else {
 86             timer -> prev -> next = timer -> next;
 87             if(timer -> next) {
 88                 timer -> next -> prev = timer -> prev;
 89             }
 90             delete timer;
 91         }
 92     }
 93     void tick() {
 94         tw_timer* tmp = slots[cur_slot];
 95         printf("current slot is %d\n", cur_slot);
 96         while(tmp) {
 97             printf("tick the timer once\n");
 98             if(tmp -> rotation > 0) {
 99                 tmp -> rotation --;
100                 tmp = tmp -> next;
101             }
102             else {
103                 tmp -> cb_func(tmp -> user_data);
104                 if(tmp == slots[cur_slot]) {
105                     printf("delete header in cur_slot\n");
106                     slots[cur_slot] = tmp -> next;
107                     delete tmp;
108                     if(slots[cur_slot]) {
109                         slots[cur_slot] -> prev = NULL;
110                     }
111                     tmp = slots[cur_slot];
112                 }
113                 else {
114                     tmp -> prev -> next = tmp -> next;
115                     if(tmp -> next) {
116                         tmp -> next -> prev = tmp -> prev;
117                     }
118                     tw_timer* tmp2 = tmp -> next;
119                     delete tmp;
120                     tmp = tmp2;
121                 }
122             }
123         }
124         cur_slot = ++cur_slot % N;
125     }
126 private:
127     static const int N = 60;
128     static const int SI = 1;
129     tw_timer* slots[N];
130     int cur_slot;
131 };
132 
133 #endif

  我们来将11-3的程序修改一下,使用时间轮来进行定时:

  1 /*************************************************************************
  2     > File Name: 11-3.cpp
  3     > Author: Torrance_ZHANG
  4     > Mail: 597156711@qq.com
  5     > Created Time: Sun 11 Feb 2018 04:23:50 AM PST
  6  ************************************************************************/
  7 
  8 #include"head.h"
  9 #include"time_wheel_timer.h"
 10 using namespace std;
 11 
 12 #define FD_LIMIT 65535
 13 #define MAX_EVENT_NUMBER 1024
 14 #define TIMESLOT 1
 15 
 16 static int pipefd[2];
 17 static time_wheel t_wheel;
 18 static int epollfd = 0;
 19 
 20 int setnonblocking(int fd) {
 21     int old_option = fcntl(fd, F_GETFL);
 22     int new_option = old_option | O_NONBLOCK;
 23     fcntl(fd, F_SETFL, new_option);
 24     return old_option;
 25 }
 26 
 27 void addfd(int epollfd, int fd) {
 28     epoll_event event;
 29     event.data.fd = fd;
 30     event.events = EPOLLIN | EPOLLET;
 31     epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
 32     setnonblocking(fd);
 33 }
 34 
 35 void sig_handler(int sig) {
 36     int save_errno = errno;
 37     int msg = sig;
 38     send(pipefd[1], (char*)&msg, 1, 0);
 39     errno = save_errno;
 40 }
 41 
 42 void addsig(int sig) {
 43     struct sigaction sa;
 44     memset(&sa, 0, sizeof(sa));
 45     sa.sa_handler = sig_handler;
 46     sa.sa_flags |= SA_RESTART;
 47     sigfillset(&sa.sa_mask);
 48     assert(sigaction(sig, &sa, NULL) != -1);
 49 }
 50 
 51 void timer_handler() {
 52     t_wheel.tick();
 53     alarm(TIMESLOT);
 54 }
 55 
 56 void cb_func(client_data* user_data) {
 57     epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data -> sockfd, 0);
 58     assert(user_data);
 59     close(user_data -> sockfd);
 60     printf("close fd %d\n", user_data -> sockfd);
 61 }
 62 
 63 int main(int argc, char** argv) {
 64     if(argc <= 2) {
 65         printf("usage: %s ip_address port_number\n", basename(argv[0]));
 66         return 1;
 67     }
 68     const char* ip = argv[1];
 69     int port = atoi(argv[2]);
 70 
 71     int ret = 0;
 72     struct sockaddr_in address;
 73     bzero(&address, sizeof(address));
 74     address.sin_family = AF_INET;
 75     inet_pton(AF_INET, ip, &address.sin_addr);
 76     address.sin_port = htons(port);
 77 
 78     int listenfd = socket(AF_INET, SOCK_STREAM, 0);
 79     assert(listenfd >= 0);
 80 
 81     ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
 82     if(ret == -1) {
 83         printf("errno is %d\n", errno);
 84         return 1;
 85     }
 86     ret = listen(listenfd, 5);
 87     assert(ret != -1);
 88 
 89     epoll_event events[MAX_EVENT_NUMBER];
 90     int epollfd = epoll_create(5);
 91     assert(epollfd != -1);
 92     addfd(epollfd, listenfd);
 93 
 94     ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
 95     assert(ret != -1);
 96     setnonblocking(pipefd[1]);
 97     addfd(epollfd, pipefd[0]);
 98     
 99     addsig(SIGALRM);
100     addsig(SIGTERM);
101     bool stop_server = false;
102     client_data* users = new client_data[FD_LIMIT];
103     bool timeout = false;
104     alarm(TIMESLOT);
105 
106     while(!stop_server) {
107         int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
108         if((number < 0) && (errno != EINTR)) {
109             printf("epoll failure\n");
110             break;
111         }
112         for(int i = 0; i < number; i ++) {
113             int sockfd = events[i].data.fd;
114             if(sockfd == listenfd) {
115                 struct sockaddr_in client_address;
116                 socklen_t client_addrlength = sizeof(address);
117                 int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
118                 addfd(epollfd, connfd);
119                 users[connfd].address = client_address;
120                 users[connfd].sockfd = connfd;
121 
122                 tw_timer* new_timer = t_wheel.add_timer(15 * TIMESLOT);
123                 new_timer -> user_data = &users[connfd];
124                 new_timer -> cb_func = cb_func;
125                 users[connfd].timer = new_timer;
126             }
127             else if((sockfd == pipefd[0]) && (events[i].events & EPOLLIN)) {
128                 int sig;
129                 char signals[1024];
130                 ret = recv(pipefd[0], signals, sizeof(signals), 0);
131                 if(ret == -1) continue;
132                 else if(ret == 0) continue;
133                 else {
134                     for(int i = 0; i < ret; i ++) {
135                         switch(signals[i]) {
136                             case SIGALRM: {
137                                 timeout = true;
138                                 break;
139                             }
140                             case SIGTERM: {
141                                 stop_server = true;
142                             }
143                         }
144                     }
145                 }
146             }
147             else if(events[i].events & EPOLLIN) {
148                 memset(users[sockfd].buf, 0, BUFFER_SIZE);
149                 ret = recv(sockfd, users[sockfd].buf, BUFFER_SIZE, 0);
150                 printf("get %d bytes of client data %s from %d\n", ret, users[sockfd].buf, sockfd);
151 
152                 tw_timer* timer = users[sockfd].timer;
153                 if(ret < 0) {
154                     if(errno != EAGAIN) {
155                         cb_func(&users[sockfd]);
156                         if(timer) {
157                             t_wheel.del_timer(timer);
158                         }
159                     }
160                 }
161                 else if(ret == 0) {
162                     cb_func(&users[sockfd]);
163                     if(timer) {
164                         t_wheel.del_timer(timer);
165                     }
166                 }
167                 else {
168                     if(timer) {
169                         t_wheel.del_timer(timer);
170                         tw_timer* new_timer = t_wheel.add_timer(15 * TIMESLOT);
171                         new_timer -> user_data = &users[sockfd];
172                         new_timer -> cb_func = cb_func;
173                         users[sockfd].timer = new_timer;
174                     }
175                 }
176             }
177             else {}
178         }
179         if(timeout) {
180             timer_handler();
181             timeout = false;
182         }
183     }
184     close(listenfd);
185     close(pipefd[1]);
186     close(pipefd[0]);
187     delete [] users;
188     return 0;
189 }

技术分享图片

  时间轮每秒转动一格,当15s没有消息发送的时候就会自动断开连接。对于时间轮而言,添加和删除一个定时器的时间复杂度是O(1),执行一个定时器的时间复杂度表面上看是O(n),但是由于我们将所有定时器散列到不同的链表中,所以每条链表上的定时器其实很少,当时用多个时间轮的时候其复杂度可以降至接近O(1),所以其效率是很高的。

  对于时间堆,有数据结构基础的话其实很好理解,就是一个基于时间排序的小根堆,每一次添加操作可以用O(lgn)的复杂度完成,而执行和删除堆顶的操作复杂度为O(1),只是删除后需要将堆进行调整,其复杂度也约为O(lgn)。整体来看时间堆的复杂度也是很好的,我们来看一下代码:

  1 /*************************************************************************
  2     > File Name: min_heap.h
  3     > Author: Torrance_ZHANG
  4     > Mail: 597156711@qq.com
  5     > Created Time: Mon 12 Feb 2018 09:31:19 PM PST
  6  ************************************************************************/
  7 
  8 #ifndef _MIN_HEAP_H
  9 #define _MIN_HEAP_H
 10 
 11 #include<iostream>
 12 #include<netinet/in.h>
 13 #include<time.h>
 14 
 15 using std::exception;
 16 
 17 #define BUFFER_SIZE 64
 18 class heap_timer;
 19 
 20 struct client_data {
 21     sockaddr_in address;
 22     int sockfd;
 23     char buf[BUFFER_SIZE];
 24     heap_timer* timer;
 25 };
 26 
 27 class heap_timer {
 28 public:
 29     heap_timer(int delay) {
 30         expire = time(NULL) + delay;
 31     }
 32 public:
 33     time_t expire;
 34     void (*cb_func)(client_data*);
 35     client_data* user_data;
 36 };
 37 
 38 class time_heap {
 39 public:
 40     time_heap(int cap) throw (std::exception) : capacity(cap), cur_size(0) {
 41         array = new heap_timer* [capacity];
 42         if(!array) throw std::exception();
 43         for(int i = 0; i < capacity; i ++) {
 44             array[i] = NULL;
 45         }
 46     }
 47     time_heap(heap_timer** init_array, int size, int capacity) throw (std::exception) : cur_size(size), capacity(capacity) {
 48         if(capacity < size) throw std::exception();
 49         array = new heap_timer* [capacity];
 50         if(!array) throw std::exception();
 51         for(int i = 0; i < capacity; i ++) {
 52             array[i] = NULL;
 53         }
 54         if(size != 0) {
 55             for(int i = 0; i < size; i ++) {
 56                 array[i] = init_array[i];
 57             }
 58             for(int i = (cur_size - 1) / 2; i >= 0; -- i) {
 59                 percolate_down(i);
 60             }
 61         }
 62     }
 63     ~time_heap() {
 64         for(int i = 0; i < cur_size; i ++) {
 65             delete array[i];
 66         }
 67         delete [] array;
 68     }
 69 public:
 70     void add_timer(heap_timer* timer) throw (std::exception) {
 71         if(!timer) return;
 72         if(cur_size >= capacity) resize();
 73         int hole = cur_size ++;
 74         int parent = 0;
 75         for(; hole > 0; hole = parent) {
 76             parent = (hole - 1) / 2;
 77             if(array[parent] -> expire <= timer -> expire) break;
 78             array[hole] = array[parent];
 79         }
 80         array[hole] = timer;
 81     }
 82     void del_timer(heap_timer* timer) {
 83         if(!timer) return;
 84         //延迟销毁,这样可以节省删除定时器的开销,但是会使堆数组膨胀,空间换时间
 85         timer -> cb_func = NULL;
 86     }
 87     heap_timer* top() const {
 88         if(empty()) return NULL;
 89         return array[0];
 90     }
 91     void pop_timer() {
 92         if(empty()) return;
 93         if(array[0]) {
 94             delete array[0];
 95             array[0] = array[-- cur_size];
 96             percolate_down(0);
 97         }
 98     }
 99     void tick() {
100         heap_timer* tmp = array[0];
101         time_t cur = time(NULL);
102         while(!empty()) {
103             if(!tmp) break;
104             if(tmp -> expire > cur) break;
105             if(array[0] -> cb_func) array[0] -> cb_func(array[0] -> user_data);
106             pop_timer();
107             tmp = array[0];
108         }
109     }
110     bool empty() const {
111         return cur_size == 0;
112     }
113 private:
114     void percolate_down(int hole) {
115         heap_timer* temp = array[hole];
116         int child = 0;
117         for(; ((hole * 2 + 1) <= (cur_size - 1)); hole = child) {
118             child = hole * 2 + 1;
119             if((child < (cur_size - 1)) && (array[child + 1] -> expire < array[child] -> expire)) ++child;
120             if(array[child] -> expire < temp -> expire) array[hole] = array[child];
121             else break;
122         }
123         array[hole] = temp;
124     }
125     void resize() throw (std::exception) {
126         heap_timer** temp = new heap_timer* [2 * capacity];
127         for(int i = 0; i < 2 * capacity; i ++) {
128             temp[i] = NULL;
129         }
130         if(!temp) {
131             throw std::exception();
132         }
133         capacity = 2 * capacity;
134         for(int i = 0; i < cur_size; i ++) {
135             temp[i] = array[i];
136         }
137         delete [] array;
138         array = temp;
139     }
140 private:
141     heap_timer** array;
142     int capacity;
143     int cur_size;
144 };
145 
146 #endif

 

《Linux高性能服务器编程》学习总结(十一)——定时器

标签:bytes   好的   client   case   send   limit   eve   head   city   

原文地址:https://www.cnblogs.com/Torrance/p/8446606.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!