一、前言php
公司遊戲裏面有個簡單的聊天室,瞭解了以後才知道是node+websocket作的,想一想php也來作個簡單的聊天室。因而蒐集各類資料看文檔、找實例本身也寫了個簡單的聊天室。html
http鏈接分爲短鏈接和長鏈接。短鏈接通常能夠用ajax實現,長鏈接就是websocket。短鏈接實現起來比較簡單,可是太過於消耗資源。websocket高效不過兼容存在點問題。websocket是html5的資源前端
若是想要詳細瞭解websocket長鏈接的原理請看https://www.zhihu.com/question/20215561。html5
本文主要介紹websocket簡易聊天室的實現步驟具體部分知識點的深刻會給出連接或者麻煩讀者本身蒐集資料。node
二、前端git
前端實現websocket很簡單直接github
//鏈接websocketweb
var ws = new WebSocket("ws://127.0.0.1:8000");ajax
//成功鏈接websoc的時候算法
ws.onopen = function(){}
//成功獲取服務端輸出的消息
ws.onmessage = function(e){}
//鏈接錯誤的時候
ws.onerror = function(){}
//向服務端發送數據
ws.send();
三、後臺
websocket的難點主要在後臺
3.1websocket鏈接過程
websocket 通訊圖解 這是一個簡易的客戶端和服務端的通訊圖解,php主要就作的就是接受加密key 並返回 其中完成套接字的建立和握手操做
下圖是一張詳細的服務端處理websocket的流程圖
3.2 代碼實踐
服務端作的流程大體是:
①、掛起一個socket套接字進程等待鏈接
②、有socket鏈接以後遍歷套接字數組
③、沒有握手的進行握手操做,若是已經握手則接收數據解析並寫入緩衝區進行輸出
下面是示例代碼(我寫的是一個類因此代碼是根據函數分段的),文底給出github地址以及本身遇到的一些坑
一、首先是建立套接字
//創建套接字 public function createSocket($address,$port) { //建立一個套接字 $socket= socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //設置套接字選項 socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); //綁定IP地址和端口 socket_bind($socket,$address,$port); //監聽套接字 socket_listen($socket); return $socket; }
二、將套接字放入數組
public function __construct($address,$port) { //創建套接字 $this->soc=$this->createSocket($address,$port); $this->socs=array($this->soc); }
三、掛起進程遍歷套接字數組,主要操做都是在這裏面完成的
public function run(){ //掛起進程 while(true){ $arr=$this->socs; $write=$except=NULL; //接收套接字數字 監聽他們的狀態 socket_select($arr,$write,$except, NULL); //遍歷套接字數組 foreach($arr as $k=>$v){ //若是是新創建的套接字返回一個有效的 套接字資源 if($this->soc == $v){ $client=socket_accept($this->soc); if($client <0){ echo "socket_accept() failed"; }else{ // array_push($this->socs,$client); // unset($this[]); //將有效的套接字資源放到套接字數組 $this->socs[]=$client; } }else{ //從已鏈接的socket接收數據 返回的是從socket中接收的字節數 $byte=socket_recv($v, $buff,20480, 0); //若是接收的字節是0 if($byte<7) continue; //判斷有沒有握手沒有握手則進行握手,若是握手了 則進行處理 if(!$this->hand[(int)$client]){ //進行握手操做 $this->hands($client,$buff,$v); }else{ //處理數據操做 $mess=$this->decodeData($buff); //發送數據 $this->send($mess,$v); } } } } }
四、進行握手 流程是接收websocket內容從Sec-WebSocket-Key:中獲取key並經過加密算法寫入緩衝區客戶端會進行驗證(自動驗證不須要咱們處理)
public function hands($client,$buff,$v) { //提取websocket傳的key並進行加密 (這是固定的握手機制獲取Sec-WebSocket-Key:裏面的key) $buf = substr($buff,strpos($buff,'Sec-WebSocket-Key:')+18); //去除換行空格字符 $key = trim(substr($buf,0,strpos($buf,"\r\n"))); //固定的加密算法 $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); $new_message = "HTTP/1.1 101 Switching Protocols\r\n"; $new_message .= "Upgrade: websocket\r\n"; $new_message .= "Sec-WebSocket-Version: 13\r\n"; $new_message .= "Connection: Upgrade\r\n"; $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n"; //將套接字寫入緩衝區 socket_write($v,$new_message,strlen($new_message)); // socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0))); //標記此套接字握手成功 $this->hand[(int)$client]=true; }
五、解析客戶端的數據(我這裏沒有進行加密,若是有須要也能夠本身加密 )
//解析數據 public function decodeData($buff) { //$buff 解析數據幀 $mask = array(); $data = ''; $msg = unpack('H*',$buff); //用unpack函數從二進制將數據解碼 $head = substr($msg[1],0,2); if (hexdec($head{1}) === 8) { $data = false; }else if (hexdec($head{1}) === 1){ $mask[] = hexdec(substr($msg[1],4,2)); $mask[] = hexdec(substr($msg[1],6,2)); $mask[] = hexdec(substr($msg[1],8,2)); $mask[] = hexdec(substr($msg[1],10,2)); //遇到的問題 剛鏈接的時候就發送數據 顯示 state connecting $s = 12; $e = strlen($msg[1])-2; $n = 0; for ($i=$s; $i<= $e; $i+= 2) { $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2))); $n++; } //發送數據到客戶端 //若是長度大於125 將數據分塊 $block=str_split($data,125); $mess=array( 'mess'=>$block[0], ); return $mess; }
六、將套接字寫入緩衝區
//發送數據 public function send($mess,$v) { //遍歷套接字數組 成功握手的 進行數據羣發 foreach ($this->socs as $keys => $values) { //用系統分配的套接字資源id做爲用戶暱稱 $mess['name']="Tourist's socket:{$v}"; $str=json_encode($mess); $writes ="\x81".chr(strlen($str)).$str; // ob_flush(); // flush(); // sleep(3); if($this->hand[(int)$values]) socket_write($values,$writes,strlen($writes)); } }
七、運行方法
github地址git@github.com:rsaLive/websocket.git
①最好在控制檯運行server.php
轉到server.php腳本目錄(能夠先php -v 看下有沒有配置php若是沒有Linux配置下bash windows 配置下path)
php -f server.php
若是有錯誤會提示
②經過服務器訪問html文件
八、踩過的坑,打開調試工做方便查看錯誤
①server.php 掛起的進程中能夠打印輸出的,若是出現問題能夠在代碼中加入打印來調試
能夠在各個判斷裏面作標記在控制檯查看代碼運行在哪一個區間
不過每次修改完代碼以後須要從新運行腳本 php server.php
②
若是出現這種錯誤多是
一、在與服務器初始套接字的時候發送數據 (在第一次與服務器驗證握手的時候不能發送內容)
二、若是已經驗證過了可是客戶端沒有發送或者發送的消息爲空也會出現這樣的狀況
因此要檢驗已鏈接的套接字的數據
③可能瀏覽器不支持或者服務端沒有開啓socket開始以前最好驗證下
if (window.WebSocket){
console.log("This browser supports WebSocket!");
} else {
console.log("This browser does not support WebSocket.");
}
若有不正歡迎指出