c_socket.io_server笔记之长轮询超时(timeout)处理
不吐不快
当你习惯了现有WEB服务器,诸如nginx、apache,JAVA应用服务器Tomcat等,你就不能不注意HTTP请求的响应超时时间,需要小心,尤其是反向代理时。当你可以自由控制请求timeout超时时,那是怎样一个快意。
在libev中使用timeout,没有像java那样封装的完善,一切都很原始,但确实锋利多了。
长轮询
一般长轮询需要定义超时时间,一旦超时,服务器端会主动断开连接。无论是xhr形式的长轮询,还是jsonp长轮询,在服务器端处理没有多大差别,输出数据有异。
输出头部
一般优先输出头部,告诉浏览器,需要保持长连接,当然,这需要浏览器支持http 1.1协议,并且明确的注明当前连接为一直保持着:keep-alive:
strcat(heaer_str, "HTTP/1.1 200 OK\r\n");
strcat(heaer_str, "Content-Type: text/plain; charset=UTF-8\r\n");
strcat(heaer_str, "Connection: keep-alive\r\n");
strcat(heaer_str, "\r\n");
write_msg(client, heaer_str);
定时器启动,等待
连接什么时候关闭,需要在代码中手动控制,除非浏览器端在发出请求等待响应期间出现异常,无故断开了连接。设服务器端设定好连接持续时间为30秒,那么就应该启动一个定时器,除非所使用的语言层面提供了内置支持。
ev_timer_init(&client->timeout, timeout_cb, 30.0, 0); //30s
ev_timer_start(loop, &client->timeout);
定时器start之后,触发的函数timeout_cb:
2 if (EV_ERROR & revents) {
3 fprintf(stderr, "error event in timer_beat\n");
4 return ;
5 }
6
7 if (timer == NULL) {
8 fprintf(stderr, "the timer is NULL now !\n");
9 return;
10 }
11
12 client_t *client = timer->data;
13
14 if (client == NULL) {
15 fprintf(stderr, "Timeout the client is NULL !\n");
16 return;
17 }
18
19 write_msg(client, HTML_RESPONSE_ECHO);
20 free_res(loop, client);
21 }
可以看到,定时器触发之后,本例中将输出一串预先定义好的文字,然后关闭连接。
如何关闭触发器,则很简单:
if (timer != NULL && (timer->data != NULL)) {
ev_timer_stop(loop, timer);
}
编译运行
编译一下:
gcc long polling.c -o longpolling ../include/libev.a ../include/http-parser/http_parser.o -lm
运行它:
./long_polling
然后测试:
curl -i http://192.168.190.150:9000/long_polling
可以先看到头部:
HTTP/1.1 200 OK Content-Type: text/plain; charset=UTF-8 Connection: keep-alive
等到30秒后输出具体的文字内容:
The timeout(30s) had passed, you are welcome ~!
小结
所演示的长轮询,没有什么难度,HTTP 1.1头部输出,定时器启动,然后等待输出。
libev内含的timer组件简单易用,控制方便,但不算是最佳实践,官方文档给出了若干种最佳实践方式。具体可参阅:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code evtimer code relativeandopti
完整代码