php使用WebSocket詳細教程之建立連線(一)
阿新 • • 發佈:2020-07-14
本次教程需要理解的內容:
- 什麼是WebSocket?
- WebSocket可以用來幹什麼?
- 什麼是WebSocket握手?
- php使用WebSocket的流程?
- php中WebSocket相關函式的作用?
(一)什麼是WebSocket?
WebSocket是一種在單個TCP連線上進行全雙工通訊的協議。WebSocket通訊協議於2011年被IETF定為標準RFC 6455,並由RFC7936補充規範。WebSocket API也被W3C定為標準。WebSocket使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。
(二)WebSocket的作用?
WebSock其實在平常使用,我們是時常見到的,用於實時通訊,例如我們常用的實時聊天、服務端向客戶端訊息推送、也可以實現踢使用者下線功能。實時彈幕功能等等。
(三)什麼是握手?
為了建立Websocket連線,需要通過瀏覽器發出請求,之後伺服器進行迴應,這個過程通常稱為“握手”(handshaking)。
這是比較正式的理解,在接下來使用方式中會在介紹到握手的實際含義。
(四)php使用WebSocket的流程及相關函式的意義
這裡程式碼註釋都會進行逐一解釋,所以就直接上程式碼,有什麼不懂歡迎提出來。
<?php //設定應該報告何種 PHP 錯誤 error_reporting(E_ALL^E_NOTICE); //設定指令碼最大執行時間,0則為不限制 set_time_limit(0); //開啟或關閉絕對(隱式)刷送 ob_implicit_flush(); //設定建立socket伺服器的ip $address="127.0.0.1"; //設定socket監聽的埠 $port=10000; //socket的resource,即前期初始化socket時返回的socket資源 $master; //用來儲存連線進來的使用者資訊的陣列 $users; //socket的連線池,即client連線進來的socket標誌,一個數組 $sockets; /** * 以下socket_?()方法都為建立一個socket必須的,且順序不能亂,缺一不可 */ //建立一個socket $master=socket_create(AF_INET,SOCK_STREAM,SOL_TCP); //設定socket選項,1表示接受所有的資料包 socket_set_option($master,SOL_SOCKET,SO_REUSEADDR,1); //繫結socket到指定ip與埠 socket_bind($master,$address,$port); //監聽已連線的socket socket_listen($master); //初始化sockets連線池 $sockets=array($master); //對一些必要資訊的輸出記錄 echo "socket已連線,時間:".date("Y-m-d H:i:s")."\n"; echo "監聽中:".$address.":".$port."\n"; //設定迴圈使指令碼持續執行處理訊息 while(true){ //用來檢測是否有變化的陣列(就是有新訊息到或者有客戶端連線/斷開) $changes=$sockets; $write=NULL; $except=NULL; /** * 對於個人理解,這個函式的作用為阻塞程式往下執行,它會不停的檢驗$changes是否有變化,沒有變化就將阻斷程式往下執行。 * 只有出現$changes出現變化(有新訊息到或者有客戶端連線/斷開)才會對繼續執行程式。 * 很重要的一個函式。有的說法是同時接受多個連線的關鍵 * @param array $write是監聽是否有客戶端寫資料,傳入NULL是不關心是否有寫變化。 * @param array $except是$sockets裡面要被排除的元素,傳入NULL是”監聽”全部。 * @param int 最後一個引數是超時時間 * 如果為0:則立即結束 * 如果為n>1: 則最多在n秒後結束,如遇某一個連線有新動態,則提前返回 * 如果為null:如遇某一個連線有新動態,則返回 */ socket_select($changes,$write,$except,NULL); //這裡遍歷檢測出出現何種變化,然後進行處理 foreach($changes as $sock){ //當下列條件的滿足時,表示有新使用者連線進來 if($sock==$master){ //接受該使用者的連線 $client=socket_accept($master); //給這個使用者生成一個獨一無二的id,用與獲取該使用者的資訊的唯一標識。 $key=uniqid(); //將新使用者存入socke連線池 $sockets[]=$client; //記錄使用者連線的資訊,為了方便能對指定使用者傳送訊息。其中handshake代表伺服器與客戶端握手與否,socket的另外一個重要的操作 $users[$key]=array( "socket"=>$client, "handshake"=>false, ); echo "分配id為".$key."的使用者連線\n"; } // 剩下的為使用者斷開連線或者使用者向服務端傳送資訊 else{ $len=0;//收到資料的長度 $buffer='';//收到的資料 /** * socket_recv( resource $socket, string &$buf, int $len, int $flags) : int * 函式 socket_recv() 從 socket 中接受長度為 最大為$len 位元組的資料,並儲存在 buf 中,$l返回的為實際讀取資料的長度。 * socket_recv() 用於從已連線的socket中接收資料。除此之外,可以設定一個或多個 flags 來控制函式的具體行為。 */ //通過迴圈的方式讀取全部資料$len可根據自身設定 do{ $l=socket_recv($sock,$buf,1000,0); $len+=$l; $buffer.=$buf; }while($l==1000); $tmpk;//獲取操作使用者的key,即一開始分配的唯一標識id foreach($users as $k=>$v){//$k為鍵,$v為值 if($sock==$v['socket']){ //獲取連線的使用者陣列users,當users裡存在有隻返回該使用者被分配的唯一id $tmpk=$k; } } // 如果資料長度小於7為斷開連線 if($len<7){ socket_close($users[$k]['socket']);//關閉該使用者連線,可以寫成socket_close($sock),這種寫法是封裝後的寫法,為了容易看懂不進行封裝; unset($users[$tmpk]);//銷燬指定的users的某個使用者資訊 $sockets=array($master);//可以理解為初始化sockets連線池 //遍歷users陣列,將連線的資訊存入$sockets中 foreach($users as $v){ $sockets[]=$v['socket']; } echo "id為".$tmpk."使用者斷開連線\n"; continue; } //服務端與使用者握手 //如果沒有與客戶端握手,資料交換都會錯誤。 //一旦伺服器傳送了以下標頭檔案,握手就完成了,我們就可以交換資料了,可以理解為檢驗身份差不多的意思 if(!$users[$tmpk]['handshake']){ //擷取客戶端請求時傳送給服務端Sec-WebSocket-Key的值並加密,其中$key後面的一部分258EAFA5-E914-47DA-95CA-C5AB0DC85B11字串應該是固定的 $buf = substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+18); $key=trim(substr($buf,0,strpos($buf,"\r\n")));//前兩步可以直接替換為trim(substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+16)) $new_key=base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); //向客戶端返回該資訊,也就是所說的握手。 $hand_message="HTTP/1.1 101 Switching Protocols\r\n" ."Upgrade: websocket\r\n" ."Sec-Websocket-Version: 13\r\n" ."Connection: Upgrade\r\n" ."Sec-Websocket-Accept: ".$new_key."\r\n\r\n"; /** * writes to the socket from the given buffer * 向指定的socket傳送資訊 * 這裡向用戶傳送握手資訊 */ $status=socket_write($users[$tmpk]['socket'],$hand_message,strlen($hand_message)); if($status){ echo "與使用者id".$tmpk."握手成功\n"; echo $hand_message."\n"; } } // 最後剩下的就為使用者傳送訊息,做接收操作,由於需要包含二進位制資料的轉換,需瞭解websocket的資料收發協議,下一篇將更新接下來資料的處理 else{ //接收資料處理操作 } } } } ?>
結語:由於接下來資料的接收與傳送,會涉及到資料的解碼與編碼,下一篇內容將會介紹資料的傳送與接收,對各個操作都詳細的解釋。
自己學習過程中沒看到叫詳細的教程,就寫個專題關於WebSocket的使用,當然也可以使用workman等開源通訊框架,少去很多麻煩,在這裡也是為了構造自己的通訊方式,自己編寫。
————————————————
原文連結:https://blog.csdn.net/Vae_sun/article/details/90318326