socket心跳检测_Swoole学习笔记五:搭建WebSocket长连接 之 客户端实现心跳重连

99df1d43cd4996e0010bea16847747d1.png

0、前言

我们先来想一个场景,我们进入index.php客户端页面这时候是正常上线了,也与服务端握手成功。
突然,外面网线被人挖断了,3分钟后恢复网络,这时候我们的页面没有刷新,也没有接收到服务端的断开提示。
这时候该怎么办?我们是需要刷新页面重新登录吗?
答案当然是否,在产品思维里,每个客户端的用户都是大熊猫,都是懒的,所以你要他刷新页面,他宁愿不再使用这个程序。
这时候我们需要在程序上想办法解决,先辈们已经帮我们得出了无数种解决方案,我们今天要讲的就是其中最常用的方案之一:心跳重连。

1、什么是心跳重连

心跳与重连是两个词,顾名思义,就是让重连的操作像心跳一样,定时执行。

在使用WebSocket过程中, 可能会出现网络断开的情况, 比如信号不好, 或者网络临时性关闭;
这时候WebSocket的连接已经断开, 而浏览器不会执行webSocket的onclose方法, 我们无法知道是否断开连接, 也就无法进行重连操作。
如果当前发送WebSocket数据到后端, 一旦请求超时, onclose便会执行, 这时候便可进行绑定好的重连操作。
因此WebSocket心跳重连就应运而生了。

2、客户端心跳重连

修改index.php代码为如下代码,依照案例四的demo。

<?php
// +----------------------------------------------------------------------
// 小黄牛blog - websocket
// +----------------------------------------------------------------------
// Copyright (c) 2018 https://xiuxian.junphp.com All rights reserved.
// +----------------------------------------------------------------------
// Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// Author: 小黄牛 <1731223728@qq.com>
// +----------------------------------------------------------------------
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Swoole+Websocket案例 - 小黄牛</title>
<script src="./js/jquery.min.js"></script>
<style>
html,body{margin:0;padding:0;font-size:13px}
.left{width: 20%;height: 600px;border: 1px solid #ddd;float: left;}
.right{width: 79.7%;height: 400px;border: 1px solid #ddd;border-left: 0px;float: left;overflow: auto;}
.bottom{width: 79.7%;height: 199px;border: 1px solid #ddd;border-left: 0px;border-top: 0px;float: left;}
#content{width: 99.5%;height: 165px;}
.blue{color:blue}
.red{color:red}
.div_left{width:100%;float:left}
.div_right{width:100%;float:left;text-align: right;}
.div_centent{width:100%;float:left;text-align: center;}
</style>
</head>
<body>
<div id="USER"></div>
<div class="left">
    <ul>
    </ul>
</div>
<div class="right">
</div>
<div class="bottom">
    <textarea id="content"></textarea>
    <button type="button" id="submit">发送消息</button>
</div>
<div id="error"></div>
<h3>使用方法:</h3>
<p>①:CD进您的server.php文件目录</p>
<p>②:如果您是调试阶段,可以直接php server.php,激活程序,这样的话在运行过程中出错,能在cmd界面查看报错内容</p>
<p>③:如果您是部署阶段,可以使用nohup server.php >>/dev/null 2>&1 &命令,后台守护进程运行。</p>
</body>
</html>
<script>
// 生成一个唯一ID,假设这是userID
var USER_ID = "static" + Math.round(Math.random() * 10000);
$('#USER').html('您的USER_ID为:'+USER_ID);
var lockReconnect = false; // 正常情况下我们是关闭心跳重连的
var wsServer = 'ws://47.106.187.208:9502';
var websocket;
var time;
createWebSocket();
// ①开启WebSocket
function createWebSocket() {
    try {
        websocket = new WebSocket(wsServer);
        init();
    } catch(e) {
        reconnect(wsUrl);
    }
}
// ②初始化WebSocket,并设置心跳检测
function init() {
    // 接收Socket断开时的消息通知
    websocket.onclose = function(evt) {
        $('#error').append('<p class="red">Socket断开了...正在试图重新连接...</p>');
        reconnect(wsServer);
    };
    // 接收Socket连接失败时的异常通知
    websocket.onerror = function(e){
        $('#error').append('<p class="red">Socket断开了...正在试图重新连接...</p>');
        reconnect(wsServer);
    };
    // 连接成功
    websocket.onopen = function (evt) {
        $('#error').append('<p class="blue">握手成功,打开socket连接了。。。</p>');
        var data = {
            'code':1, // 我们假设code为1时,是绑定登录请求
            'user_id':USER_ID
        };
        // 前端发送json前,必须先转义成字符串
        data = JSON.stringify(data);
        websocket.send(data);
        // 心跳检测重置
        heartCheck.start();
    };
    var message = '';
    var flag    = true;
    // 接收服务端广播的消息通知
    websocket.onmessage = function(evt){
        heartCheck.start();
        var obj = JSON.parse(evt.data); 
        // 登录广播
        if (obj.code == 1) {
            // 存在修改上线状态
            if ($("#"+obj.user_id).length>0) {
                $("#"+obj.user_id+' span').removeClass('blue');
                $("#"+obj.user_id+' span').addClass('red');
                $("#"+obj.user_id+' span').html('离线');
            // 不存在,添加用户列表
            } else {
                $('.left ul').append('<li id="'+obj.user_id+'">'+obj.user_id+' <span class="blue">(在线)</span></li>');
            }
            $('.right').append('<div class="div_centent">'+obj.content+'</div>');
        // 下线广播
        } else if (obj.code == 2) {
            $("#"+obj.user_id+' span').removeClass('blue');
            $("#"+obj.user_id+' span').addClass('red');
            $("#"+obj.user_id+' span').html('离线');
            $('.right').append('<div class="div_centent">'+obj.content+'</div>');
        // 聊天消息广播
        } else if (obj.code == 3) {
            $('.right').append('<div class="div_left">'+obj.user_id+':'+obj.content+'</div>');
            // 聊天界面默认自动底部
            $('.right').scrollTop( $('.right')[0].scrollHeight );
        // 如果是心跳检测的广播就不做任何操作
        } else if (obj.code == 4){
            return false;
        }
    };
}
// ③ 掉线重连
function reconnect(url) {
    if(lockReconnect) {
        return;
    };
    lockReconnect = true;
    // 没连接上会一直重连,设置心跳延迟避免请求过多
    time && clearTimeout(time);
    time = setTimeout(function () {
        createWebSocket(url);
        lockReconnect = false;
    }, 5000);
}
// ④心跳检测
var heartCheck = {
    timeout: 5000,
    timeoutObj: null,
    serverTimeoutObj: null,
    start: function() {
        var self = this;
        this.timeoutObj && clearTimeout(this.timeoutObj);
        this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
        this.timeoutObj = setTimeout(function(){
            // 这里发送一个心跳,后端收到后,返回一个心跳消息,
            // onmessage拿到返回的心跳就说明连接正常
            websocket.send("123456789");
            var data = {
                'code':4, // 我们假设code为4时,既为心跳检测
                'user_id':USER_ID
            };
            // 前端发送json前,必须先转义成字符串
            data = JSON.stringify(data);
            websocket.send(data);
        }, this.timeout)
    }
}
// 点击发送消息按钮
$('#submit').click(function(){
    var content = $('#content').val();
    $('.right').append('<div class="div_right">'+content+':'+USER_ID+'</div>');
    var data = {
        'code':3, // 我们假设code为3时,既为聊天消息广播请求
        'user_id':USER_ID,
        'content':content
    };
    // 前端发送json前,必须先转义成字符串
    data = JSON.stringify(data);
    websocket.send(data);
    // 输入表单清空
    $('#content').val('');
    // 聊天界面默认自动底部
    $('.right').scrollTop( $('.right')[0].scrollHeight );
});
</script>

开启两个浏览器标签分别访问index.php,然后把网络断开1分钟,观察页面提示,然后再将网络连上。

完整的案例DEMO,可以直接到我的开源栏目中进行下载:Swoole聊天室Demo之二

上一篇文章在我的专栏里,有兴趣的朋友可以去看下。


最后推荐大家可以用下我开源的一个基于Swoole4.5+研发的PHP框架。该框架基于注解实现了很多好玩的功能,很适合新人快速上手Swoole扩展。

SW-X框架-专注高性能便捷开发而生的PHP-SwooleX框架​www.sw-x.cn
9d590090f591cf29a666ba44fd4ff0e5.png
Swoole中,WebSocket客户端可以通过设置`Swoole\Client`的`onClose`事件来实现断线自动重连。具体步骤如下: 1. 首先创建WebSocket客户端对象: ``` $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL); ``` 2. 设置WebSocket客户端连接参数: ``` $client->set([ 'open_websocket_protocol' => true, 'websocket_compression' => true, 'websocket_mask' => true, 'websocket_subprotocol' => 'chat', ]); ``` 3. 设置WebSocket客户端的事件回调函数: ``` $client->on('connect', function ($client) { echo "Connected successfully.\n"; }); $client->on('receive', function ($client, $data) { echo "Received data: $data \n"; }); $client->on('error', function ($client) { echo "Error occurred.\n"; }); $client->on('close', function ($client) { echo "Connection closed.\n"; // 断线重连 swoole_timer_after(5000, function () use ($client) { $client->connect('127.0.0.1', 9501); }); }); ``` 4. 连接WebSocket服务器: ``` $client->connect('127.0.0.1', 9501); ``` 在`onClose`事件中,使用`swoole_timer_after`函数实现5秒后自动重连,即断线自动重连的功能。 完整代码示例: ``` <?php $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL); $client->set([ 'open_websocket_protocol' => true, 'websocket_compression' => true, 'websocket_mask' => true, 'websocket_subprotocol' => 'chat', ]); $client->on('connect', function ($client) { echo "Connected successfully.\n"; }); $client->on('receive', function ($client, $data) { echo "Received data: $data \n"; }); $client->on('error', function ($client) { echo "Error occurred.\n"; }); $client->on('close', function ($client) { echo "Connection closed.\n"; // 断线重连 swoole_timer_after(5000, function () use ($client) { $client->connect('127.0.0.1', 9501); }); }); $client->connect('127.0.0.1', 9501); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值