1. 程式人生 > >php基於websocket搭建簡易聊天室(socket)

php基於websocket搭建簡易聊天室(socket)

前言

http連線分為短連線和長連線。短連線一般可以用ajax實現,長連線就是websocket。短連線實現起來比較簡單,但是太過於消耗資源。websocket高效不過相容存在點問題。websocket是html5的資源

前端

//連線socket
var ws = new Websocket(‘ws://127.0.0.1:8080’);
//成功連線socket的時候
ws.onopen = function(){}
//成功獲取服務端輸出的資訊
ws.onmessage = function(e){}
//連線錯誤的時候
ws.onerror = function(){}
//向服務端傳送資料
ws.send();

後臺,服務端

websocket 通訊圖解 這是一個簡易的客戶端和服務端的通訊圖解,php主要就做的就是接受加密key 並返回 其中完成套接字的建立和握手操作

服務端處理websocket的圖解

服務端程式碼

服務端做的流程大致是:
1,掛起一個socket套接字程序等待連線
2,有socket連線之後遍歷套接字陣列
3,沒有握手的進行握手操作,如果已經握手則接收資料解析並寫入緩衝區進行輸出

套接字

1,將套接字放入陣列
public function __contruct($address,$port){
//建立套接字
$this->soc = $

this->createSocket($address,$port);
$this->socs = array($this->soc);
}

2,建立套接字
socket_create解釋:http://www.php.net/manual/zh/function.socket-create.php
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 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));
}
}