当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP实现WebSocket实例详解

发布:smiling 来源: PHP粉丝网  添加日期:2022-05-13 15:06:48 浏览: 评论:0 

这篇文章主要介绍了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 端。

PHP实现WebSocket实例详解

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

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

  1. GET /chat HTTP/1.1  
  2.   Host: server.example.com  
  3.   Upgrade: websocket //告诉服务器现在发送的是WebSocket协议 
  4.   Connection: Upgrade  
  5.   Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== //是一个Base64 encode的值,这个是浏览器随机生成的,用于验证服务器端返回数据是否是WebSocket助理 
  6.   Sec-WebSocket-Protocol: chat, superchat  
  7.   Sec-WebSocket-Version: 13  
  8.   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 服务端

  1. <?php 
  2.  
  3. if(($socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) { 
  4.     echo "socket_create() 失败的原因是:".socket_strerror($sock)."\n"
  5. if(($ret = socket_bind($socket,'127.0.0.1','9090')) < 0) { 
  6.     echo "socket_bind() 失败的原因是:".socket_strerror($ret)."\n"
  7. if(($ret = socket_listen($socket,3)) < 0) { 
  8.     echo "socket_listen() 失败的原因是:".socket_strerror($ret)."\n"
  9.  
  10. $all_sockets = [$socket];    // socket 集合 
  11.  
  12. do { 
  13.     $copy_sockets = $all_sockets;   // 单独拷贝一份 
  14.  
  15.     // 因为客户端是长连接,如果客户端非正常断开,服务端会在 socket_accept 阻塞,现在使用 select 非阻塞模式 socket 
  16.     if(socket_select($copy_sockets$write$except, 0) === false) 
  17.         exit('sosket_select error!'); 
  18.  
  19.     // 接收第一次 socket 连入,连入后移除服务端 socket 
  20.     if(in_array($socket$copy_sockets)) { 
  21.         $client = socket_accept($socket); 
  22.         if($client) { 
  23.             $buf = socket_read($client, 1024); 
  24.             echo $buf
  25.  
  26.             // 匹配 Sec-Websocket-Key 标识 
  27.             if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/i",$buf,$match)) { 
  28.                 // 需要将 Sec-WebSocket-Key 值累加字符串,并依次进行 SHA-1 加密和 base64 加密 
  29.                 $key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true)); 
  30.                 // 拼凑响应内容 
  31.                 $res"HTTP/1.1 101 Switching Protocol".PHP_EOL 
  32.                     ."Upgrade: WebSocket".PHP_EOL 
  33.                     ."Connection: Upgrade".PHP_EOL 
  34.                     ."WebSocket-Location: ws://127.0.0.1:9090".PHP_EOL 
  35.                     ."Sec-WebSocket-Accept: " . $key .PHP_EOL.PHP_EOL;  // 注意这里,需要两个换行 
  36.                 // 向客户端应答 Sec-WebSocket-Accept 
  37.                 socket_write($client$resstrlen($res)); 
  38.                 // 向客户端发送消息 
  39.                 socket_write($client, buildMsg('socket ok'), 1024); 
  40.  
  41.                 // 加入客户端 socket 
  42.                 $all_sockets[] = $client
  43.             } 
  44.             // 移除服务端 socket 
  45.             $key = array_search($socket$copy_sockets); 
  46.             unset($copy_sockets[$key]); 
  47.  
  48.             // socket_close($client); 
  49.         } 
  50.     } 
  51.  
  52.     // 循环所有客户端 sockets 
  53.     foreach ($copy_sockets as $s) { 
  54.         // 获取客户端发给服务端的内容 
  55.         $buf = socket_read($s, 8024); 
  56.         echo strlen($buf).'---'.PHP_EOL; 
  57.         // 代表客户端主动关闭 
  58.         if(strlen($buf) < 9) { 
  59.             $key = array_search($s$all_sockets); 
  60.             unset($all_sockets[$key]); 
  61.             socket_close($s); 
  62.             continue
  63.         } 
  64.         // 输出 
  65.         echo getMsg($buf).PHP_EOL; 
  66.     } 
  67.  
  68. }while(true); 
  69. socket_close($socket); 
  70.  
  71. // 编码服务端向客户端发送的内容 
  72. function buildMsg($msg) { 
  73.     $frame = []; 
  74.     $frame[0] = '81'
  75.     $len = strlen($msg); 
  76.     if ($len < 126) { 
  77.         $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len); 
  78.     } else if ($len < 65025) { 
  79.         $s = dechex($len); 
  80.         $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s
  81.     } else { 
  82.         $s = dechex($len); 
  83.         $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s
  84.     } 
  85.     $data = ''
  86.     $l = strlen($msg); 
  87.     for ($i = 0; $i < $l$i++) { 
  88.         $data .= dechex(ord($msg{$i})); 
  89.     } 
  90.     $frame[2] = $data
  91.     $data = implode(''$frame); 
  92.     return pack("H*"$data); 
  93.  
  94. // 解析客户端向服务端发送的内容 
  95. function getMsg($buffer) { 
  96.     $res = ''
  97.     $len = ord($buffer[1]) & 127; 
  98.     if ($len === 126) { 
  99.         $masks = substr($buffer, 4, 4); 
  100.         $data = substr($buffer, 8); 
  101.     } else if ($len === 127) { 
  102.         $masks = substr($buffer, 10, 4); 
  103.         $data = substr($buffer, 14); 
  104.     } else { 
  105.         $masks = substr($buffer, 2, 4); 
  106.         $data = substr($buffer, 6); 
  107.     } 
  108.     for ($index = 0; $index < strlen($data); $index++) { 
  109.         $res .= $data[$index] ^ $masks[$index % 4]; 
  110.     } 
  111.     return $res

客户端

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"
  5.     <title>Title</title> 
  6.     <script> 
  7.         // 创建一个Socket实例 
  8.         var socket = new WebSocket('ws://localhost:9090'); 
  9.  
  10.         // 打开Socket 
  11.         socket.onopen = function(event) { 
  12.             // 发送一个初始化消息 
  13.             socket.send("init msg"); 
  14.  
  15.         }; 
  16.         socket.onmessage = function(event) { 
  17.             console.log('收到消息',event); 
  18.  
  19.         }; 
  20.  
  21.         // 监听Socket的关闭 
  22.         socket.onclose = function(event) { 
  23.             console.log('关闭监听',event); 
  24.         }; 
  25.  
  26.         function  send() 
  27.         { 
  28.             socket.send("client msg"); 
  29.         } 
  30.     </script> 
  31. </head> 
  32. <body> 
  33. <button onclick="send()">发送消息</button> 
  34. </body> 
  35. </html> 

运行测试:

Client

PHP实现WebSocket实例详解

Server

PHP实现WebSocket实例详解

Tags: WebSocket

分享到: