Loading... # [PHP 用websocket实现客户端和服务器消息双向推送](https://blog.p2hp.com/archives/4987) **PHP 实现websocket** ## html代码 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>发送弹幕</title> <link href="https://cdn.bootcss.com/<a class="wpal-linked-keyword" href="https://bootstrap.p2hp.com/" target="_blank">bootstrap</a>/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-xs-1 col-sm-1 col-md-1 col-lg-1"> </div> <div class="col-xs-10 col-sm-10 col-md-10 col-lg-10"> <div class="form-group"> <p style="height:30px"></p> <div class="col-sm-2"> </div> <div class="col-sm-4"> <input type="text" class="form-control" id="barrage" name="barrage" placeholder="弹幕" value=""> </div> <div class="col-sm-4"> <button type="button" class="btn btn-primary" id="send">发送弹幕</button> </div> </div> <!-- 弹幕内容 --> <div class="form-group"> <p style="height:30px"></p> <textarea class="form-control" rows="20" id="content"></textarea> </div> </div> <div class="col-xs-1 col-sm-1 col-md-1 col-lg-1"> </div> </div> </div> </body> <script> $(document).ready(function () { var ws = new <a class="wpal-linked-keyword" href="https://websocket.p2hp.com/" target="_blank">WebSocket</a>("ws://127.0.0.1:9777"); ws.onopen = function () { console.log("握手成功"); } ws.onmessage = function (e) { var content = e.data; $('#content').append(content + "\n"); console.log(content); } ws.onerror = function () { console.log("error"); } $('#send').click(function (e) { e.preventDefault(); var barrage = $('#barrage').val(); ws.send(barrage); }); $('#barrage').bind('keypress', function (event) { if (event.keyCode == "13") { var barrage = $('#barrage').val(); ws.send(barrage); } }); }); </script> </html> ``` ## PHP代码 ```php <?php class <a class="wpal-linked-keyword" href="https://socketio.p2hp.com/" target="_blank">Socket</a> { const BIND_NUM = 20; private $master; private $sockets = []; private $handshake = false; // 握手 public function __construct($address, $port) { try { // 创建 $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // 参数 socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1); socket_set_nonblock($this->master); // 绑定 socket_bind($this->master, $address, $port); // 监听 socket_listen($this->master, static::BIND_NUM); $this->sockets[] = $this->master; $pid = posix_getpid(); // 输出 $this->say("Server Started : " . date('Y-m-d H:i:s')); $this->say("Listening on : " . $address . " port " . $port); $this->say("Pid : " . $pid); $this->say("Master socket : " . $this->master . PHP_EOL); } catch (\Exception $e) { $this->error(); } while (true) { try { // 慢点 usleep(200000); $this->doServer(); } catch (\Exception $e) { $this->error(); } } } /** * 开始服务 */ public function doServer() { $write = $except = NULL; socket_select($this->sockets, $write, $except, NULL); //自动选择来消息的socket 如果是握手 自动选择主机 foreach ($this->sockets as $socket) { // 主机 if ($this->master == $socket) { $client = socket_accept($this->master); if ($client < 0) { $this->notice("socket_accept() failed"); continue; } else { $this->connect($client); } } else { // 非主机 $bytes = socket_recv($socket, $buffer, 2048, 0); if ($bytes == 0) { // 断开连接 $this->disConnect($socket); } else { if (!$this->handshake) { // 准备握手 $this->doHandShake($socket, $buffer); } else { // 发送消息 $buffer = $this->decode($buffer); $buffer='server say:'.$buffer; $this->send($socket, $buffer); } } } } } /** * 连接 * * @param $socket */ public function connect($socket) { array_push($this->sockets, $socket); $this->say("\n" . $socket . " CONNECTED!"); $this->say(date("Y-n-d H:i:s")); } /** * 断开连接 * * @param $socket */ public function disConnect($socket) { $index = array_search($socket, $this->sockets); socket_close($socket); $this->say($socket . " DISCONNECTED!"); if ($index >= 0) { array_splice($this->sockets, $index, 1); } } /** * 握手 * * @param $socket * @param $buffer * @return bool */ function doHandShake($socket, $buffer) { $this->say("\nRequesting handshake..."); $this->say($buffer); $key = ''; if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $buffer, $match)) { $key = $match[1]; } $this->say("Handshaking..."); $upgrade = "HTTP/1.1 101 Switching Protocol\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept: " . $this->calcKey($key) . "\r\n\r\n"; //必须以两个回车结尾 $this->say($upgrade); socket_write($socket, $upgrade, strlen($upgrade)); $this->handshake = true; $this->say($key); $this->say("Done handshaking..."); return true; } /** * 基于websocket version 13 * * @param $key * @return string */ function calcKey($key) { $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); return $accept; } /** * 解密 * * @param $buffer * @return null|string */ function decode($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; } /** * 发送消息 * * @param $client * @param $msg */ function send($client, $msg) { $this->say("> " . $msg); $msg = $this->frame($msg); socket_write($client, $msg, strlen($msg)); $this->say("! " . strlen($msg)); } /** * 数据帧 * * @param $s * @return string */ 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; } /** * 标准输出 * * @param string $msg */ public function say($msg = "") { echo $msg . PHP_EOL; } /** * 异常错误输出 */ public function error() { $error = socket_last_error(); $error_msg = socket_strerror($error); echo $error_msg . PHP_EOL; } /** * 普通错误输出 * * @param string $notice */ public function notice($notice = "") { echo $notice . PHP_EOL; } } new Socket('127.0.0.1', 9777); ``` 以上开多窗口可能有问题,下面这个没有问题 https://www.cnblogs.com/jiangzuo/p/5896301.html 最后修改:2023 年 08 月 10 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏