本文是我在实际工作中用到的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实现的更多相关文章

  1. webSocket 前端 js 加入 心跳机制 的基本写法

    1前言 websocket 一般 每隔 90 秒无操作则会自动断开  ,需要加入一个心跳机制 来防止 自断 2. 实验过程 (1)设定一个jsp 或html 文件都行 ,加入元素 (2)js 源码 , ...

  2. 一个Socket连接管理池(心跳机制)

    一个Socket连接管理池(心跳机制) http://cuisuqiang.iteye.com/blog/1489661

  3. socket心跳包机制实践与理解

    实现Socket心跳包主要分为两大类,第一采用tcp自带的KeepAlive,第二是自定义心跳包,恰巧我在产品VICA中都使用过,下面就这两种心跳包机制谈谈个人的理解与感受. 首先第一种KeepAli ...

  4. uni-app中websocket的使用 断开重连、心跳机制

    前言 最近关于H5和APP的开发中使用到了webSocket,由于web/app有时候会出现网络不稳定或者服务端主动断开,这时候导致消息推送不了的情况,需要客户端进行重连.查阅资料后发现了一个心跳机制 ...

  5. zookeeper心跳机制流程梳理

    zookeeper心跳机制流程梳理 Processor链Chain protected void setupRequestProcessors() { RequestProcessor finalPr ...

  6. ESFramework 开发手册(07) -- 掉线与心跳机制(转)

    虽然我们前面已经介绍完了ESFramework开发所需掌握的各种基础设施,但是还不够.想要更好地利用ESFramework这一利器,有些背景知识是我们必须要理解的.就像本文介绍的心跳机制,在严峻的In ...

  7. 判定生死的心跳机制 --ESFramework 4.0 快速上手(07)

    在Internet上采用TCP进行通信的系统,都会遇到一个令人头疼的问题,就是"掉线".而"TCP掉线"这个问题远比我们通常所能想象的要复杂的多 -- 网络拓扑 ...

  8. 转 互联网推送服务原理:长连接+心跳机制(MQTT协议)

    http://blog.csdn.net/zhangzeyuaaa/article/details/39028369 目录(?)[-] 无线移动网络的特点 android系统的推送和IOS的推送有什么 ...

  9. Java: server/client 心跳机制实现 示例

    心跳机制 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制. 大部分CS的应用需要心跳机制.心跳机制一般在Server和Client都要实现,两者实现原理 ...

随机推荐

  1. python实现生产者消费者模型

    生产者消费之模型就是,比如一个包子铺,中的顾客吃包子,和厨师做包子,不可能是将包子一块做出来,在给顾客吃,但是单线程只能这麽做,所以用多线程来执行,厨师一边做包子,顾客一边吃包子,当顾客少时,厨师做的 ...

  2. MySQL服务启动时显示本地计算机上的MySQL服务启动后停止。某些服务在。。。

    之前一直用的好端端的,这次启动服务突然就报了这错误. 更好的阅读体验可访问 这里. 起因 为了使用 LOAD_FILE 函数,在数据库配置文件 my.ini的 [mysqld] 里添加 secure_ ...

  3. 【转】Pandas学习笔记(七)plot画图

    Pandas学习笔记系列: Pandas学习笔记(一)基本介绍 Pandas学习笔记(二)选择数据 Pandas学习笔记(三)修改&添加值 Pandas学习笔记(四)处理丢失值 Pandas学 ...

  4. $.fn与$.fx什么意思; $.extend与$.fn.extend用法区别; $(function(){})和(function(){})(jQuery)

    $.fn是指jquery的命名空间,加上fn上的方法及属性,会对jquery实例每一个有效. 如扩展$.fn.abc() 那么你可以这样子:$("#div").abc(); 通常使 ...

  5. BEST FREE UNITY ASSETS – OVER 200 CURATED QUALITY ASSETS

    http://www.procedural-worlds.com/blog/best-free-unity-assets-categorised-mega-list/ BEST FREE UNITY ...

  6. IIS 加载字体

    原文:https://blog.csdn.net/prospertu/article/details/72852500 <system.webServer> <staticConte ...

  7. ESA2GJK1DH1K升级篇: STM32远程乒乓升级,基于GPRS模块AT指令TCP透传方式,定时访问升级(含有数据校验)

    实现功能概要 单片机定时使用http访问云端的程序版本,如果版本不一致, 然后通过http下载最新的升级文件,实现远程升级STM32程序. 兼容Air202 ,SIM800 测试准备工作(默认访问我的 ...

  8. 《Attack ML Models - 李宏毅》视频笔记(完结)

    Attack ML Models - 李宏毅 https://www.bilibili.com/video/av47022853 Training的Loss:固定x,修改θ,使y0接近ytrue. N ...

  9. Web项目中使用Log4net 案例

    简介: 几乎所有的大型应用都会有自己的用于跟踪调试的API.因为一旦程序被部署以后,就不太可能再利用专门的调试工具了.然而一个管理员可能需要有一套强大的日志系统来诊断和修复配置上的问题. 经验表明,日 ...

  10. Ubuntu16.04安装Filebeat

    Filebeat官方文档地址 https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html 下载和 ...