纯PHP实现的websocket客户端

本文介绍了一个使用纯PHP编写的轻量级WebSocket客户端,详细展示了如何解析WebSocket连接地址、建立连接、握手、发送和接收数据的过程。客户端支持文本帧、二进制帧、关闭连接、ping和pong操作。

websocket协议RFC文档地址:https://tools.ietf.org/html/rfc6455,以下是用纯PHP实现的一个轻量的websocket客户端,可以用来与websocket服务器进行通信。

 

<?php

namespace library\util;

use Exception;

class BadUriException extends Exception{}
class ServerConnectException extends Exception{}
class HandshakeException extends Exception{}
class BadFrameException extends Exception{}
class SocketRWException extends Exception{}

class WebSocketClient
{
    const PROTOCOL_WS = 'ws';
    const PROTOCOL_WSS = 'wss';

    const HTTP_HEADER_SEPARATION_MARK = "\r\n";
    const HTTP_HEADER_END_MARK = "\r\n\r\n";

    const UUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

    const PACKET_SIZE = (1 << 15);

    // 还有后续帧
    const OPCODE_CONTINUATION_FRAME = 0;
    // 文本帧
    const OPCODE_TEXT_FRAME = 1;
    // 二进制帧
    const OPCODE_BINARY_FRAME = 2;
    // 关闭连接
    const OPCODE_CLOSE = 8;
    // ping
    const OPCODE_PING = 9;
    // pong
    const OPCODE_PONG = 10;

    const FRAME_LENGTH_LEVEL_1_MAX = 125;
    const FRAME_LENGTH_LEVEL_2_MAX = 65535;

    private $protocol;

    private $host;

    private $port;

    private $path;

    private $sock;

    private $secWebSocketKey;

    private $handshakePass = false;

    private $readBuf;

    private $currentReadBufLen;

    private $readBufPos;

    private $closed = false;

    /**
     * WebSocketClient constructor.
     * @param string $wsUri
     * @param float $connectTimeout 设置连接服务器的超时时间
     * @param float $rwTimeout 设置读写数据的超时时间
     * @throws Exception
     */
    public function __construct($wsUri, $connectTimeout = 1.0, $rwTimeout = 5.0)
    {
        $this->parseUri($wsUri);

        $this->connect($connectTimeout, $rwTimeout);

        $this->handshake();

        $this->initReadBuf();
    }

    /**
     * 解析websocket连接地址
     * @param $wsUri
     * @throws BadUriException
     */
    protected function parseUri($wsUri)
    {
        $uriData = parse_url($wsUri);
        if (!$uriData) {
            throw new BadUriException('不正确的ws uri格式', __LINE__);
        }
        if ($uriData['scheme'] != self::PROTOCOL_WS && $uriData['scheme'] != self::PROTOCOL_WSS) {
            throw new BadUriException('ws的uri必须是以ws://或wss://开头', __LINE__);
        }
        $this->protocol = $uriData['scheme'];
        $this->host = $uriData['host'];

        if ($uriData['port']) {
            $this->port = (int)$uriData['port'];
        } else {
            if ($this->protocol == self::PROTOCOL_WSS) {
                $this->port = 443;
            } else {
                $this->port = 80;
            }
        }
        $this->path = $uriData['path'] ?: '/';
        if ($uriData['query']) {
            $this->path .= '?' . $uriData['query'];
        }
        if ($uriData['fragment']) {
            $this->path .= '#' . $uriData['fragment'];
        }
    }

    /**
     * 连接websocket服务器
     * @param float $timeout 连接服务器的超时时间
     * @param float $rwTimeout 设置读写数据的超时时间
     * @throws ServerConnectException
     */
    protected function connect($timeout, $rwTimeout)
    {
        $this->sock = stream_socket_client(
            ($this->protocol == self::PROTOCOL_WSS ? 'ssl://' : 'tcp://') . $this->host . ':' . $this->port,
            $errno,
            $errstr,
            $timeout
        );

        if (!$this->sock) {

            if ($errstr) {
                throw new ServerConnectException('连接ws服务器失败:' . $errstr, $errno);
            }

            throw new ServerConnectException('连接ws服务器失败: 未知错误', __LINE__);
       
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值