數據從瀏覽器通過websocket發送給服務器的數據,是原始的幀數據,默認是被掩碼處理過的,所以需要對其利用掩碼進行解碼。
從服務器發送給瀏覽器的數據是默認沒有掩碼處理的,只要符合一定結構就可以了。具體可以參考websocket的RFC文檔
http://www.rfcreader.com/#rfc6455
The threat model being protected against is one in which the client sends data that appears to be an HTTP request. As such, the channel that needs to be masked is the data from the client to the server. The data from the server to the client can be made to look like a response, but to accomplish this request, the client must also be able to forge a request. As such, it was not deemed necessary to mask data in both directions (the data from the server to the client is not masked)
解碼函數
function decode($received) { $len = $masks = $data = $decoded = null; $buffer = $received; $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; }
編碼函數:
1 function encode($buffer) 2 { 3 $len = strlen($buffer); 4 5 $first_byte = self::BINARY_TYPE_BLOB; 6 7 if ($len <= 125) { 8 $encode_buffer = $first_byte . chr($len) . $buffer; 9 } else { 10 if ($len <= 65535) { 11 $encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer; 12 } else { 13 //pack("xxxN", $len)pack函數只處理2的32次方大小的文件,實際上2的32次方已經4G了。 14 $encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer; 15 } 16 } 17 18 return $encode_buffer; 19 } 20 }
服務器端代碼:
1 <?php 2 3 class WebSocket 4 { 5 const BINARY_TYPE_BLOB = "\x81"; 6 private $socket; 7 8 public function __construct($port) 9 { 10 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); 11 socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); 12 socket_bind($socket, 0, $port); 13 socket_listen($socket); 14 return $this ->socket = $socket; 15 } 16 17 public function run() 18 { 19 $clients[] = $this -> socket; 20 while(true) 21 { 22 $read = $clients; 23 $write = null; 24 $except = null; 25 if(false === socket_select($read, $write, $except, null)) 26 { 27 continue; 28 } 29 30 if(in_array($this -> socket, $read)) 31 { 32 echo "new request ...\n"; 33 $clients[] = $newsock = socket_accept($this -> socket); 34 $request = socket_read($newsock, 321600*2); 35 if($this -> handshake($newsock, $request)) 36 { 37 socket_getpeername($newsock, $ip); 38 echo "new client $ip conntected\n"; 39 $key = array_search($this -> socket, $read); 40 unset($read[$key]); 41 } 42 else 43 { 44 echo "handshake failed \n"; 45 } 46 } 47 48 foreach ($read as $sock_read) 49 { 50 $data = socket_read($sock_read, 32160*2); 51 if(false === $data) 52 { 53 $key = array_search($sock_read, $clients); 54 socket_getpeername($clients[$key], $ip); 55 unset($clients[$key]); 56 echo "client $ip disconnected\n"; 57 continue; 58 } 59 $data = $this -> decode($data); 60 $response = "recevied data len : ".strlen($data)." $data"; 61 $response = $this -> encode($response); 62 socket_write($sock_read, $response, strlen($response)); 63 } 64 } 65 socket_close($this -> socket); 66 } 67 68 public function handshake($socket, $request) 69 { 70 if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $request, $match)) { 71 $Sec_WebSocket_Key = $match[1]; 72 } 73 // Calculation websocket key. 74 $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)); 75 // Handshake response data. 76 $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n"; 77 $handshake_message .= "Upgrade: websocket\r\n"; 78 $handshake_message .= "Sec-WebSocket-Version: 13\r\n"; 79 $handshake_message .= "Connection: Upgrade\r\n"; 80 $handshake_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n"; 81 82 socket_write($socket, $handshake_message); 83 84 return true; 85 } 86 87 public function decode($received) 88 { 89 $len = $masks = $data = $decoded = null; 90 $buffer = $received; 91 $len = ord($buffer[1]) & 127; 92 if ($len === 126) { 93 $masks = substr($buffer, 4, 4); 94 $data = substr($buffer, 8); 95 } else { 96 if ($len === 127) { 97 $masks = substr($buffer, 10, 4); 98 $data = substr($buffer, 14); 99 } else { 100 $masks = substr($buffer, 2, 4); 101 $data = substr($buffer, 6); 102 } 103 } 104 for ($index = 0; $index < strlen($data); $index++) { 105 $decoded .= $data[$index] ^ $masks[$index % 4]; 106 } 107 108 return $decoded; 109 } 110 111 public function encode($buffer) 112 { 113 $len = strlen($buffer); 114 115 $first_byte = self::BINARY_TYPE_BLOB; 116 117 if ($len <= 125) { 118 $encode_buffer = $first_byte . chr($len) . $buffer; 119 } else { 120 if ($len <= 65535) { 121 $encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer; 122 } else { 123 //pack("xxxN", $len)pack函數只處理2的32次方大小的文件,實際上2的32次方已經4G了。 124 $encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer; 125 } 126 } 127 128 return $encode_buffer; 129 } 130 } 131 132 $ws = new WebSocket(1313); 133 $ws -> run();
客戶端代碼:
1 $(function(){ 2 url = "ws://localhost:1313/server.php"; 3 socket = new WebSocket(url); 4 cnt = 1; 5 6 socket.onopen = function() 7 { 8 socket.send("this is chrome broswer"); 9 } 10 11 socket.onmessage = function(msg) 12 { 13 $('#info').append(msg.data); 14 } 15 });
效果