原生php使用websocket编程,简单能用

前言

首先要知道websocket的概念,我只说下我的简单理解

  • 基于http协议,在http基础上升级为ws协议,所以会携带一个Upgrade,websocket-key用于握手过程中安全验证,握手完成后就建立起了长久连接。
  • 因为连接断了不会重连,而且不止一个客户端,所以也有心跳机制和消息群发

image.png

直接上代码

  • 这里不知为什么onConnect用不了

后台

  • composer require workerman/workerman安装完后,把vendor丢到linux服务器即可使用
  • 似乎只能用命令行运行,所以还用 shell_exec去执行命了了,此处不赘述
  • 因为需求比较特殊仅做参考,只是用于持续发送消息
更新一下
  • php,狗看了都摇头
<?php
use Workerman\Worker;
use Workerman\Lib\Timer;
require_once __DIR__ . '/vendor/autoload.php';

function sendSocket($socket)
{
    
    // global $socket;
    try {
        // 创建套接字
        // $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        if ($socket) {
            // (不知道有没有用,因为不知道这里是客户端还是服务端) ['sec' => 1, 'usec' => 0]是一个关联数组,用于设置超时时间的值。其中,sec表示秒,usec表示微秒
            socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 1, 'usec' => 0]);
            // 可以立即重连,不然断开再连接会报错
            // socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
            // if (socket_bind($socket, '0.0.0.0', 7111)) {
                // 连接服务端
                if (socket_connect($socket, '0.0.0.0', 7110)) {
                    while ($buf = socket_read($socket, 2048)) {
                        // 当套接字中没有更多数据可读取时,socket_read() 返回一个长度为 0 的空字符串("")
                        if ($buf != '') {
                            return $buf;
                        }
                    }
                } else {
                    echo 'false3';
                }
            // } else {
            //     echo "socket_bind() ";
            // }
            // echo 'false2';

        } else {
            echo 'false';
        }
    } catch (Exception $e) {
        echo 'no' . $e->getMessage() . '';
    } finally {
        // socket_close($socket);
        // return 'false';
    }
}

// 注意有时没有清理干净进程,需要手动处理
global $connectAr; //连接池
global $timer_id; //连接池
$connectArr = [];
$ws = new Worker('websocket://0.0.0.0:9501');
$ws->count = 2; //提供2个进程
$ws->onWorkerStart = function () {
    global $timer_id;

    global $socket;
    $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
    // print_r($socket);
    // bind只能用一次
    socket_bind($socket, '0.0.0.0', 7111);
    $timer_id = Timer::add(1, function () {
        global $socket;
        global $connectArr; // 声明为全局变量
        $res = sendSocket($socket);
        foreach ($connectArr as $key => $connection) {
            $hex = bin2hex($res);//这里不转换会无法被游览器解析报错
            $response = json_encode([
                "data" => $hex,
                "receipt_time" => date("Y-m-d H:i:s"),
            ]);
            $connection->send($response);

            // 将连接对象转换为数组
            $connections = [];
            foreach ($connectArr as $conn) {
                $connections[] = [
                    'id' => $conn->id,
                    'remoteAddress' => $conn->getRemoteIp(),
                    'remotePort' => $conn->getRemotePort(),
                ];
            }
            $connection->send(json_encode($connections));
        }
    });

};
$ws->onMessage = function ($connection) {
    global $connectArr; // 声明为全局变量
    // global $timer_id; // 声明为全局变量
    // 新的连接
    if (!in_array($connection, $connectArr)) {
        $connectArr[] = $connection;
        $connection->send('hello'); // 向服务端发送欢迎消息
    }
};


$ws->onClose = function ($connection) {
    $connection->send('bye');
    global $connectArr; // 声明为全局变量
    global $timer_id;
    // 从连接池清除
    $index = array_search($connection, $connectArr);
    if ($index !== false) {
        unset($connectArr[$index]);
    }

    // 连接池空了就关闭
    if (empty($connectArr)) {
        Timer::del($timer_id);
        $timer_id = null;

        Worker::stopAll();
        $command = 'php websocket.php stop';
        shell_exec($command);
    };

};

Worker::runAll();
旧版本
<?php
use Workerman\Worker;
use Workerman\Lib\Timer;

require_once __DIR__ . '/vendor/autoload.php';

function sendSocket($connection)
{
    // 创建套接字
    $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    // while (true) {
    if ($socket) {
        if (socket_bind($socket, '0.0.0.0', 7111)) { //必须要有
            socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 1, 'usec' => 0]);
            // 连接客户端
            if (socket_connect($socket, '0.0.0.0', 7110)) {
                // 读取客户端发送的消息
                $buf = @socket_read($socket, 2048);
                // 当套接字中没有更多数据可读取时,socket_read() 返回一个长度为 0 的空字符串("")
                if ($buf != "") {
                    $hex = bin2hex($buf);
                    $response = json_encode([
                        "data" => $hex,
                        "receipt_time" => date("Y-m-d H:i:s"),
                    ]);
                    $connection->send($response);
                    // sendSocket($connection);
                }
            }
        }
    }
    socket_close($socket);
}

// 注意有时没有清理干净进程,需要手动处理
$connectArr = []; //连接池
$ws = new Worker('websocket://0.0.0.0:9501');
$ws->count = 1; //提供2个进程
$ws->onMessage = function ($connection) use ($connectArr) {
    // 新的连接
    if (!in_array($connection, $connectArr)) {
        $connectArr[] = $connection;
        $connection->send('{"hello":Welcome to the server!}'); // 向客户端发送欢迎消息
        // 每个连接都持续发送消息
        // 添加定时器,每隔 1 秒触发一次 sendSocket 函数
        sendSocket($connection);
        $timer_id = Timer::add(1, function () use ($connection) {
            sendSocket($connection);
        });
        // 将定时器 ID 存储到连接对象中
        $connection->timer_id = $timer_id;
    }
    // $connection->send('hello' . $data);
    // sendSocket($connectArr);
};
$ws->onClose = function ($connection) use ($connectArr) {
    // 从连接池清除
    $index = array_search($connection, $connectArr);
    if ($index !== false) {
        unset($connectArr[$index]);
    }

    // 连接池空了就关闭
    if (empty($connectArr)) {
        $connection->send('bye');
        Worker::stopAll();
        $command = 'php websocket.php stop';
        shell_exec($command);
    };
    // 取消定时器 
    Timer::del($connection->timer_id);
};
Worker::runAll();

前台

// 创建一个Socket实例
        this.websocket = new WebSocket(
          "ws://" + window.location.hostname + ":9501"
        ); //居然不能写127.0.0.1
        console.log("连接", this.websocket.readyState, this.websocket);
        // 监听状态改变事件
        this.websocket.onopen =  () => {
          //   0 CONNECTING 连接尚未建立
          //   1 OPEN 已连接
          //   2 CLOSING 正在关闭
          //   3 CLOSED 已关闭
          console.log("连接", this.websocket.readyState);
          this.websocket.send('')
        };
        // 监听消息事件(服务器推送事件)
        this.websocket.onmessage =  (event) => {
          console.log("接收", event, event.data);
        };
        // 监听连接关闭事件
        this.websocket.onclose = () => {
          console.log("this.websocket关闭", this.websocket.readyState);
        };

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值