随着人们对于网络使用越来越频繁,之前的 客户端请求 -> 服务器处理并返回的 通信模式已经不能满足人们的对于网络的需求了。目前非常常见的一种场景是需要系统给个人推送通知。你想想,当你刷知乎,朋友圈,微博,如果需要你不断刷新你才能知道是否有新的消息是不是觉得很不方便。
这样就需要 服务器主动给客户端推送数据;
出于好奇,我实验了这样几种方法:
实现原理就是在客户端不断地向服务器发起请求,然后服务器去查询是否有数据变化,如果有变化的话把数据返回给客户端。
示例
window.setInterval(function ({
$.get("url",
{"timed": new Date().getTime()},
function (data) {
$("#logs").append("[data: " + data + " ]
");
});
}, 3000);
这样会有什么问题呢? 会给服务器造成大量的额外的开销,因为 每一次请求都需要服务器处理, 在没有数据变化的时候也会给出响应,给服务器造成很大压力。
长轮询就是对轮询的一些改造,服务器不再不管有没有检测到数据变化都要处理。而是,如果没有数据变化,就保持连接状态,知道有返回时,才返回结果。
长轮询示例代码
前端代码
var postAjax = function(){
$.ajax({
url : 'url',
type : 'GET',
success : function(data) {
console.log(data);
postAjax();
}
});
}
//服务端代码
public function getTestData()
{
while (1) {
sleep(5);
$c = rand(1, 100);
if ($c > 90) {
$this->ajaxReturn($c.'time = '.date('Y-m-d H:i:s'));
break;
}
}
}
Server-Sent 详情参照 阮大神的
http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html
这篇博客已经说得很明白了。我的理解是跟长轮询差不多的,也是不断的发请求然后接收。
说一下Server-Sent 和 长轮询的一个问题,就是在服务端挂着的时候,如果有及时响应的另一个请求进来的话,不会立刻返回,会等待轮询的请求结束的时候,排队返回。这是因为 浏览器的 最大并发连接数导致的,所以效率也不会很高。
最后测试还是webSocket比较合适用来做这个东西。不会有最大连接数的问题。
示例代码
前端
var postAjaxJS = function(){
var wsServer = 'ws://192.168.0.119:9501';
var websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) {
console.log("Connected to WebSocket server.");
};
websocket.onclose = function (evt) {
console.log("Disconnected");
};
websocket.onmessage = function (evt) {
console.log('Retrieved data from server: ' + evt.data);
};
websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
};
}
后端 (使用swoole 扩展,swoole 文档有详细用法)
$server = new swoole_websocket_server("0.0.0.0", 9501);
$server->on('open', function (swoole_websocket_server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
while (1) {
sleep(1);
$server->push($request->fd, "this is server:".time());
}
});
$server->on('message', function (swoole_websocket_server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
$server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
$server->start();