亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

Websocket協議之php實現

2020-03-22 19:33:03
字體:
來源:轉載
供稿:網友
  • 前面學習了HTML5中websocket的握手協議、打開和關閉連接等基礎內容,最近用php實現了與瀏覽器websocket的雙向通信。在學習概念的時候覺得看懂了的內容,真正在實踐過程中還是會遇到各種問題,網上也有一些關于php的websocket的實現,但是只有自己親手寫過之后才知道其中的感受。其中,google有一個開源的phpwebsocket類(https://code.google.com/p/phpwebsocket/),但是從其握手過程中可以明顯看出,這還是最初的websocket協議,請求頭中使用了兩個KEY,并非version 13(現行版本)。下面是本人實踐過程,同時封裝好了一個現行版本的php實現的實用的websocket類。

    一、握手 1、客戶端發送請求

    websocket協議提供給javascript的API就是特別簡潔易用。


    先看效果,客戶端和服務器端握手的結果如下:

    2、服務器端

    封裝的類為WebSocket,address和port為類的屬性。

    (1)建立socket并監聽
     1     function createSocket() 2     { 3         $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) 4             or die('socket_create() failed:'.socket_strerror(socket_last_error())); 5              6         socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) 7             or die('socket_option() failed'.socket_strerror(socket_last_error())); 8              9         socket_bind($this->master, $this->address, $this->port)10             or die('socket_bind() failed'.socket_strerror(socket_last_error()));11             12         socket_listen($this->master,20)13             or die('socket_listen() failed'.socket_strerror(socket_last_error()));14         15         $this->say('Server Started : '.date('Y-m-d H:i:s'));16         $this->say('Master socket  : '.$this->master);17         $this->say('Listening on   : '.$this->address.' port '.$this->port.'');18         19     }

    然后啟動監聽,同時要維護連接到服務器的用戶的一個數組(連接池),每連接一個用戶,就要push進一個,同時關閉連接后要刪除相應的用戶的連接。

     1     html' target='_blank'>public function __construct($a, $p) 2     { 3         if ($a == 'localhost') 4             $this->address = $a; 5         else if (preg_match('/^[d.]*$/is', $a)) 6             $this->address = long2ip(ip2long($a)); 7         else 8             $this->address = $p; 9         10         if (is_numeric($p) && intval($p) > 1024 && intval($p) < 65536)11             $this->port = $p;12         else13             die ('Not valid port:' . $p);14         15         $this->createSocket();16         array_push($this->sockets, $this->master);17     }
    (2)建立連接

    維護用戶的連接池

    1     public function connect($clientSocket)2     {3         $user = new User();4         $user->id = uniqid();5         $user->socket = $clientSocket;6         array_push($this->users,$user);7         array_push($this->sockets,$clientSocket);8         $this->log($user->socket . ' CONNECTED!' . date('Y-m-d H-i-s'));9     }
    (3)回復響應頭

    首先要獲取請求頭,從中取出Sec-Websocket-Key,同時還應該取出Host、請求方式、Origin等,可以進行安全檢查,防止未知的連接。

     1     public function getHeaders($req) 2     { 3         $r = $h = $o = null; 4         if(preg_match('/GET (.*) HTTP/'   , $req, $match)) 5             $r = $match[1]; 6         if(preg_match('/Host: (.*)/'  , $req, $match)) 7             $h = $match[1]; 8         if(preg_match('/Origin: (.*)/', $req, $match)) 9             $o = $match[1];10         if(preg_match('/Sec-WebSocket-Key: (.*)/', $req, $match))11             $key = $match[1];12             13         return array($r, $h, $o, $key);14     }

    之后是得到key然后進行websocket協議規定的加密算法進行計算,返回響應頭,這樣瀏覽器驗證正確后就握手成功了。這里涉及的詳細解析信息過程參見另一篇博文http://blog.csdn.net/u010487568/article/details/20569027

     1     protected function wrap($msg='', $opcode = 0x1) 2     { 3         //默認控制幀為0x1(文本數據) 4         $firstByte = 0x80 | $opcode; 5         $encodedata = null; 6         $len = strlen($msg); 7          8         if (0 <= $len && $len <= 125) 9             $encodedata = chr(0x81) . chr($len) . $msg;10         else if (126 <= $len && $len <= 0xFFFF)11         {12             $low = $len & 0x00FF;13             $high = ($len & 0xFF00) >> 8;14             $encodedata = chr($firstByte) . chr(0x7E) . chr($high) . chr($low) . $msg;15         }16         17         return $encodedata;            18     }

    其中我只實現了發送數據長度在2的16次方以下個字符的情況,至于長度為8個字節的超大數據暫未考慮。

     1      private function doHandShake($user, $buffer) 2      { 3         $this->log('Requesting handshake...'); 4         $this->log($buffer); 5         list($resource, $host, $origin, $key) = $this->getHeaders($buffer); 6          7         //websocket version 13 8         $acceptKey = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); 9         10         $this->log('Handshaking...');11         $upgrade  = 'HTTP/1.1 101 Switching Protocol' .12                     'Upgrade: websocket' .13                     'Connection: Upgrade' .14                     'Sec-WebSocket-Accept: ' . $acceptKey . '';  //必須以兩個回車結尾15         $this->log($upgrade);16         $sent = socket_write($user->socket, $upgrade, strlen($upgrade));17         $user->handshake=true;18         $this->log('Done handshaking...');19         return true;20     }
    二、數據傳輸 1、客戶端

    客戶端websocket的API非常容易,直接使用websocket對象的send方法即可。

    1 ws.send(message);
    2、服務器端

    客戶端發送的數據是經過瀏覽器支持的websocket進行了mask處理的,而根據規定服務器端返回的數據不能進行掩碼處理,但是需要按照協議的數據幀規定進行封裝后發送。因此服務器需要接收數據必須將接收到的字節流進行解碼。

     1     protected function unwrap($clientSocket, $msg='') 2     {  3         $opcode = ord(substr($msg, 0, 1)) & 0x0F; 4         $payloadlen = ord(substr($msg, 1, 1)) & 0x7F; 5         $ismask = (ord(substr($msg, 1, 1)) & 0x80) >> 7; 6         $maskkey = null; 7         $oridata = null; 8         $decodedata = null; 9         10         //關閉連接11         if ($ismask != 1 || $opcode == 0x8)12         {13             $this->disconnect($clientSocket);14             return null;15         }16         17         //獲取掩碼密鑰和原始數據18         if ($payloadlen <= 125 && $payloadlen >= 0)19         {20             $maskkey = substr($msg, 2, 4);21             $oridata = substr($msg, 6);22         }23         else if ($payloadlen == 126)24         {25             $maskkey = substr($msg, 4, 4);26             $oridata = substr($msg, 8);27         }28         else if ($payloadlen == 127)29         {30             $maskkey = substr($msg, 10, 4);31             $oridata = substr($msg, 14);32         }33         $len = strlen($oridata);34         for($i = 0; $i < $len; $i++)35         {36             $decodedata .= $oridata[$i] ^ $maskkey[$i % 4];37         }        38         return $decodedata; 39     }

    其中得到掩碼和控制幀后需要進行驗證,如果掩碼不為1直接關閉,如果控制幀為8也直接關閉。后面的原始數據和掩碼獲取是通過websocket協議的數據幀規范進行的。

    效果如下



    數據交互的過程非常的直接,其中“u”是服務器發送給客戶端的,然后客戶端發送一段隨機字符串給服務器。

    三、連接關閉 1、客戶端
    1 ws.close();
    2、服務器端

    需要將維護的用戶連接池移除相應的連接用戶。

     1     public function disconnect($clientSocket) 2     { 3         $found = null; 4         $n = count($this->users); 5         for($i = 0; $i<$n; $i++) 6         { 7             if($this->users[$i]->socket == $clientSocket) 8             {  9                 $found = $i;10                 break;11             }12         }13         $index = array_search($clientSocket,$this->sockets);14         15         if(!is_null($found))16         { 17             array_splice($this->users, $found, 1);18             array_splice($this->sockets, $index, 1); 19             20             socket_close($clientSocket);21             $this->say($clientSocket.' DISCONNECTED!');22         }23     }

    其中遇到的一個問題就是,如果將上述函數中的socket_close語句提出到if語句外面的時候,當瀏覽器連接到服務器后,F5刷新頁面后會發現出錯:

    后來發現是重復關閉socket了,這個是因為在unwrap函數中遇到了控制幀直接關閉的原因。因此需要注意瀏覽器已經連接后進行刷新的操作。最后提供整個封裝好的類,https://github.com/OshynSong/web/blob/master/websocket.class.php

    PHP編程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美激情第三页| 国产精品扒开腿做爽爽爽视频| 久久久久久久久久久人体| 欧美韩国理论所午夜片917电影| 亚洲最大福利网| 国产精品一区二区三区成人| 亚洲天堂网站在线观看视频| 国语自产精品视频在免费| 国产免费一区二区三区在线观看| 亚洲aⅴ男人的天堂在线观看| 国产一区二区三区四区福利| 深夜福利日韩在线看| 亚洲精品按摩视频| 成人免费网站在线| 久久精品这里热有精品| 91成品人片a无限观看| 亚洲va码欧洲m码| www.日韩系列| 亚洲国内精品视频| 黑人狂躁日本妞一区二区三区| 91精品国产自产在线| 成人免费视频在线观看超级碰| 国产福利精品视频| 日本精品va在线观看| 日韩av网址在线| 91视频国产精品| 欧美高清视频一区二区| 中文字幕久久久| 中文字幕在线观看日韩| 欧美激情国产日韩精品一区18| 91亚洲精品久久久| 国产成人精品亚洲精品| 亚洲福利精品在线| 国产日韩欧美电影在线观看| 日韩精品在线视频| 国产精品久久久久久久av电影| 国产精品美女www爽爽爽视频| 亚洲一区二区少妇| 日韩免费在线观看视频| 中文字幕精品一区久久久久| 亚洲最大av在线| 日韩中文字幕欧美| 精品国产精品三级精品av网址| 欧美巨大黑人极品精男| 69久久夜色精品国产7777| 欧美一级片久久久久久久| 精品国偷自产在线视频| 91亚洲精品久久久久久久久久久久| 欧美福利小视频| 成人免费自拍视频| 欧美有码在线视频| 日韩av网址在线观看| 欧美日韩亚洲一区二| 96国产粉嫩美女| 亚洲精品98久久久久久中文字幕| 日韩av黄色在线观看| 欧美多人乱p欧美4p久久| 欧美俄罗斯乱妇| 亚洲一区二区福利| 97精品在线视频| 亚洲经典中文字幕| 欧美激情成人在线视频| 91免费看片在线| 精品国内亚洲在观看18黄| 日韩不卡在线观看| 亚洲aa在线观看| 九九热精品视频国产| 亚洲天堂av在线播放| 欧美日韩视频免费播放| 97超碰蝌蚪网人人做人人爽| 国产亚洲视频中文字幕视频| 色综合男人天堂| 亚洲精品自在久久| 日本精品视频网站| 亚洲欧美日韩高清| 国产精品专区一| 久久久亚洲天堂| 亚洲精品视频播放| 国产精品va在线播放我和闺蜜| 欧美激情第一页xxx| 热99久久精品| 日韩精品欧美激情| 久久精品青青大伊人av| 国产一区二区黄| 疯狂蹂躏欧美一区二区精品| 91国偷自产一区二区三区的观看方式| 亚洲成人三级在线| 91久久久久久久久| 亚洲成色www8888| 91国内产香蕉| 亚洲成人网久久久| 久久精品91久久久久久再现| 日韩免费高清在线观看| 中国china体内裑精亚洲片| 久久亚洲精品国产亚洲老地址| 在线观看亚洲区| 亚洲精品影视在线观看| 日韩中文字幕免费| 欧美激情a∨在线视频播放| 国产精品第二页| 国产美女直播视频一区| 国产精品美女主播| 国产精品视频午夜| 欧美精品久久一区二区| 一色桃子一区二区| 91精品久久久久久久久久久| 日韩美女av在线免费观看| 国产成人极品视频| 亚洲精选一区二区| 欧美专区国产专区| 国产成人精品在线视频| 9.1国产丝袜在线观看| 亚洲va电影大全| 日韩黄色在线免费观看| 国产美女被下药99| 青青久久av北条麻妃黑人| 少妇高潮久久久久久潘金莲| 国产精品影片在线观看| 国产精品96久久久久久| 亚洲网站在线播放| 国产一区二区三区在线播放免费观看| 国产精品一区二区电影| 久久中国妇女中文字幕| 国产欧美精品在线播放| 午夜精品蜜臀一区二区三区免费| 亚洲男人天堂九九视频| 久久深夜福利免费观看| 国产精品视频久久久久| 清纯唯美日韩制服另类| 97**国产露脸精品国产| 欧美高清视频在线播放| 欧美午夜精品伦理| 国产精品美女久久久免费| 久久久久久久av| 尤物yw午夜国产精品视频| 日本精品久久中文字幕佐佐木| 热re91久久精品国99热蜜臀| 成人精品一区二区三区| 亚洲欧美另类在线观看| 欧美精品第一页在线播放| 国产一区二区三区三区在线观看| 欧美性极品xxxx娇小| 亚洲一区二区中文字幕| 亚洲最大的成人网| 欧美一级高清免费播放| 亚洲第一精品久久忘忧草社区| 久久久久久久影视| 国产精品久久精品| 精品久久久久久中文字幕大豆网| 欧美成人精品一区二区三区| 亚洲v日韩v综合v精品v| 久久综合五月天| 日韩高清欧美高清| 亚洲淫片在线视频| 欧美专区第一页| 久久青草福利网站| 国产午夜精品全部视频播放| 懂色av影视一区二区三区| 欧美大秀在线观看| 在线观看国产成人av片| 亚洲人成电影网| 国产999精品久久久影片官网| 亚洲精品一区二区三区婷婷月| 欧美一级大片视频|