AI智能
改变未来

PHP实现WebSocket实例详解


WebSocket 是什么?

摘抄网上的一些解释:

WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

WebSocket 通信协议于2011年被 IETF 定为标准 RFC 6455,并被 RFC7936 所补充规范。

—— 百度百科

WebSocket 是一个持久化的协议,这是相对于 http 非持久化来说的。

举个简单的例子,http1.0 的生命周期是以 request 作为界定的,也就是一个 request,一个 response,对于 http 来说,本次 client 与 server 的会话到此结束;而在 http1.1 中,稍微有所改进,即添加了 keep-alive,也就是在一个 http 连接中可以进行多个 request 请求和多个 response 接受操作。然而在实时通信中,并没有多大的作用,http 只能由 client 发起请求,server 才能返回信息,即 server 不能主动向 client 推送信息,无法满足实时通信的要求。而 WebSocket 可以进行持久化连接,即 client 只需进行一次握手,成功后即可持续进行数据通信,值得关注的是 WebSocket 实现 client 与 server 之间全双工通信,即 server 端有数据更新时可以主动推送给 client 端。

上图是一个演示client和server之间建立WebSocket连接时握手部分

client 建立 WebSocket 时向服务器端请求的信息

GET /chat HTTP/1.1  Host: server.example.com  Upgrade: websocket //告诉服务器现在发送的是WebSocket协议  Connection: Upgrade  Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== //是一个Base64 encode的值,这个是浏览器随机生成的,用于验证服务器端返回数据是否是WebSocket助理  Sec-WebSocket-Protocol: chat, superchat  Sec-WebSocket-Version: 13  Origin: http://example.com

服务器获取到 client 请求的信息后,根据 WebSocket 协议对数据进行处理并返回,其中要对 Sec-WebSocket-Key 进行加密等操作

HTTP/1.1 101 Switching Protocols  Upgrade: websocket //依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket  Connection: Upgrade  Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= //这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key,也就是client要求建立WebSocket验证的凭证  Sec-WebSocket-Protocol: chat

PHP 服务端

<?phpif(($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 非阻塞模式 socketif(socket_select($copy_sockets, $write, $except, 0) === false)exit(\'sosket_select error!\');// 接收第一次 socket 连入,连入后移除服务端 socketif(in_array($socket, $copy_sockets)) {$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-Acceptsocket_write($client, $res, strlen($res));// 向客户端发送消息socket_write($client, buildMsg(\'socket ok\'), 1024);// 加入客户端 socket$all_sockets[] = $client;}// 移除服务端 socket$key = array_search($socket, $copy_sockets);unset($copy_sockets[$key]);// socket_close($client);}}// 循环所有客户端 socketsforeach ($copy_sockets as $s) {// 获取客户端发给服务端的内容$buf = socket_read($s, 8024);echo strlen($buf).\'---\'.PHP_EOL;// 代表客户端主动关闭if(strlen($buf) < 9) {$key = array_search($s, $all_sockets);unset($all_sockets[$key]);socket_close($s);continue;}// 输出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;}

客户端

<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Title</title><script>// 创建一个Socket实例var socket = new WebSocket(\'ws://localhost:9090\');// 打开Socketsocket.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>

运行测试:

Client

Server

到此这篇关于PHP实现WebSocket实例详解的文章就介绍到这了,更多相关PHP实现WebSocket内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

  • php+websocket 实现的聊天室功能详解
  • PHP 实现 WebSocket 协议原理与应用详解
  • PHP框架实现WebSocket在线聊天通讯系统
  • PHP用swoole+websocket和redis实现web一对一聊天
  • ThinkPHP5.0框架结合Swoole开发实现WebSocket在线聊天案例详解
  • PHP实现websocket通信的方法示例
  • php redis 处理websocket聊天记录的实例代码
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » PHP实现WebSocket实例详解