文章目录
前言
之前使用的都是封装好的websocket,现在使用php提供的相关函数实现一个websocket服务
流程
- 通过 socket_create 创建服务端
- socket_bind 绑定服务端地址
- socket_listen监听服务端
- socket_select 剔除不是正在接受消息的客户端或者不是正常状态的服务端
- 判断是不是正在和服务端进行握手,是的将客户端维护
- 遍历正在接受消息的客户端,对其接受到的内容进行处理(是断开,还是单纯的传输信息)
握手 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