PHP 简单实现webSocket
费话少说,用源代码说话
1)客户端实现
1 <html> 2 <head> 3 <meta charset="UTF-8"> 4 <title>Web sockets test</title> 5 <script src="jquery-min.js" type="text/javascript"></script> 6 <script type="text/javascript"> 7 var ws; 8 function ToggleConnectionClicked() { 9 try { 10 ws = new WebSocket("ws://127.0.0.1:2000");//连接服务器 11 ws.onopen = function(event){alert("已经与服务器建立了连接\r\n当前连接状态:"+this.readyState);}; 12 ws.onmessage = function(event){alert("接收到服务器发送的数据:\r\n"+event.data);}; 13 ws.onclose = function(event){alert("已经与服务器断开连接\r\n当前连接状态:"+this.readyState);}; 14 ws.onerror = function(event){alert("WebSocket异常!");}; 15 } catch (ex) { 16 alert(ex.message); 17 } 18 }; 19 20 function SendData() { 21 try{ 22 var content = document.getElementById("content").value; 23 if(content){ 24 ws.send(content); 25 } 26 27 }catch(ex){ 28 alert(ex.message); 29 } 30 }; 31 32 function seestate(){ 33 alert(ws.readyState); 34 } 35 36 </script> 37 </head> 38 <body> 39 <button id='ToggleConnection' type="button" onclick='ToggleConnectionClicked();'>连接服务器</button><br /><br /> 40 <textarea id="content" ></textarea> 41 <button id='ToggleConnection' type="button" onclick='SendData();'>发送我的名字:beston</button><br /><br /> 42 <button id='ToggleConnection' type="button" onclick='seestate();'>查看状态</button><br /><br /> 43 44 </body> 45 </html>
2)服务器端实现
1 class WS { 2 var $master; // 连接 server 的 client 3 var $sockets = array(); // 不同状态的 socket 管理 4 var $handshake = false; // 判断是否握手 5 6 function __construct($address, $port){ 7 // 建立一个 socket 套接字 8 $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) 9 or die("socket_create() failed"); 10 socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) 11 or die("socket_option() failed"); 12 socket_bind($this->master, $address, $port) 13 or die("socket_bind() failed"); 14 socket_listen($this->master, 2) 15 or die("socket_listen() failed"); 16 17 $this->sockets[] = $this->master; 18 19 // debug 20 echo("Master socket : ".$this->master."\n"); 21 22 while(true) { 23 //自动选择来消息的 socket 如果是握手 自动选择主机 24 $write = NULL; 25 $except = NULL; 26 socket_select($this->sockets, $write, $except, NULL); 27 28 foreach ($this->sockets as $socket) { 29 //连接主机的 client 30 if ($socket == $this->master){ 31 $client = socket_accept($this->master); 32 if ($client < 0) { 33 // debug 34 echo "socket_accept() failed"; 35 continue; 36 } else { 37 //connect($client); 38 array_push($this->sockets, $client); 39 echo "connect client\n"; 40 } 41 } else { 42 $bytes = @socket_recv($socket,$buffer,2048,0); 43 print_r($buffer); 44 if($bytes == 0) return; 45 if (!$this->handshake) { 46 // 如果没有握手,先握手回应 47 $this->doHandShake($socket, $buffer); 48 echo "shakeHands\n"; 49 } else { 50 51 // 如果已经握手,直接接受数据,并处理 52 $buffer = $this->decode($buffer); 53 //process($socket, $buffer); 54 echo "send file\n"; 55 } 56 } 57 } 58 } 59 } 60 61 function dohandshake($socket, $req) 62 { 63 // 获取加密key 64 $acceptKey = $this->encry($req); 65 $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" . 66 "Upgrade: websocket\r\n" . 67 "Connection: Upgrade\r\n" . 68 "Sec-WebSocket-Accept: " . $acceptKey . "\r\n" . 69 "\r\n"; 70 71 echo "dohandshake ".$upgrade.chr(0); 72 // 写入socket 73 socket_write($socket,$upgrade.chr(0), strlen($upgrade.chr(0))); 74 // 标记握手已经成功,下次接受数据采用数据帧格式 75 $this->handshake = true; 76 } 77 78 79 function encry($req) 80 { 81 $key = $this->getKey($req); 82 $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 83 84 return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); 85 } 86 87 function getKey($req) 88 { 89 $key = null; 90 if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) { 91 $key = $match[1]; 92 } 93 return $key; 94 } 95 96 // 解析数据帧 97 function decode($buffer) 98 { 99 $len = $masks = $data = $decoded = null; 100 $len = ord($buffer[1]) & 127; 101 102 if ($len === 126) { 103 $masks = substr($buffer, 4, 4); 104 $data = substr($buffer, 8); 105 } else if ($len === 127) { 106 $masks = substr($buffer, 10, 4); 107 $data = substr($buffer, 14); 108 } else { 109 $masks = substr($buffer, 2, 4); 110 $data = substr($buffer, 6); 111 } 112 for ($index = 0; $index < strlen($data); $index++) { 113 $decoded .= $data[$index] ^ $masks[$index % 4]; 114 } 115 return $decoded; 116 } 117 118 // 返回帧信息处理 119 function frame($s) 120 { 121 $a = str_split($s, 125); 122 if (count($a) == 1) { 123 return "\x81" . chr(strlen($a[0])) . $a[0]; 124 } 125 $ns = ""; 126 foreach ($a as $o) { 127 $ns .= "\x81" . chr(strlen($o)) . $o; 128 } 129 return $ns; 130 } 131 132 // 返回数据 133 function send($client, $msg) 134 { 135 $msg = $this->frame($msg); 136 socket_write($client, $msg, strlen($msg)); 137 } 138 }
测试 $ws = new WS("127.0.0.1",2000);