本文是我在实际工作中用到的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. log4j和slf4j的区别

    之前在项目中用的日志记录器都是log4j的日志记录器,可是到了公司发现项目要求使用slf4j,于是想着研究一下slf4j的用法. 注意:每次引入Logger的时候注意引入的jar包,因为有Logger ...

  2. 第08节-开源蓝牙协议栈BTStack数据处理

    本篇博客根据韦东山的视频整理所得. 在上篇博客,通过阅读BTStack的源码,大体了解了其框架,对于任何一个BTStack的应用程序都有一个main函数,这个main函数是统一的.这个main函数做了 ...

  3. Navicat Premium 12 永久使用办法

    1.按步骤安装Navicat Premium,如果没有可以去官网下载:http://www.navicat.com.cn/download/navicat-premium 2.安装好后下载激活文件:h ...

  4. 25-C#笔记-文件的输入输出

    1. 写txt FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileSh ...

  5. 常用dos命令(3)

    网络命令 ping 进行网络连接测试.名称解析 ftp 文件传输 net 网络命令集及用户管理 telnet 远程登陆 ipconfig显示.修改TCP/IP设置 msg 给用户发送消息 arp 显示 ...

  6. 修改/etc/docker/daemon.json中的log-opts配置发现无效 docker 限制日志大小

    https://colobu.com/2018/10/22/no-space-left-on-device-for-docker/ 在/etc/docker/daemon.json中修改或添加log- ...

  7. 写入Txt文本信息

    public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { L ...

  8. java类uuid源码分析

    通用唯一识别码(英语:Universally Unique Identifier,简称UUID)是一种软件建构的标准,亦为自由软件基金会组织在分散式计算环境领域的一部份.UUID的目的,是让分散式系统 ...

  9. Hyperparameters

    参数是机器学习算法的关键.它们通常由过去的训练数据中总结得出.在经典的机器学习文献中,我们可以将模型看作假设,将参数视为对特定数据集的量身打造的假设. 模型是否具有固定或可变数量的参数决定了它是否可以 ...

  10. 引入jquery时,页面一直加载

    注意jquery的引用位置最好放在<head>下面.