php:使用socket函数创建WebSocket服务

一、前言

        闲来无事,最近捣鼓了下websocket,但是不希望安装第三方类库,所以打算用socket基础函数创建个服务。

    

二、构建websocket服务端

<?phpclass SocketService
{// 默认的监听地址和端口private $address  = '0.0.0.0';private $port = 8083;private $_sockets;/*** 构造函数,初始化地址和端口** @param string $address 监听的地址,默认 '0.0.0.0'* @param int $port 监听的端口,默认 8083*/public function __construct($address = '', $port = ''){if (!empty($address)) {$this->address = $address;}if (!empty($port)) {$this->port = $port;}}/*** 初始化服务,创建套接字并开始监听*/public function service(){// 获取 TCP 协议号$tcp = getprotobyname("tcp");// 创建 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);echo "listen on $this->address $this->port ... \n";// 保存套接字$this->_sockets = $sock;}/*** 运行 WebSocket 服务* * 该方法会进入一个无限循环,处理所有客户端连接*/public function run(){// 启动服务$this->service();// 存储客户端套接字$clients[] = $this->_sockets;// 无限循环监听客户端连接while (true) {$changes = $clients;$write = NULL;$except = NULL;// 监听可读的套接字socket_select($changes, $write, $except, NULL);// 处理每个连接的套接字foreach ($changes as $key => $_sock) {// 判断是否是新连接if ($this->_sockets == $_sock) {// 接受新连接if (($newClient = socket_accept($_sock)) === false) {die('failed to accept socket: ' . socket_strerror($_sock) . "\n");}// 读取客户端发送的数据$line = trim(socket_read($newClient, 1024));// 执行 WebSocket 握手$this->handshaking($newClient, $line);// 获取客户端 IPsocket_getpeername($newClient, $ip);// 将新连接的客户端保存$clients[$ip] = $newClient;// 输出客户端 IP 和消息echo "Client ip:{$ip}   \n";echo "Client msg:{$line} \n";} else {// 处理已连接的客户端消息socket_recv($_sock, $buffer, 2048, 0);// 解码接收到的消息$msg = $this->message($buffer);// 在这里处理业务逻辑echo "{$key} client msg: {$msg}\n";// 等待用户输入响应fwrite(STDOUT, 'Please input a argument:');$response = trim(fgets(STDIN));// 发送响应给客户端$this->send($_sock, $response);echo "{$key} response to Client: {$response}\n";}}}}/*** WebSocket 握手处理* * @param resource $newClient 新连接的客户端套接字* @param string $line 接收到的握手请求头* @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];}}// 获取客户端的 Sec-WebSocket-Key$secKey = $headers['Sec-WebSocket-Key'];// 生成 Sec-WebSocket-Accept$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/websocket/websocket\r\n" ."Sec-WebSocket-Accept:$secAccept\r\n\r\n";// 发送握手响应return socket_write($newClient, $upgrade, strlen($upgrade));}/*** 解析接收到的 WebSocket 消息* * @param string $buffer 接收到的 WebSocket 数据* @return 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;}/*** 发送 WebSocket 消息给客户端* * @param resource $newClient 新连接的客户端套接字* @param string $msg 要发送的消息* @return int 返回写入的字节数*/public function send($newClient, $msg){// 封装消息为 WebSocket 数据帧$msg = $this->frame($msg);// 发送数据帧socket_write($newClient, $msg, strlen($msg));}/*** 将消息封装为 WebSocket 数据帧* * @param string $s 要封装的消息* @return string 封装后的 WebSocket 数据帧*/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;}/*** 关闭 WebSocket 连接* * @return bool 返回是否成功关闭*/public function close(){return socket_close($this->_sockets);}
}// 创建并运行 WebSocket 服务
$sock = new SocketService();
$sock->run();

三、构建websocket客户端

  接下来写个前端页面,测试服务端是否正常,代码如下:

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"><title>WebSocket</title></head><body><input id="text" value=""><input type="submit" value="发送" onclick="start()"><input type="submit" value="关闭" onclick="close()"><div id="msg"></div><script>/*** WebSocket的连接状态代码:* 0: 未连接* 1: 已连接,可以通讯* 2: 正在关闭* 3: 已关闭或无法打开*/// 创建WebSocket实例var webSocket = new WebSocket("ws://127.0.0.1:8083");// 监听错误事件webSocket.onerror = function (event) {onError(event);};// 监听连接成功事件webSocket.onopen = function (event) {onOpen(event);};// 监听消息事件webSocket.onmessage = function (event) {onMessage(event);};// 监听关闭事件webSocket.onclose = function (event) {onClose(event);};// 错误处理函数function onError(event) {document.getElementById("msg").innerHTML = "<p>连接错误</p>";console.log("错误: " + event.data);}// 连接成功后的回调函数function onOpen(event) {console.log("连接成功: " + sockState());document.getElementById("msg").innerHTML = "<p>已连接到服务</p>";}// 处理接收到的消息function onMessage(event) {console.log("接收到消息");document.getElementById("msg").innerHTML += "<p>响应: " + event.data + "</p>";}// 连接关闭后的回调函数function onClose(event) {document.getElementById("msg").innerHTML = "<p>连接已关闭</p>";console.log("关闭连接: " + sockState());webSocket.close();}// 获取WebSocket连接状态function sockState() {var status = ['未连接', '已连接,可以通讯', '正在关闭', '已关闭或无法打开'];return status[webSocket.readyState];}// 发送消息函数function start(event) {console.log(webSocket);var msg = document.getElementById('text').value;document.getElementById('text').value = ''; // 清空输入框console.log("发送消息: " + sockState());console.log("消息内容: " + msg);webSocket.send("msg=" + msg); // 发送消息document.getElementById("msg").innerHTML += "<p>请求: " + msg + "</p>";}// 关闭连接function close(event) {webSocket.close();}</script></body>
</html>

四、测试结果

出现已连接到服务,代表成功连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/476243.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tailscale 自建 Derp 中转服务器(全程无 Docker + 无域名纯 IP 版本)

文章目录 整体大纲目的&#xff1a;为什么要建立 Derp 中转服务器云服务器安装 DerpDerp 中转服务器介绍安装 Go 环境通过 Go 安装 Derp处理证书文件自签一个域名给 Derp验证 Derp 是否启动 云服务器安装并登录 Tailscale第一种组合&#xff1a; 官方 Tailscale 账号 自己的 D…

shell--第一次作业

1.接收用户部署的服务名称 # 脚本入口 read -p "请输入要部署的服务名称&#xff1a;" service_name 2.判断服务是否安装 # 判断服务是否安装 if rpm -q "$service_name" &>/dev/null; then echo "服务 $service_name 已安装。" 已…

项目部署问题bug记录(长期更新)

一、编译相关 1.submodules/simple-knn/simple_knn.cu(90): error: identifier "FLT_MAX" is undefined me.minn { FLT_MAX, FLT_MAX, FLT_MAX }; 部署photoreg工程&#xff0c;在编译simple_knn的时候&#xff0c;报错&#xff1a; (photoreg) leelee…

ant-design-vue中table组件多列排序

antD中table组件多列排序 使用前注意实现效果图实现的功能点及相关代码1. 默认按某几个字段排序2. 点击排序按钮可同时对多个字段进行排序3. 点击重置按钮可恢复默认排序状态。 功能实现完整的关键代码 使用前注意 先要确认你使用的antD版本是否支持多列排序&#xff0c;我这里…

影视后期学习Ⅰ~

1.DV是光盘 磁带 2.序列就是我们要制作的一个视频。 打开界面显示&#xff1a; 一号面板放的是素材&#xff0c;二号面板叫源监视器面板&#xff08;它的名字需要记住&#xff09;在一号面板点击文件之后&#xff0c;进入二号面板&#xff0c;在二号面板预览没问题后&#xf…

大语言模型---Llama模型文件介绍;文件组成

文章目录 1. 概要2. 文件组成 1. 概要 在使用 LLaMA&#xff08;Large Language Model Meta AI&#xff09;权重时&#xff0c;通常会涉及到与模型权重存储和加载相关的文件。这些文件通常是以二进制格式存储的&#xff0c;具有特定的结构来支持高效的模型操作。以下以Llama-7…

elasticsearch介绍和部署

1 elasticsearch介绍 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。可以很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性。Elasticsearch 的实现原理主要分为以下几个步骤&#xff0c;首先用户将数据提交到Elasticsea…

ZYNQ-7020嵌入式系统学习笔记(1)——使用ARM核配置UART发送Helloworld

本工程实现调用ZYNQ-7000的内部ARM处理器&#xff0c;通过UART给电脑发送字符串。 硬件&#xff1a;正点原子领航者-7020 开发平台&#xff1a;Vivado 2018、 SDK 1 Vivado部分操作 1.1 新建工程 设置工程名&#xff0c;选择芯片型号。 1.2 添加和配置PS IP 点击IP INTEGR…

Jenkins更换主题颜色+登录页面LOGO图片

默认主题和logo图片展示 默认主题黑色和白色。 默认LOGO图片 安装插件 Login ThemeMaterial Theme 系统管理–>插件管理–>Available plugins 搜不到Login Theme是因为我提前装好了 没有外网的可以参考这篇离线安装插件 验证插件并修改主题颜色 系统管理–>A…

《操作系统》实验内容 实验二 编程实现进程(线程)同步和互斥(Python 与 PyQt5 实现)

实验内容 实验二 编程实现进程&#xff08;线程&#xff09;同步和互斥 1&#xff0e;实验的目的 &#xff08;1&#xff09;通过编写程序实现进程同步和互斥&#xff0c;使学生掌握有关进程&#xff08;线程&#xff09;同步与互斥的原理&#xff0c;以及解决进程&#xf…

【倍数问题——同余系】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e5 10, M 1e3 10; int maxx[M][4]; void consider(int r, int x) {if(x > maxx[r][1]){maxx[r][3] maxx[r][2];maxx[r][2] maxx[r][1];maxx[r][1] x;}else if(x > maxx[r][2]){maxx[…

结合第三方模块requests,文件IO、正则表达式,通过函数封装爬虫应用采集数据

#引用BeautifulSoup更方便提取html信息&#xff1b;requests模块&#xff0c;发生http请求&#xff1b;os模块&#xff0c;文件写入import requests from bs4 import BeautifulSoup import os#当使用requests库发送请求时&#xff0c;如果不设置User - Agent&#xff0c;默认的…

Linux虚拟机网络配置

Linux固定IP 跳转到 cd /etc/sysconfig/network-scripts/ 打开文件并编辑 vim ifcfg-ens33 增加或修改选中内容 重启网卡 systemctl restart network ifconfig -a 查看ip已固定 虚拟机网络编辑器调整 子网IP进行修改&#xff0c;例如本机IP修改为10.212.197.34 此处就修改…

CSS实现实现当文本内容过长时,中间显示省略号...,两端正常展示

HTML 结构解析 文档结构: <ul class"con">: 一个无序列表&#xff0c;包含多个列表项。 每个 <li class"wrap"> 表示一个列表项&#xff0c;内部有两个 <span> 元素&#xff1a; <span class"txt">: 显示文本内容。<…

排序算法:直接插入排序,希尔排序,选择排序,快速排序,堆排序,归并排序

1.直接插入排序 基本思想&#xff1a;把待排序的数按照大小逐个插入到前面已经排序好的有序序列中&#xff0c;直到所有的都插入完为止&#xff0c;得到一个新的有序序列。 如图所示&#xff0c;当插入第i个&#xff08;i>1&#xff09;元素的时候&#xff0c;前面的arr[0]…

Qt:信号槽

一. 信号槽概念 信号槽 是 Qt 框架中一种用于对象间通信的机制 。它通过让一个对象发出信号&#xff0c;另一个对象连接到这个信号的槽上来实现通信。信号槽机制是 Qt 的核心特性之一&#xff0c;提供了一种灵活且类型安全的方式来处理事件和数据传递。 1. 信号的本质 QT中&a…

aws凭证(一)凭证存储

AWS 凭证用于验证身份&#xff0c;并授权对 DynamoDB 等等 AWS 服务的访问。配置了aws凭证后&#xff0c;才可以通过编程方式或从AWS CLI连接访问AWS资源。凭证存储在哪里呢&#xff1f;有以下几个方法&#xff1a; 一、使用文件存储 1、介绍 文件存储适用于长期和多账户配置…

Win11下载和配置VSCode(详细讲解)

配置VSCode需要的工具&#xff1a; 一、MinGW-w64 二、Visual Studio Code 一、MinGW-w64下载 1、下载 MinGW官网地址&#xff1a; Downloads - MinGW-w64 直链下载&#xff1a; 下载 mingw-w64-install.exe &#xff08;MinGW-w64 - 适用于 32 位和 64 位 Windows&#…

Python简介以及解释器安装(保姆级教学)

目录 一、Python介绍 1、简介 2、特点 3、来源 4、发展 二、Python解释器的安装 1、安装包下载 2、下载完成后&#xff0c;点击安装包进入安装流程 一、Python介绍 1、简介 Python 是一门解释型、面向对象以及动态数据类型的高级程序设计语言&#xff0c;语法简洁&…

【论文速读】| RobustKV:通过键值对驱逐防御大语言模型免受越狱攻击

基本信息 原文标题&#xff1a;ROBUSTKV: DEFENDING LARGE LANGUAGE MODELS AGAINST JAILBREAK ATTACKS VIA KV EVICTION 原文作者&#xff1a;Tanqiu Jiang, Zian Wang, Jiacheng Liang, Changjiang Li, Yuhui Wang, Ting Wang 作者单位&#xff1a;Stony Brook University…