文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

php 实现websocket服务

2023-09-04 10:49

关注

文章目录

前言

之前使用的都是封装好的websocket,现在使用php提供的相关函数实现一个websocket服务

流程

  1. 通过 socket_create 创建服务端
  2. socket_bind 绑定服务端地址
  3. socket_listen监听服务端
  4. socket_select 剔除不是正在接受消息的客户端或者不是正常状态的服务端
  5. 判断是不是正在和服务端进行握手,是的将客户端维护
  6. 遍历正在接受消息的客户端,对其接受到的内容进行处理(是断开,还是单纯的传输信息)

握手 socket_read 读到的报文内容

GET / HTTP/1.1Host: localhost:8084User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:104.0) Gecko/20100101 Firefox/104.0Accept: */*Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflate, brSec-WebSocket-Version: 13Origin: http://localhost:8081Sec-WebSocket-Extensions: permessage-deflateSec-WebSocket-Key: hJno9ufjz22HoskdJC9wMw==Connection: keep-alive, UpgradeSec-Fetch-Dest: websocketSec-Fetch-Mode: websocketSec-Fetch-Site: same-sitePragma: no-cacheCache-Control: no-cacheUpgrade: websocket

socket_select

socket_select( r e a d ) 能将当前没有正在接受消息的客户端或者不是正常状态的服务端从 read) 能将当前没有正在接受消息的客户端或者不是正常状态的服务端从 read)能将当前没有正在接受消息的客户端或者不是正常状态的服务端从read中剔除

解析客户端发送的数据

网上拷过来的代码

function parseMessage($data){    $res = "";    $len = ord($data[1]) & 127;    if ($len === 126) {        $make = substr($data, 4, 4);        $result = substr($data, 8);    } elseif ($len === 127) {        $make = substr($data, 10, 4);        $result = substr($data, 14);    } else {        $make = substr($data, 2, 4);        $result = substr($data, 6);    }    for ($i = 0; $i < strlen($result); $i++) {        $res .= $result[$i] ^ $make[$i % 4];    }    return $res;}

客户端发送消息处理

网上拷过来的代码

function createMessage($data){    $result = [0 => '81'];    $len = strlen($data);    if ($len < 126) {        $result[1] = $len < 16 ? '0' . dechex($len) : dechex($len);    } elseif ($len < 65025) {        $str = dechex($len);        $result[1] = '7e' . str_repeat('0', 4 - strlen($str)) . $str;    } else {        $str = dechex($len);        $result[1] = '7f' . str_repeat('0', 16 - strlen($str)) . $str;    }    $result2 = "";    for ($i = 0; $i < $len; $i++) {        $result2 .= dechex(ord($data[$i]));    }    $result[2] = $result2;    $response = implode('', $result);    return pack("H*", $response);}

前端连接示例

从菜鸟教程拷过来的

DOCTYPE HTML><html><head>    <meta charset="utf-8">    <title>菜鸟教程(runoob.com)title>    <script type="text/javascript">        function WebSocketTest() {            if ("WebSocket" in window) {                alert("您的浏览器支持 WebSocket!");                let socketUrl = "ws://localhost:8084";                // 打开一个 web socket                var ws = new WebSocket(socketUrl);                ws.onopen = function () {                    // Web Socket 已连接上,使用 send() 方法发送数据                    ws.send("发送数据");                    alert("数据发送中...");                };                ws.onmessage = function (evt) {                    var received_msg = evt.data;                    alert("数据已接收...");                };                ws.onclose = function () {                    // 关闭 websocket                    alert("连接已关闭...");                };            } else {                // 浏览器不支持 WebSocket                alert("您的浏览器不支持 WebSocket!");            }        }    script>head><body><div id="sse">    <a href="javascript:WebSocketTest()">运行 WebSocketa>div>body>html>

完整示例

# https://www.php.net/manual/zh/function.socket-create.php# 基于tcp协议创建socket# AF_INET ipv4网络协议# SOCK_STREAM 可靠字节流# SOL_TCP tcp传输控制协议# 创建套接字$service = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);# 绑定地址socket_bind($service, "127.0.0.1", "8084") or die("不能绑定socket地址");# 监听socket_listen($service, 6);$services = [$service];while (true) {    $copyServices = $services;    if (socket_select($copyServices, $write, $except, 0) === false)        exit("error");    # 判断服务端是否还在    # 如果服务端不是正常的状态,也会被socket_select剔除    if (in_array($service, $copyServices)) {        $client = socket_accept($service);        $body = socket_read($client, 8096);        # 判断是否是第一次建立连接        # 是的话将客户端保存        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/i", $body, $match)) {            # 按照固定算法和字符串对key进行加密            $key = base64_encode(sha1($match[1] . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));            # 拼接响应 head 头            $header = "HTTP/1.1 101 Switching Protocol" . PHP_EOL                . "Upgrade: WebSocket" . PHP_EOL                . "Connection: Upgrade" . PHP_EOL;            $header .= "WebSocket-Location: ws://127.0.0.1:8084" . PHP_EOL;            $header .= "Sec-WebSocket-Accept: " . $key . PHP_EOL . PHP_EOL;            socket_write($client, $header);            socket_write($client, createMessage("hello websocket"));            echo "建立连接" . PHP_EOL;            $services[] = $client;        }        # 剔除服务端        $key = array_search($service, $copyServices);        unset($copyServices[$key]);    }    if ($copyServices) {        echo "服务端数量" . count($services);    }    # $copyServices剔除了服务端    # 还被socket_select剔除了没在接受消息的客户端    foreach ($copyServices as $c) {        $buf = socket_read($c, 8096);        # 消息可能是断开连接的信息,需要关闭并剔除客户端        if (strlen($buf) < 9) {            $key = array_search($c, $services);            unset($services[$key]);            socket_close($c);            continue;        }        #有消息的就输出消息        echo parseMessage($buf);        echo PHP_EOL;    }}//网上拷的服务端向客户端发送信息处理function createMessage($data){    $result = [0 => '81'];    $len = strlen($data);    if ($len < 126) {        $result[1] = $len < 16 ? '0' . dechex($len) : dechex($len);    } elseif ($len < 65025) {        $str = dechex($len);        $result[1] = '7e' . str_repeat('0', 4 - strlen($str)) . $str;    } else {        $str = dechex($len);        $result[1] = '7f' . str_repeat('0', 16 - strlen($str)) . $str;    }    $result2 = "";    for ($i = 0; $i < $len; $i++) {        $result2 .= dechex(ord($data[$i]));    }    $result[2] = $result2;    $response = implode('', $result);    return pack("H*", $response);}//网上拷的解析客户端发送给服务端的消息function parseMessage($data){    $res = "";    $len = ord($data[1]) & 127;    if ($len === 126) {        $make = substr($data, 4, 4);        $result = substr($data, 8);    } elseif ($len === 127) {        $make = substr($data, 10, 4);        $result = substr($data, 14);    } else {        $make = substr($data, 2, 4);        $result = substr($data, 6);    }    for ($i = 0; $i < strlen($result); $i++) {        $res .= $result[$i] ^ $make[$i % 4];    }    return $res;}socket_close($service);

来源地址:https://blog.csdn.net/qq_29744347/article/details/126919873

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯