この記事では主に PHP での WebSocket の使用例を紹介します。必要な方は参考にしてください。以下に、クライアントとサーバーの間で WebSocket 接続を確立するときのハンドシェイク部分を示す図を描きました。この部分は、ノードによって提供されるネット モジュールがこの時点で既にカプセル化されているため、ノード内で非常に簡単に完了できます。接続の確立を考慮せずに、データの相互作用のみを考慮する必要があります。ただし、PHPはそうではなく、ソケットの接続、確立、バインディング、監視などを自分で操作する必要があるため、取り出して話す必要があります。 ①と②は実際にはHTTPリクエストとレスポンスですが、処理中に取得するのは解析されていない文字列です。のように: コードは以下のように表示されます: GET /チャット HTTP/1.1 ホスト:server.example.com 出典: http://www.jb51.com 私たちが通常目にするリクエストは次のようになります。これがサーバーに到達すると、いくつかのコード ライブラリを通じてこの情報を直接取得できます。 1.phpでWebSocketを処理する WebSocket 接続はクライアントによってアクティブに開始されるため、すべてはクライアントから開始する必要があります。最初のステップは、クライアントから送信された Sec-WebSocket-Key 文字列を解析することです。 コードは以下のように表示されます: GET /チャット HTTP/1.1 ホスト:server.example.com アップグレード: Webソケット 接続: アップグレード Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== 出典: http://www.jb51.com Sec-WebSocket-Protocol: チャット、スーパーチャット Sec-WebSocket-バージョン: 13 クライアントリクエストフォーマット まず、php はソケット接続を確立し、ポート情報をリッスンします。 1. ソケット接続の確立 ソケットの確立については、大学でコンピュータ ネットワークを学んだことがある方はご存知かと思いますが、次のような接続の確立プロセスがあります。 コードは以下のように表示されます: // ソケットソケットを作成します $master = ソケット_作成(AF_INET, SOCK_STREAM, SOL_TCP); ソケットセットオプション($master, SOL_SOCKET, SO_REUSEADDR, 1); ソケットバインド($マスター、$アドレス、$ポート); ソケット_リッスン($マスター); ここの処理はnodeに比べて非常に面倒です。上記のコードは接続を確立するものではありませんが、ソケットを確立するために書かなければならないコードです。処理プロセスが少し複雑なので、管理や呼び出しを容易にするために、さまざまな処理をクラスに記述しました。 コードは以下のように表示されます: //デモ.php クラス WS { var $master // クライアントがサーバーに接続します。 var $sockets = array(); // さまざまな状態のソケット管理 var $handshake = false // 握手をするかどうかを決定します。 関数 __construct($address, $port){ //ソケットソケットを作成する $this->master =socket_create(AF_INET, SOCK_STREAM, SOL_TCP) または die("socket_create() failed"); socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) または die("socket_option() failed"); ソケットバインド($this->マスター, $アドレス, $ポート) または die("socket_bind() failed"); socket_listen($this->master, 2) または die("socket_listen() failed"); $this->sockets[] = $this->master; // デバッグ echo("マスターソケット: ".$this->master."n"); while(true) { // 受信メッセージのソケットを自動的に選択します。ハンドシェイクの場合は、ホストを自動的に選択します。 $write = NULL; $excel = NULL; ソケット_select($this->ソケット, $write, $excel, NULL); foreach ($this->sockets as $socket) { //ホストに接続するクライアント if ($socket == $this->master){ $client =ソケット_accept($this->master); if ($client sockets, $client); echo "Connect Clientn"; } それ以外 { $bytes = @socket_recv($socket,$buffer,2048,0); if (!$this->ハンドシェイク) { // 握手をしない場合は、まず握手をして応答してください //doHandShake($socket, $buffer); エコー「ShakeHandsn」; } それ以外 { に $buffer = decode($buffer); //process($socket, $buffer); エコー「Filenを送信」; } } } } } } 上記のコードは私がデバッグしたもので、大きな問題はありません。テストしたい場合は、cmd コマンド ラインに php /path/to/demo.php と入力できます。もちろん、上記は単なるクラスです。テストしたい場合は、新しいインスタンスを作成する必要があります。 コードは以下のように表示されます。 $ws = 新しい WS('localhost', 4000); クライアント コードはもう少し単純になります。 コードは以下のように表示されます。 var ws = 新しい WebSocket("ws://localhost:4000"); ws.onopen = function(){ console.log("ハンドシェイクが成功しました"); }; ws.onerror = function(){ console.log("エラー"); }; サーバー コードを実行すると、クライアントが接続すると、以下が表示されます。 2. Sec-WebSocket-Key 情報を抽出する コードは以下のように表示されます。 関数 getKey($req) { $key = null; if (preg_match("/Sec-WebSocket-Key: (.*)rn/", $req, $match)) { $key = $match[1]; } $key を返します。 } ここでは比較的単純です。WebSocket 情報ヘッダーには Sec-WebSocket-Key が含まれている必要があるため、すぐに照合できます。 3. 暗号化 Sec-WebSocket-Key コードは以下のように表示されます。 関数 encry($req){ $key = $this->getKey($req); $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; returnbase64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); } SHA-1 で暗号化された文字列は、base64 で暗号化されます。暗号化アルゴリズムが間違っている場合、クライアントは以下のチェック時にエラーを直接報告します。 4. Sec-WebSocket-Accept を返信します コードは以下のように表示されます。 関数 dohandshake($socket, $req){ // 暗号化キーを取得する $acceptKey = $this->encry($req); $upgrade = "HTTP/1.1 101 スイッチング プロトコル" 。 「アップグレード: websocketrn」 。 「接続: アップグレード」 。 "Sec-WebSocket-Accept: " . $acceptKey . " . "rn"; //ソケットへの書き込み socket_write(ソケット,$upgrade.chr(0), strlen($upgrade.chr(0))); // ハンドシェイクが成功したことをマークします。次回データが受け入れられるとき、データはデータ フレーム形式になります。 $this->ハンドシェイク = true; } ここで注意しなければならないのは、すべてのリクエストと対応する形式の最後に空白行があることです。これは、テストを開始したときに失われ、長い間苦労しました。 クライアントがキーのチェックに成功すると、onopen 関数がトリガーされます。 5. データフレーム処理 コードは以下のように表示されます。 // データフレームを解析します 関数デコード($buffer) { $len = $masks = $data = $decoded = null; $len = ord($buffer[1]) & 127; if ($len === 126) { $マスク = substr($buffer, 4, 4); $data = substr($buffer, 8); } else if ($len === 127) { $マスク = substr($buffer, 10, 4); $data = substr($buffer, 14); } それ以外 { $マスク = substr($buffer, 2, 4); $data = substr($buffer, 6); } for ($index = 0; $index frame($msg); ソケット書き込み($client, $msg, strlen($msg)); } クライアントコード: コードは以下のように表示されます: var ws = 新しい WebSocket("ws://localhost:4000"); ws.onopen = function(){ console.log("ハンドシェイクが成功しました"); }; ws.onmessage = function(e){ console.log("メッセージ:" + e.data); }; ws.onerror = function(){ console.log("エラー"); }; ws.send("李静"); 接続後にデータを送信すると、サーバーは次のように戻ります。 2. 問題に注意する 1.WebSocketのバージョンの問題 ハンドシェイク中のクライアントのリクエストには、バージョン識別子である Sec-WebSocket-Version: 13 が含まれており、現在のすべてのブラウザはこのバージョンを使用します。以前のバージョンでは、データ暗号化の部分でさらに問題が発生し、次の 2 つのキーが送信されました。 コードは以下のように表示されます: GET /チャット HTTP/1.1 ホスト:server.example.com アップグレード: Webソケット 接続: アップグレード 出典: http://www.jb51.net Sec-WebSocket-Protocol: チャット、スーパーチャット Sec-WebSocket-Key1: xxxx Sec-WebSocket-Key2: xxxx このバージョン(古いため使用されなくなったバージョン)の場合は、次の方法で入手する必要があります コードは以下のように表示されます: function encry($key1,$key2,$l8b){ //数値を取得します preg_match_all('/([d]+)/', $key1, $key1_num); 、$key2、$key2_num); $key1_num = 爆破($key1_num[0]); $key2_num = implode($key2_num[0]); //スペースを数える preg_match_all('/([ ]+)/', $key1, $key1_spc); preg_match_all('/([ ]+)/', $key2, $key2_spc); if($key1_spc==0|$key2_spc==0){ $this->log("無効なキー");return; // いくつかの数学 $key1_sec = Pack("N",$key1_num / $key1_spc); $key2_sec = Pack("N",$key2_num / $key2_spc); return md5($key1_sec.$key2_sec.$l8b,1); } この検証方法については文句を言うとキリがないです! nodeJsのWebSocket操作方法と比較すると以下のようになります。 コードは以下のように表示されます: //サーバープログラム var crypto = require('crypto'); var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var キー; o.on('データ',function(e){ if(!キー){ //握手をする key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key = crypto.createHash('sha1').update(key + WS).digest('base64'); o.write('HTTP/1.1 101 スイッチング プロトコル'); o.write('アップグレード: websocketrn'); o.write('接続: アップグレード'); o.write('Sec-WebSocket-Accept: ' + key + 'rn'); o.write('rn'); }それ以外{ コンソール.ログ(e); }; }); }).listen(8000); 2. データフレーム解析コード この記事では、decodeFrame などのデータ フレーム解析コードは提供しません。データ フレームの形式は、純粋に物理的な作業です。