1. 服务端
class WebSocketServer
{
public $server;
public function __construct($controller) {
$this->server = new \swoole_websocket_server("0.0.0.0", 9501);
$this->server->set(array(
'worker_num' => 8,
'daemonize' => false,
// 'heartbeat_check_interval' => 30,
// 'heartbeat_idle_time' => 90,
'heartbeat_check_interval' => 60,
'heartbeat_idle_time' => 600,
));
$this->server->on('WorkerStart', array($controller, 'onWorkerStart'));
$this->server->on('open', array($controller, 'onOpen'));
$this->server->on('message',array($controller, 'onMessage') );
$this->server->on('close',array($controller, 'onClose') );
$this->server->on('request', array($controller, 'onRequest'));
$this->server->on('Timer', array($controller, 'onTimer'));
}
public function start(){
$this->server->start();
}
}
class ServerController extends Controller
{
private $fdKey='swoole_websocket_server_fd';
private $userIdKey='swoole_websocket_server_userId';
const GPS_POINT_KEY = "GPS:GPSPOINT"; // 实时坐标点的标识
public function start()
{
try{
$server = new WebSocketServer($this);
$server->start();
}catch(\Exception $e) {
$this->log("websocket:Caught exception000:".$e->getMessage());
}
}
public function onWorkerStart( $server , $worker_id)
{
// 在Worker进程开启时绑定定时器
echo "onWorkerStart\n";
// 只有当worker_id为0时才添加定时器,避免重复添加
if ($worker_id == 0) {
$server->addtimer(1000);
}
}
public function onTimer($server, $interval) {
switch( $interval ) {
case 1000: { //
echo "Do Thing A at interval 30000\n";
$this->pushData($server);
break;
}
}
}
public function onOpen(\swoole_websocket_server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
try{
$server = $request->server;
$uri = trim($server['request_uri'],'/');
$user = explode('/',$uri);
$userId = $user[1];
$redis = Cache::getInstance();
$fdArr = $redis->get($this->fdKey.$userId);
$fd = $request->fd;
if(!empty($fdArr)){
array_push($fdArr,$fd);
$newFdArr = $fdArr;
}else{
$newFdArr = array($fd);
}
$redis->set($this->fdKey.$userId,$newFdArr);
$redis->set($this->userIdKey.$fd,$userId);
$this->log("websocket:server start, fd:".$request->fd);
}catch(\Exception $e) {
$this->log("websocket:Caught exception000:".$e->getMessage());
}
}
public function onMessage(\swoole_websocket_server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
try{
$data = $frame->data;
if($data=='close'){
$redis = Cache::getInstance();
$key = $this->userIdKey.$frame->fd;
$userId = $redis->get($key);
$redis->rm($key);
$redis->rm($this->fdKey.$userId);
}
}catch(\Exception $e){
$this->log("websocket:Caught exception111:".$e->getMessage());
}
}
public function onClose($ser,$fd) {
echo "client {$fd} closed\n";
$redis = Cache::getInstance();
$redis->rm('swoole_websocket_server_fd1');
$key = $this->userIdKey.$fd;
$userId = $redis->get($key);
$redis->rm($key);
$fdKey = $this->fdKey.$userId; //团队下的fd
$fdArr = $redis->get($fdKey);
if(!empty($fdArr)&&in_array($fd,$fdArr)){
$fd = arrray($fd);
$newFdArr = array_diff($fdArr,$fd);
if(!empty($newFdArr)){
$redis->set($fdKey,$newFdArr);
}else{
$redis->rm($fdKey);
}
}
$this->log("websocket:server close:".date("Y-m-d H:i:s"));
}
public function onRequest($request, $response) {
// 接收http请求从get获取message参数的值,给用户推送
// $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
foreach ($this->server->connections as $fd) {
$this->server->push($fd, $request->get['message']);
}
}
public function pushData($server){
$redis = Cache::getInstance();
$all = $redis->hGetAll(self::GPS_POINT_KEY);
foreach($all as $key=>$val){
if(!empty($val)){
list($lonlat, $gateTime) = explode('|', $val);
list($lon, $lat) = explode(',', $lonlat);
$latitude = "$lat";
$longitude = "$lon";
if($gateTime+60>time()){//60s内的算坐标更新了
if(strlen($key)==11){ //手机
//取得对应在线手机所在的团队id
$where = ['id_card'=>$key,'state'=>1];
}elseif(strlen($key)==15){ //定位神器
//取得对应在线手机所在的团队id
$where = ['device_imei'=>$key,'state'=>1];
}
$deviceData = PointDeviceService::getOnlineDevice($where);
$deviceData['record'] = [
'gate_time'=>date("Y-m-d H:i:s", $gateTime),
'latitude'=>$latitude,
'longitude'=>$longitude,
];
print_r($deviceData);
$jsonData = json_encode($deviceData);
echo $userId = $deviceData['user_id'];//团队ID
$fdArr = $redis->get($this->fdKey.$userId);
echo "aaa:";print_r($fdArr);
if(!empty($fdArr)){
foreach($fdArr as $fd){
$server->push($fd,$jsonData);
}
}
$this->log("websocket:server push data:".$jsonData);
}else{
$this->log("websocket:".$key."设备没有更新的坐标数据");
}
}
}
}
private function log($message=''){
$trueDir = RUNTIME_PATH.'websocketServer'.DIRECTORY_SEPARATOR;
if(!is_dir($trueDir)){
mkdir($trueDir,0777,true);
}
$file = date('Y_m_d').'.log';
$trueFile = $trueDir.$file;
$message = date("Y-m-d H:i:s").":".$message;
file_put_contents($trueFile,$message.PHP_EOL,FILE_APPEND);
}
}
2. 客户端
function wsConnect(){
// var userId = '{userId}';
var wsServer = 'ws://127.0.0.1:9501/user_id/1';
var websocket = new WebSocket(wsServer);
return websocket;
}
//在地图上动态显示所有在线设备 add by mzc 2017.10.19
function dynamicDisplay(){
websocket = wsConnect();
websocket.onopen = function (evt) {
if(websocket.readyState==1){
websocket.send('user_id=1');
console.log("websocket连接成功");
}
};
websocket.onclose = function (evt) {
console.log("Disconnected");
if(websocket.readyState==3){
websocket.send('close');
wsConnect();
console.log('reconnect');
}
};
websocket.onmessage = function (evt) {
console.log('服务器推送过来的数据: ' + evt.data);
var data = evt.data;
data = eval('(' + data + ')');
var temp = [];
// for (var i = 0; i <data.length; i++) {
// temp.push(data[i]);
// }
temp.push(data);
openInfo(temp);
};
websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
if(websocket.readyState==3){
wsConnect();
console.log('reconnect');
}
};
//关闭页面时记得一定要关闭连接,否则服务端可能不会释放
window.onunload = function(){
websocket.close();
};
}