搜索个人公众号 前端交流 或者加个人学生党v q5281951 欢迎一起学习前端
客户端 websocket对象通过ws协议实现 在运行客户端之前先把服务器端运行 客户端可以多个页面创建同一端口的websocket对象 用于长连接通信
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> // 创建一个Socket实例 var socket = new WebSocket('ws://localhost:9090'); // 打开Socket // socket.onopen = function(event) { // // 发送一个初始化消息 // // socket.send("init msg"); // }; socket.onmessage = function(event) { console.log('收到消息',event); }; // 监听Socket的关闭 socket.onclose = function(event) { console.log('关闭监听',event); }; function send() { socket.send("client msg"); } </script> </head> <body> <button onclick="send()">发送消息</button> </body> </html>服务器端(PHP实现)
<?php $a="127.0.0.1"; $b="root"; $c="123456"; $q="web"; $conn=mysqli_connect($a,$b,$c,$q); mysqli_query($conn,"set names utf8"); if(($socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) { echo "socket_create() 失败的原因是:".socket_strerror($sock)."\n"; } if(($ret = socket_bind($socket,'127.0.0.1','9090')) < 0) {//前后端口一致 echo "socket_bind() 失败的原因是:".socket_strerror($ret)."\n"; } if(($ret = socket_listen($socket,3)) < 0) { echo "socket_listen() 失败的原因是:".socket_strerror($ret)."\n"; } $all_sockets = [$socket]; // socket 集合 do { $copy_sockets = $all_sockets; // 单独拷贝一份 // 因为客户端是长连接,如果客户端非正常断开,服务端会在 socket_accept 阻塞,现在使用 select 非阻塞模式 socket if(socket_select($copy_sockets, $write, $except, 0) === false) exit('sosket_select error!'); // 接收第一次 socket 连入,连入后移除服务端 socket if(in_array($socket, $copy_sockets)) { global $client; $client = socket_accept($socket); if($client) { $buf = socket_read($client, 1024); echo $buf; // 匹配 Sec-Websocket-Key 标识 if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/i",$buf,$match)) { // 需要将 Sec-WebSocket-Key 值累加字符串,并依次进行 SHA-1 加密和 base64 加密 $key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true)); // 拼凑响应内容 $res= "HTTP/1.1 101 Switching Protocol".PHP_EOL ."Upgrade: WebSocket".PHP_EOL ."Connection: Upgrade".PHP_EOL ."WebSocket-Location: ws://127.0.0.1:9090".PHP_EOL ."Sec-WebSocket-Accept: " . $key .PHP_EOL.PHP_EOL; // 注意这里,需要两个换行 // 向客户端应答 Sec-WebSocket-Accept socket_write($client, $res, strlen($res)); // 向客户端发送消息 $da['a']="12"; $da['b']="13"; socket_write($client, buildMsg(json_encode($da)), 1024);//连接成功返回值就加载一次 // 加入客户端 socket $all_sockets[] = $client; } // 移除服务端 socket $key = array_search($socket, $copy_sockets); unset($copy_sockets[$key]); // socket_close($client); } } // 循环所有客户端 sockets 每一次send提交就会触发一次 foreach ($copy_sockets as $s) { // 获取客户端发给服务端的内容 $buf = socket_read($s, 8024); if(strlen($buf) < 9) { $key = array_search($s, $all_sockets); unset($all_sockets[$key]); socket_close($s); continue; } $te=getMsg($buf); $b=mysqli_query($conn,"INSERT INTO talk(text) VALUES ('{$te}')"); if($b){//多个页面连接websocket结合数据库 echo 'yes'; } else{ echo 'no'; } // 输出 echo getMsg($buf).PHP_EOL; } }while(true); socket_close($socket); // 编码服务端向客户端发送的内容 function buildMsg($msg) { $frame = []; $frame[0] = '81'; $len = strlen($msg); if ($len < 126) { $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len); } else if ($len < 65025) { $s = dechex($len); $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s; } else { $s = dechex($len); $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s; } $data = ''; $l = strlen($msg); for ($i = 0; $i < $l; $i++) { $data .= dechex(ord($msg{$i})); } $frame[2] = $data; $data = implode('', $frame); return pack("H*", $data); } // 解析客户端向服务端发送的内容 function getMsg($buffer) { $res = ''; $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++) { $res .= $data[$index] ^ $masks[$index % 4]; } return $res; } ?>