0

0

WebSocket中的长连接以及超时问题的解决问题(代码)

不言

不言

发布时间:2019-01-12 10:01:04

|

8958人浏览过

|

来源于博客园

转载

本篇文章给大家带来的内容是关于websocket中的长连接以及超时问题的解决问题(代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

<?php
set_time_limit(0);
class SocketService
{
    private $address  = 'localhost';
    private $port = 80;
    private $_sockets;
    public function __construct($address = '', $port='')
    {
        if(!empty($address)){
            $this->address = $address;
        }
        if(!empty($port)) {
            $this->port = $port;
        }
    }

    public function service(){
        //获取tcp协议号码。
        $tcp = getprotobyname("SOL_TCP");           # 获取与协议名称关联的协议号
        $sock = socket_create(AF_INET, SOCK_STREAM, $tcp);  # 创建一个套接字(通讯节点)
        socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);  # 设置套接字选项
        if($sock < 0)
        {
            throw new Exception("failed to create socket: ".socket_strerror($sock)."\n");
        }
        socket_bind($sock, $this->address, $this->port);    # 绑定
        socket_listen($sock, $this->port);      # 监听套接字上的连接
        $this->_sockets = $sock;   
    }

    public function run(){
        $this->service();
        $clients[] = $this->_sockets;   # 数组存储 每个socket
        # 让服务器无限获取客户端传过来的信息
        while (true){
            $changes = $clients;
            $write = NULL;
            $except = NULL;
            socket_select($changes,  $write,  $except, NULL);
            foreach ($changes as $key => $_sock){
                if($this->_sockets == $_sock){  # 判断是不是新接入的socket
                    if(($newClient = socket_accept($_sock))  === false){    # 接受新的套接字上的连接   socket_accept的作用就是接受socket_bind()所绑定的主机发过来的套接流
                        die('failed to accept socket: '.socket_strerror($_sock)."\n");  # 返回描述套接字错误的字符串
                    }
                    $line = trim(socket_read($newClient, 1024));  # 读取客户端传过来的资源,并转化为字符串     socket_read的作用就是读出socket_accept()的资源并把它转化为字符串
                    $this->handshaking($newClient, $line);
                    //获取client ip
                    socket_getpeername ($newClient, $ip);                                   # 查询给定套接字的远程端,这可能导致主机/端口或UNIX文件系统路径,具体取决于其类型。
                    $clients[$ip] = $newClient;
                } else {
                    # 读取该socket的信息,注意:第二个参数是引用传参即接收数据,第三个参数是接收数据的长度
                    $lenght = socket_recv($_sock, $buffer,  2048, 0);               # 从已连接的socket接收数据   $lenght 接收到字符串长度
                    $msg = $this->message($buffer);          # 接收到的信息
                    //在这里业务代码
                    fwrite(STDOUT, 'Please input a argument:');
                    $response = trim(fgets(STDIN));
//                    $this->send($_sock, $response);                                                       # 第二个参数是获取数据    要发送的信息
                    $this->send($_sock, '在线');
                }
            }
        }
    }

    /**
     * 握手处理
     * @param $newClient socket
     * @return int  接收到的信息
     */
    public function handshaking($newClient, $line){
        $headers = array();
        $lines = preg_split("/\r\n/", $line);   # 通过一个正则表达式分隔字符串。
        foreach($lines as $line)
        {
            $line = chop($line);    # 移除字符串右端的空白字符或其他预定义字符
            if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
            {
                $headers[$matches[1]] = $matches[2];
            }
        }
        $secKey = $headers['Sec-WebSocket-Key'];
        $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
        $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
            "Upgrade: websocket\r\n" .
            "Connection: Upgrade\r\n" .
            "WebSocket-Origin: $this->address\r\n" .
            "WebSocket-Location: ws://$this->address:$this->port/服务器地址\r\n".
            "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
        return socket_write($newClient, $upgrade, strlen($upgrade));    # socket_write的作用是向socket_create的套接流写入信息,或者向socket_accept的套接流写入信息
    }

    /**
     * 解析接收数据
     * @param $buffer
     * @return null|string
     */
    public function message($buffer){
        $len = $masks = $data = $decoded = null;
        $len = ord($buffer[1]) & 127;
        if ($len === 126)  {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127)  {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else  {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    /**
     * 发送数据
     * @param $newClinet 新接入的socket
     * @param $msg   要发送的数据
     * @return int|string
     */
    public function send($newClinet, $msg){
        $msg = $this->frame($msg);
        socket_write($newClinet, $msg, strlen($msg));   # 写入套接字
    }

    public function frame($s) {
        $a = str_split($s, 125);    # 把字符串分割到数组中 第二个长度参数
        if (count($a) == 1) {
            return "\x81" . chr(strlen($a[0])) . $a[0];
        }
        $ns = "";
        foreach ($a as $o) {
            $ns .= "\x81" . chr(strlen($o)) . $o;
        }
        return $ns;
    }

    /**
     * 关闭socket
     */
    public function close(){
        # socket_close的作用是关闭socket_create()或者socket_accept()所建立的套接流
        return socket_close($this->_sockets);
    }
}

$sock = new SocketService();
$sock->run();

 

Movio
Movio

AI真人出镜视频讲解

下载

网上看到很多说会断开链接,设置心跳包也没有用

我这里直接配置了下 set_time_limit(0);   改变 php.ini中的 max_execution_time设置时间 然后就没有断线的问题了! 也保证了持久连接!

 

HTML部分


<!DOCTYPE html>
<html>
<head>
    <title>Socket 测试</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no">
    <link href="https://cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">

 <style type="text/css">
        html, body {
            min-height: 100%; }

        body {
            margin: 0;
            padding: 0;
            width: 100%;
            font-family: "Microsoft Yahei",sans-serif, Arial; }

        .container {
            text-align: center; }

        .title {
            font-size: 16px;
            color: rgba(0, 0, 0, 0.3);
            position: fixed;
            line-height: 30px;
            height: 30px;
            left: 0px;
            right: 0px;
            background-color: white; }

        .content {
            background-color: #f1f1f1;
            border-top-left-radius: 6px;
            border-top-right-radius: 6px;
            margin-top: 30px; }
        .content .show-area {
            text-align: left;
            padding-top: 8px;
            padding-bottom: 168px; }
        .content .show-area .message {
            width: 70%;
            padding: 5px;
            word-wrap: break-word;
            word-break: normal; }
        .content .write-area {
            position: fixed;
            bottom: 0px;
            right: 0px;
            left: 0px;
            background-color: #f1f1f1;
            z-index: 10;
            width: 100%;
            height: 160px;
            border-top: 1px solid #d8d8d8; }
        .content .write-area .send {
            position: relative;
            top: -28px;
            height: 28px;
            border-top-left-radius: 55px;
            border-top-right-radius: 55px; }
        .content .write-area #name{
            position: relative;
            top: -20px;
            line-height: 28px;
            font-size: 13px; }
    </style>

</head>
<body>
<div class="container">
    <div class="title">Socket 测试长连接</div>
    <div class="content">
        <div class="show-area"></div>
        <div class="write-area">
            <div><button class="btn btn-default send" >发送</button></div>
            <div><input name="name" id="name" type="text" placeholder="input your name"></div>
            <div>
                <textarea name="message" id="message" cols="38" rows="4" placeholder="input your message..."></textarea>
            </div>
        </div>
    </div>
</div>

<script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script>
        var wsurl = 'ws://localhost:80/websocket/test2.php';
        var websocket;
        websocket = new WebSocket(wsurl);
            //连接建立
            websocket.onopen = function(evevt){
                console.log("Connected to WebSocket server.");
                $('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>Connected to WebSocket server!</p>');
            }
            //收到消息
            websocket.onmessage = function(event) {
                console.log(event);
                $('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>'+event.data+'</p>');
            }

            //发生错误
            websocket.onerror = function(event){
                console.log("Connected to WebSocket server error");
                $('.show-area').append('<p class="bg-danger message"><a name=""></a><i class="glyphicon glyphicon-info-sign"></i>Connect to WebSocket server error.</p>');
            }

            //连接关闭
            websocket.onclose = function(event){
                console.log('websocket Connection Closed. ');
                $('.show-area').append('<p class="bg-warning message"><a name=""></a><i class="glyphicon glyphicon-info-sign"></i>websocket Connection Closed.</p>');
            }

    // 发送信息
            function send(){
                var name = $('#name').val();
                var message = $('#message').val();
                if(!name){
                    alert('请输入用户名!');
                    return false;
                }
                if(!message){
                    alert('发送消息不能为空!');
                    return false;
                }
                 var msg = {
                     message: message,
                     name: name
                 };
                try{
                     websocket.send(JSON.stringify(msg));
                } catch(ex) {
                    console.log(ex);
                }
            }
            //点发送按钮发送消息
            $('.send').bind('click',function(){
                send();
            });

</script>
</body>
</html>

 

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

89

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

276

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

59

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

99

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

105

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

230

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

619

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

173

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
swoole入门物联网开发与实战
swoole入门物联网开发与实战

共15课时 | 1.4万人学习

swoole项目实战(第二季)
swoole项目实战(第二季)

共15课时 | 1.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号