<html> <head> <meta charset="UTF-8"> <title>Web sockets test</title> <script src="jquery-min.js" type="text/javascript"></script> <script type="text/javascript"> var ws; function ToggleConnectionClicked() { try { ws = new WebSocket("ws://127.0.0.1:2000");//鏈接服務器 ws.onopen = function(event){alert("已經與服務器創建了鏈接\r\n當前鏈接狀態:"+this.readyState);}; ws.onmessage = function(event){alert("接收到服務器發送的數據:\r\n"+event.data);}; ws.onclose = function(event){alert("已經與服務器斷開鏈接\r\n當前鏈接狀態:"+this.readyState);}; ws.onerror = function(event){alert("WebSocket異常!");}; } catch (ex) { alert(ex.message); } }; function SendData() { try{ var content = document.getElementById("content").value; if(content){ ws.send(content); } }catch(ex){ alert(ex.message); } }; function seestate(){ alert(ws.readyState); } </script> </head> <body> <button id='ToggleConnection' type="button" onclick='ToggleConnectionClicked();'>鏈接服務器</button><br /><br /> <textarea id="content" ></textarea> <button id='ToggleConnection' type="button" onclick='SendData();'>發送個人名字:beston</button><br /><br /> <button id='ToggleConnection' type="button" onclick='seestate();'>查看狀態</button><br /><br /> </body> </html>
服務器端javascript
class WS { var $master; // 鏈接 server 的 client var $sockets = array(); // 不一樣狀態的 socket 管理 var $handshake = false; // 判斷是否握手 function __construct($address, $port){ // 創建一個 socket 套接字 $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed"); socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed"); socket_bind($this->master, $address, $port) or die("socket_bind() failed"); socket_listen($this->master, 2) or die("socket_listen() failed"); $this->sockets[] = $this->master; // debug echo("Master socket : ".$this->master."\n"); while(true) { //自動選擇來消息的 socket 若是是握手 自動選擇主機 $write = NULL; $except = NULL; socket_select($this->sockets, $write, $except, NULL); foreach ($this->sockets as $socket) { //鏈接主機的 client if ($socket == $this->master){ $client = socket_accept($this->master); if ($client < 0) { // debug echo "socket_accept() failed"; continue; } else { //connect($client); array_push($this->sockets, $client); echo "connect client\n"; } } else { $bytes = @socket_recv($socket,$buffer,2048,0); print_r($buffer); if($bytes == 0) return; if (!$this->handshake) { // 若是沒有握手,先握手迴應 $this->doHandShake($socket, $buffer); echo "shakeHands\n"; } else { // 若是已經握手,直接接受數據,並處理 $buffer = $this->decode($buffer); //process($socket, $buffer); echo "send file\n"; } } } } } function dohandshake($socket, $req) { // 獲取加密key $acceptKey = $this->encry($req); $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept: " . $acceptKey . "\r\n" . "\r\n"; echo "dohandshake ".$upgrade.chr(0); // 寫入socket socket_write($socket,$upgrade.chr(0), strlen($upgrade.chr(0))); // 標記握手已經成功,下次接受數據採用數據幀格式 $this->handshake = true; } function encry($req) { $key = $this->getKey($req); $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); } function getKey($req) { $key = null; if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) { $key = $match[1]; } return $key; } // 解析數據幀 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; } // 返回幀信息處理 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; } // 返回數據 function send($client, $msg) { $msg = $this->frame($msg); socket_write($client, $msg, strlen($msg)); } } 測試 $ws = new WS("127.0.0.1",2000);