《PHP學(xué)習(xí):php基于websocket搭建簡易聊天室實(shí)踐》要點(diǎn):
本文介紹了PHP學(xué)習(xí):php基于websocket搭建簡易聊天室實(shí)踐,希望對您有用。如果有疑問,可以聯(lián)系我們。
本文實(shí)例講述了php基于websocket搭建簡易聊天室實(shí)踐.分享給大家供大家參考.具體如下:
1、前言PHP學(xué)習(xí)
公司游戲里面有個(gè)簡單的聊天室,了解了之后才知道是node+websocket做的,想想php也來做個(gè)簡單的聊天室.于是搜集各種資料看文檔、找實(shí)例自己也寫了個(gè)簡單的聊天室.PHP學(xué)習(xí)
http連接分為短連接和長連接.短連接一般可以用ajax實(shí)現(xiàn),長連接就是websocket.短連接實(shí)現(xiàn)起來比較簡單,但是太過于消耗資源.websocket高效不過兼容存在點(diǎn)問題.websocket是html5的資源PHP學(xué)習(xí)
2、前端PHP學(xué)習(xí)
前端實(shí)現(xiàn)websocket很簡單直接PHP學(xué)習(xí)
//連接websocket var ws = new WebSocket("ws://127.0.0.1:8000"); //成功連接websoc的時(shí)候 ws.onopen = function(){} //成功獲取服務(wù)端輸出的消息 ws.onmessage = function(e){} //連接錯(cuò)誤的時(shí)候 ws.onerror = function(){} //向服務(wù)端發(fā)送數(shù)據(jù) ws.send();
3、后臺
PHP學(xué)習(xí)
websocket的難點(diǎn)主要在后臺
PHP學(xué)習(xí)
3.1websocket連接過程
websocket 通信圖解 這是一個(gè)簡易的客戶端和服務(wù)端的通信圖解,php主要就做的就是接受加密key? 并返回 其中完成套接字的創(chuàng)建和握手操作
PHP學(xué)習(xí)
PHP學(xué)習(xí)
下圖是一張?jiān)敿?xì)的服務(wù)端處理websocket的流程圖
PHP學(xué)習(xí)
PHP學(xué)習(xí)
3.2 代碼實(shí)踐
PHP學(xué)習(xí)
服務(wù)端做的流程大致是:PHP學(xué)習(xí)
下面是示例代碼(我寫的是一個(gè)類所以代碼是根據(jù)函數(shù)分段的),文底給出github地址以及自己遇到的一些坑
?1、首先是創(chuàng)建套接字
?PHP學(xué)習(xí)
//建立套接字 public function createSocket($address,$port) { //創(chuàng)建一個(gè)套接字 $socket= socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //設(shè)置套接字選項(xiàng) socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); //綁定IP地址和端口 socket_bind($socket,$address,$port); //監(jiān)聽套接字 socket_listen($socket); return $socket; }
2、將套接字放入數(shù)組
PHP學(xué)習(xí)
public function __construct($address,$port) { //建立套接字 $this->soc=$this->createSocket($address,$port); $this->socs=array($this->soc); }
3、掛起進(jìn)程遍歷套接字?jǐn)?shù)組,主要操作都是在這里面完成的
PHP學(xué)習(xí)
public function run(){ //掛起進(jìn)程 while(true){ $arr=$this->socs; $write=$except=NULL; //接收套接字?jǐn)?shù)字 監(jiān)聽他們的狀態(tài) socket_select($arr,$write,$except, NULL); //遍歷套接字?jǐn)?shù)組 foreach($arr as $k=>$v){ //如果是新建立的套接字返回一個(gè)有效的 套接字資源 if($this->soc == $v){ $client=socket_accept($this->soc); if($client <0){ echo "socket_accept() failed"; }else{ // array_push($this->socs,$client); // unset($this[]); //將有效的套接字資源放到套接字?jǐn)?shù)組 $this->socs[]=$client; } }else{ //從已連接的socket接收數(shù)據(jù) 返回的是從socket中接收的字節(jié)數(shù) $byte=socket_recv($v, $buff,20480, 0); //如果接收的字節(jié)是0 if($byte<7) continue; //判斷有沒有握手沒有握手則進(jìn)行握手,如果握手了 則進(jìn)行處理 if(!$this->hand[(int)$client]){ //進(jìn)行握手操作 $this->hands($client,$buff,$v); }else{ //處理數(shù)據(jù)操作 $mess=$this->decodeData($buff); //發(fā)送數(shù)據(jù) $this->send($mess,$v); } } } } }
4、進(jìn)行握手 流程是接收websocket內(nèi)容從Sec-WebSocket-Key:中獲取key并通過加密算法寫入緩沖區(qū)客戶端會進(jìn)行驗(yàn)證(自動驗(yàn)證不需要我們處理)
PHP學(xué)習(xí)
public function hands($client,$buff,$v) { //提取websocket傳的key并進(jìn)行加密 (這是固定的握手機(jī)制獲取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"; //將套接字寫入緩沖區(qū) socket_write($v,$new_message,strlen($new_message)); // socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0))); //標(biāo)記此套接字握手成功 $this->hand[(int)$client]=true; }
5、解析客戶端的數(shù)據(jù)(我這里沒有進(jìn)行加密,如果有需要也可以自己加密 )
PHP學(xué)習(xí)
//解析數(shù)據(jù) public function decodeData($buff) { //$buff 解析數(shù)據(jù)幀 $mask = array(); $data = ''; $msg = unpack('H*',$buff); //用unpack函數(shù)從二進(jìn)制將數(shù)據(jù)解碼 $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)); //遇到的問題 剛連接的時(shí)候就發(fā)送數(shù)據(jù) 顯示 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++; } //發(fā)送數(shù)據(jù)到客戶端 //如果長度大于125 將數(shù)據(jù)分塊 $block=str_split($data,125); $mess=array( 'mess'=>$block[0], ); return $mess; }
6、將套接字寫入緩沖區(qū)
PHP學(xué)習(xí)
//發(fā)送數(shù)據(jù) public function send($mess,$v) { //遍歷套接字?jǐn)?shù)組 成功握手的 進(jìn)行數(shù)據(jù)群發(fā) foreach ($this->socs as $keys => $values) { //用系統(tǒng)分配的套接字資源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)); } }
7、運(yùn)行方法PHP學(xué)習(xí)
github地址git@github.com:rsaLive/websocket.gitPHP學(xué)習(xí)
①最好在控制臺運(yùn)行server.phpPHP學(xué)習(xí)
轉(zhuǎn)到server.php腳本目錄(可以先php -v 看下有沒有配置php如果沒有Linux配置下bash windows 配置下path)PHP學(xué)習(xí)
php -f server.phpPHP學(xué)習(xí)
PHP學(xué)習(xí)
如果有錯(cuò)誤會提示PHP學(xué)習(xí)
PHP學(xué)習(xí)
②通過服務(wù)器訪問html文件PHP學(xué)習(xí)
PHP學(xué)習(xí)
PHP學(xué)習(xí)
8、踩過的坑,打開調(diào)試工作方便查看錯(cuò)誤PHP學(xué)習(xí)
①server.php 掛起的進(jìn)程中可以打印輸出的,如果出現(xiàn)問題可以在代碼中加入打印來調(diào)試PHP學(xué)習(xí)
可以在各個(gè)判斷里面做標(biāo)記在控制臺查看代碼運(yùn)行在哪個(gè)區(qū)間PHP學(xué)習(xí)
不過每次修改完代碼之后需要重新運(yùn)行腳本 php server.phpPHP學(xué)習(xí)
②如果出現(xiàn)這種錯(cuò)誤可能是PHP學(xué)習(xí)
PHP學(xué)習(xí)
1、在與服務(wù)器初始套接字的時(shí)候發(fā)送數(shù)據(jù) (在第一次與服務(wù)器驗(yàn)證握手的時(shí)候不能發(fā)送內(nèi)容)PHP學(xué)習(xí)
2、如果已經(jīng)驗(yàn)證過了但是客戶端沒有發(fā)送或者發(fā)送的消息為空也會出現(xiàn)這樣的情況PHP學(xué)習(xí)
所以要檢驗(yàn)已連接的套接字的數(shù)據(jù)PHP學(xué)習(xí)
?PHP學(xué)習(xí)
③可能瀏覽器不支持或者服務(wù)端沒有開啟socket開始之前最好驗(yàn)證下PHP學(xué)習(xí)
if (window.WebSocket){ console.log("This browser supports WebSocket!"); } else { console.log("This browser does not support WebSocket."); }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持維易PHP.PHP學(xué)習(xí)
轉(zhuǎn)載請注明本頁網(wǎng)址:
http://www.snjht.com/jiaocheng/2957.html