Socket心跳机制-JS+PHP实现
本文是我在实际工作中用到的Socket通信,关于心跳机制的维护方式,特意总结了一下,希望对朋友们有所帮助。
Socket应用:首先Socket 封装了tcp协议的,通过长连接的方式来与服务器通信,是由服务器和客户端两部分组成的,当客户端成功连接之后,服务器会记录这个用户,并为它分配资源,当客户端断开连接后,服务器会自动释放资源。
但在实际的网络环境中会有很多因素的导致服务器不知道客户端断开,或者客户端不知道服务器宕机,等等,比如网络中使用了路由器、交换机等等;这就带来一个问题:此时此刻服务器如何知道能否同客户端正常通信?解决这个问题的办法就是采用心跳。简单的说就是:在客户端和服务器连接成功后,隔一段时间服务器询问一下客户端是否还在,客户端收到后应答服务器"我还在",如果服务器超出一定时间(一般40-50秒)未收到客户端的应答,就判定它已经无法通信了,这时候需要释放资源,断开这个客户端用户。
客户端JS代码:
<!DOCTYPE html>
<html> <head lang="en">
<meta charset="utf-8">
<title></title>
</head> <body>
<h3>WebSocket协议的客户端程序</h3>
<button id="btConnect">连接到WS服务器</button>
<button id="btSendAndReceive">向WS服务器发消息并接收消息</button>
<button id="btClose">断开与WS服务器的连接</button>
<div id="val"></div>
<script type="text/javascript">
var wsClient=null;
var lastHealthTime = ; //记录最近一次心跳更新时间
var heartbeatTimer = null;//心跳执行timer
var checkTime = ; //心跳检查时间间隔-毫秒 10秒
var healthTimeOut = ;//心跳时间间隔-毫秒 20秒 var reconnectTimer = null;//重连接timer
var reconnectTime = ;//重连接时间10秒后
var uid = ""; var connectStatus = ; //状态 function connect(){
if (connectStatus == ){
wsClient=new WebSocket('ws://127.0.0.1:8000'); //这个端口号和容器监听的端口号一致
console.log("连接中...");
console.log("readyState:"+wsClient.readyState);
if (reconnectTimer){
clearTimeout(reconnectTimer);
} //连接成功
wsClient.onopen = function(){
connectStatus = wsClient.readyState;
// 表名自己是uid1
var data = uid; //1标识连接
wsClient.send(data);
console.log('ws客户端已经成功连接到服务器上');
msg.innerHTML="连接成功...";
console.log("readyState:"+wsClient.readyState);
var time = new Date();
lastHealthTime = time.getTime(); if(heartbeatTimer){
clearInterval(heartbeatTimer);
} heartbeatTimer = setInterval(function(){keepalive(wsClient)}, checkTime);
}; //收到消息
wsClient.onmessage = function(e){
console.log('ws客户端收到一个服务器消息:'+e.data);
console.log("readyState:"+wsClient.readyState);
val.innerHTML=e.data;
var data = e.data;
if (data){
var msg_type = data.substr(,);
var uid = data.substr();var time = new Date();
lastHealthTime = time.getTime();//更新客户端的最后一次心跳时间
}
} //错误
wsClient.onerror = function(e){
connectStatus = wsClient.readyState;
console.log("error");
console.log("readyState:"+wsClient.readyState);
msg.innerHTML="连接错误...";
}; //关闭
wsClient.onclose = function(){
connectStatus = wsClient.readyState;
console.log('到服务器的连接已经断开');
msg.innerHTML="连接断开...";
console.log("readyState:"+wsClient.readyState);
//n秒后重连接
reconnectTimer = setTimeout(function(){
connect();
},reconnectTime);
}
}
} btConnect.onclick = function(){
connect();
}
btSendAndReceive.onclick = function(){
wsClient.send('Hello Server');
}
btClose.onclick = function(){
console.log("断开连接");
console.log(wsClient.readyState);
wsClient.close();
} function keepalive(ws){
var time = new Date();
console.log(time.getTime()-lastHealthTime);
if ((time.getTime()-lastHealthTime)>healthTimeOut){
msg.innerHTML="心跳超时,请连接断开..."; if (heartbeatTimer){
clearInterval(heartbeatTimer); //n秒后重连接
ws.close();
reconnectTimer = setTimeout(function(){
connect();
},reconnectTime);
}
}
else{
msg.innerHTML="我依然在连接状态";
ws.send(data);
}
}
</script>
<div id="msg"></div>
</body>
</html>
服务端代码:
这里我采用的是PHP语言,使用workman来实现的socket服务器端
<?php
require_once __DIR__ .'/Autoloader.php';
use Workerman\Worker;
use Workerman\Lib\Timer; define('HEARTBEAT_TIME', 40);//心跳间隔时间
define('CHECK_HEARTBEAT_TIME', 10); // 检查连接的间隔时间
// 初始化一个worker容器,监听1234端口
$worker = new Worker('websocket://0.0.0.0:8000');
// 这里进程数必须设置为1
$worker->count = 1;
// worker进程启动后建立一个内部通讯端口
$worker->onWorkerStart = function($worker)
{
Timer::add(CHECK_HEARTBEAT_TIME, function()use($worker){
$time_now = time();
foreach($worker->connections as $connection) { // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
if (empty($connection->lastMessageTime)) {
$connection->lastMessageTime = $time_now;
continue;
} // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
$connection->close();
}
}
});
};
// 新增加一个属性,用来保存uid到connection的映射
$worker->uidConnections = array();
// 当有客户端发来消息时执行的回调函数
$worker->onMessage = function($connection, $data)use($worker)
{
$uid = $data; //uid //echo 'connection...'.$uid.'\n';
// 判断当前客户端是否已经验证,既是否设置了uid
if(!isset($connection->uid))
{
if (intval($msg_type) === 1){ //连接
//上次收到的心跳消息时间
$connection->lastMessageTime = time(); // 没验证的话把第一个包当做uid(这里为了方便演示,没做真正的验证) $connection->uid = $uid;
/* 保存uid到connection的映射,这样可以方便的通过uid查找connection,
* 实现针对特定uid推送数据
*/
$worker->uidConnections[$connection->uid] = $connection;
echo 'MSG USER COUNT:'.count($worker->uidConnections);
echo '\n';
return;
}
}
else{
if ($connection->uid === $uid){
//服务器收到心跳
//echo 'U-heart:'.$connection->uid.'\n';
$connection->lastMessageTime = time(); echo 'back send:';
$buffer = $uid;
$ret = sendMessageByUid($uid, $buffer);
$result = $ret ? 'ok' : 'fail';
// echo $result;
}
} };
// 当有客户端连接断开时
$worker->onClose = function($connection)use($worker)
{
global $worker;
if(isset($connection->uid))
{
// 连接断开时删除映射
unset($worker->uidConnections[$connection->uid]); echo 'CLOSE USER COUNT:'.count($worker->uidConnections);
echo '-'.$connection->uid.' closed';
echo '\n';
}
};
// 向所有验证的用户推送数据
function broadcast($message)
{
global $worker;
foreach($worker->uidConnections as $connection)
{
$connection->send($message);
}
}
// 针对uid推送数据
function sendMessageByUid($uid, $message)
{
global $worker;
if(isset($worker->uidConnections[$uid]))
{
$connection = $worker->uidConnections[$uid];
$connection->send($message);
return true;
}
return false;
} // 运行所有的worker(其实当前只定义了一个)
Worker::runAll();
Socket心跳机制-JS+PHP实现的更多相关文章
- webSocket 前端 js 加入 心跳机制 的基本写法
1前言 websocket 一般 每隔 90 秒无操作则会自动断开 ,需要加入一个心跳机制 来防止 自断 2. 实验过程 (1)设定一个jsp 或html 文件都行 ,加入元素 (2)js 源码 , ...
- 一个Socket连接管理池(心跳机制)
一个Socket连接管理池(心跳机制) http://cuisuqiang.iteye.com/blog/1489661
- socket心跳包机制实践与理解
实现Socket心跳包主要分为两大类,第一采用tcp自带的KeepAlive,第二是自定义心跳包,恰巧我在产品VICA中都使用过,下面就这两种心跳包机制谈谈个人的理解与感受. 首先第一种KeepAli ...
- uni-app中websocket的使用 断开重连、心跳机制
前言 最近关于H5和APP的开发中使用到了webSocket,由于web/app有时候会出现网络不稳定或者服务端主动断开,这时候导致消息推送不了的情况,需要客户端进行重连.查阅资料后发现了一个心跳机制 ...
- zookeeper心跳机制流程梳理
zookeeper心跳机制流程梳理 Processor链Chain protected void setupRequestProcessors() { RequestProcessor finalPr ...
- ESFramework 开发手册(07) -- 掉线与心跳机制(转)
虽然我们前面已经介绍完了ESFramework开发所需掌握的各种基础设施,但是还不够.想要更好地利用ESFramework这一利器,有些背景知识是我们必须要理解的.就像本文介绍的心跳机制,在严峻的In ...
- 判定生死的心跳机制 --ESFramework 4.0 快速上手(07)
在Internet上采用TCP进行通信的系统,都会遇到一个令人头疼的问题,就是"掉线".而"TCP掉线"这个问题远比我们通常所能想象的要复杂的多 -- 网络拓扑 ...
- 转 互联网推送服务原理:长连接+心跳机制(MQTT协议)
http://blog.csdn.net/zhangzeyuaaa/article/details/39028369 目录(?)[-] 无线移动网络的特点 android系统的推送和IOS的推送有什么 ...
- Java: server/client 心跳机制实现 示例
心跳机制 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制. 大部分CS的应用需要心跳机制.心跳机制一般在Server和Client都要实现,两者实现原理 ...
随机推荐
- hexo 搜索功能
搜索功能真心好用,当文章多起来的时候,标签提供的作用已经很少了,只能简单索引,搜索却能精确查找,这里我用的依旧是最简单的本地站内搜索. 安装插件 npm install hexo-generator- ...
- python基础之四:list、tuple
一.列表 list # 列表 ''' 类似str,可以进行切片 ''' li = [', [1, 2, 3], 55, 'we all in ', 'Tom', ''] print(li[0:2]) ...
- 【java异常】expected at least 1 bean which qualifies as autowire candidate for this depende
1.查看接口实现类是否加入注解,如service.repository等 2.查看spring配置文件是否自动扫描包 <context:component-scan base-packag ...
- jvm堆、栈、String常量池
原文地址:http://blog.csdn.net/tophawk/article/details/78704074 程序计数器:它的生命周期与线程相同,线程私有.较小的内存区域,用以完成分支.循环. ...
- vue+ typescript 使用parcel 构建
parcel 是一个零配置的前端构建工具,相比webpack 更快,同时使用简单以下是 一个简单的使用typescript 开发vue 应用,同时使用parcel 构建,同时集成了docker 构建, ...
- pychram-redis破解
1. Preferences -> Plugins-> 选择右下角Browse repositories 2. 搜索Iedis 3. 找到Iedis插件目录:C:\Users\用户名\.P ...
- 恐怖的AVL树
学习参考:http://www.cnblogs.com/Camilo/p/3917041.html 今天闲来无事打算学习AVL树,并以AVL树的插入作为切入点. 不知不觉,我就在电脑前编了4个小时…… ...
- 开发中常用linux命令
1.创建目录mkdir 创建目录命令,常用的参数-p,递归创建目录 [root@web01 ~]# mkdir /data [root@web01 ~]# mkdir /data/a/b mkdir: ...
- 洛谷P5020 货币系统
题目 题意简化一下就是找题目给定的n个数最多能消掉多少个,我们用个tong[i]来记录i这个数值能不能用小于等于i的货币组合起来,等于1意味着他只能由自己本身的货币组成,等于2说明他可以被其他货币组成 ...
- JAVA JDK安装及path环境变量配置
JDK安装 JVM :JAVA虚拟机 JRE :java运行环境=JVM+核心类库 JDK :JAVA开发工具包=JRE+java开发工具 java开发工具:编译工具(javac.exe) . 运行 ...