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都要实现,两者实现原理 ...
随机推荐
- 团队第五次——Alpha2的发布
这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesign/ 这个作业要求在哪里 https:// ...
- 借助模板类自动实现COM连接点接收器(Sink)
本文的更新:借助模板类自动实现COM连接点接收器(Sink)更新 (2014-06-09 17:09) 最初的代码源自free2000fly的一个标准的 COM 连接点接收器(Sink)的实现, 使用 ...
- node 单线程异步非阻塞
链接:http://www.runoob.com/nodejs/nodejs-callback.html 首先什么是单线程异步非阻塞? 单线程的意思整个程序从头到尾但是运用一个线程,程序是从上往下执行 ...
- 数据库备份 DBS(Database Backup),知识点
资料 网址 什么是DBS https://help.aliyun.com/document_detail/59133.html?spm=5176.13685554.103.6.3fa463f9CDwW ...
- Oracle存储过程常用语法及其使用
1.什么是存储过程 存储过程Procedure是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名称并给出参数来执行.它可以接受参数.输出参数,并可以返回单个或多个 ...
- q1095
一,写题 1,我这个递归的错误我挺想搞出来的 int fa(int x) { ) return cnt; ==) { x=x/; cout<<"测试1:"<< ...
- contest5 CF991 div2 ooooxx ooooox ooooox
题意 div2D 给出一个棋盘, 有一些点不能放, 总共\(2\)排, 长度\(n(\le 100)\) 在上面空位摆放\('L'\)字形的牌, 问最多能放几个 例如: 00X00X0XXX0 0XX ...
- STL——list用法总结
头文件 #include<list> 声明一个int型的list:list<int> a: 1.list的构造函数 list<int>a{1,2,3} list&l ...
- C# 委托的本质
它本质是一个方法的容器 委托 只是 一件衣服, 在所有将委托做参数的地方 ,首先想到的是放一个对应的方法进来.
- 洛谷 p1516 青蛙的约会 题解
dalao们真是太强了,吊打我无名蒟蒻 我连题解都看不懂,在此篇题解中,我尽量用语言描述,不用公式推导(dalao喜欢看公式的话绕道,这篇题解留给像我一样弱的) 进入正题 如果不会扩展欧里几德的话请先 ...