环境配置
先开启sockets扩展
php -m | grep sockets
项目源码
https://github.com/guainttt/WebSocketChat
WebSocketSocket.php
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?php /** * Created by PhpStorm. * User: SUN * Date: 2021/3/13 * Time: 0:29 */ namespace WebSocket; class Socket { //服务端 protected $master = null; //socket连接池 protected $connectPool = []; //握手池 http升级websocket protected $handPool = []; public function __construct($ip,$port) { $this->startServer($ip,$port); } public function startServer($ip,$port) { $this->connectPool[] = $this->master= socket_create(AF_INET,SOCK_STREAM,SOL_TCP); socket_bind($this->master,$ip,$port); //1000 并发数 socket_listen($this->master,1000); while (true){ $sockets = $this->connectPool; $write = null; $except = null;//排除 //60秒 超时时间 socket_select($sockets,$write,$except,60); //从socket里拿数据 foreach ($sockets as $socket){ //服务端 if ($socket == $this->master){ //服务端 socket可读说明有新用户连接 $this->connectPool[] = $client = socket_accept($this->master); // var_dump($client);exit; $keyArr = array_keys($this->connectPool,$client); $key = end($keyArr); $this->handPool[$key] = false; }else{ // 客户端 $length = socket_recv($socket,$buffer,1024,0); if ($length<1){ // 客户端已经断开连接了 // 断开服务端连接 $this->close($socket); }else{ // 数据是正常长度 $key = array_search($socket,$this->connectPool); // 还没有握手 if ($this->handPool[$key] == false){ // 握手 $this->handShake($socket,$buffer,$key); }else{ // 解帧和封帧 $message = $this->deFrame($buffer); $message = $this->enFrame($message); //发送给所有人 $this->send($message); } } } } } } /** * 客户端断开连接 * @param $socket */ public function close($socket) { $key = array_search($socket,$this->connectPool); unset($this->connectPool[$key]); unset($this->handPool[$key]); socket_close($socket); } /** * http升级websocket * @param $socket * @param $buffer * @param $key */ public function handShake($socket,$buffer,$key) { if(preg_match("/Sec-WebSocket-Key:s*(.*?) /", $buffer, $match)){ $responseKey = base64_encode(sha1($match[1] .'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true)); //按照协议组合信息进行返回 // 加载头 $resposeHeader = "HTTP/1.1 101 Switching Protocols "; $resposeHeader .= "Upgrade: websocket "; $resposeHeader .= "Sec-WebSocket-Version: 13 "; $resposeHeader .= "Connection: Upgrade "; $resposeHeader .= "Sec-WebSocket-Accept: " . $responseKey . " "; // 发动送到客户端 socket_write($socket,$resposeHeader,strlen($resposeHeader)); //对已经握手的client做标志 $this->handPool[$key]=true; } } /** * 数据解帧 */ public function deFrame($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; } /** * 封帧 */ public function enFrame($message) { $len = strlen($message); if ($len <= 125) { return "x81" . chr($len) . $message; } else if ($len <= 65535) { return "x81" . chr(126) . pack("n", $len) . $message; } else { return "x81" . char(127) . pack("xxxxN", $len) . $message; } } /** * 群聊发送给所有客户端 */ public function send($message) { foreach ($this->connectPool as $socket){ if ($socket != $this->master){ socket_write($socket,$message,strlen($message)); } } } }
调用
server.php
<?php //use WebSocketsocket; include __DIR__.'/WebSocket/Socket.php'; $ip = '0.0.0.0'; $port = 3333; new WebSocketSocket($ip,$port);
运行
php server.php
测试
查看你的端口是否正常绑定
netstat -anp |grep 3333
打开chrome(火狐不行)
var ws=new WebSocket("ws://192.168.2.234:3333"); ws.onmessage =function(data){ console.log(data.data);} ws.send('bbb');
再打开一个页面
var ws=new WebSocket("ws://192.168.2.234:3333"); ws.onmessage =function(data){ console.log(data.data);} ws.send('ttt');
即可实现通信
参考 https://blog.csdn.net/qq_27773645/article/details/94001996
bilibili视频教程 https://www.bilibili.com/video/BV17s41177UQ?t=2653
1 安装socket扩展
docker-php-ext-install sockets