Copyright (C) 2012 メモリー ---------------------------------------- チェンジログ: 2013/5/24 Fixed * 128以上65535以下の文字数の場合正常に送れない問題を修正しました。 2012/8/31 Fixed * Version 2.1.1 リリース * Google Chromeで正常に送受信ができない問題を修正 2012/3/22 Fixed * WebSocketEventクラスがNamespaceでextendsされている場合、 WebSocketServer::registerEvent()が失敗するバグを修正しました。 2012/3/18 Added * wssプロトコルに対応しました。 * WebSocketServer::isSecure ()を追加 * WebSocketServer::setSSLDirectoryPath ($path)を追加 * WebSocketServer::getSSLDirectoryPath ()を追加 * WebSocketClient::$lastAccessを追加 * デスクトップ配信サンプルを追加 Notice * wssプロトコルの問題については後述します。 Fixed * クラスインスタンスをイベントとして設定した場合、$message引数などが渡されない問題を修正しました。 * 例外が発生するとNotice Error: Undefined Index displayExceptionがコンソールに出力される問題を修正 (Exceptionsのsの付け忘れです…) 2012/3/16 Added * Version 2.0.1 リリース * デフォルトの設定でログが表示されるようになりました。 * WebSocketクラスからWebSocketServerクラスに改名。 * WebSocketServer::setListenInterval($interval)を追加 * WebSocketServer::setDisplayLog($bool)を追加 * WebSocketClient::$resourceQueryを追加 * WebSocketEvent, IWebSocketEventクラスを追加しました。 * WebSocketServer::registerEventの第一引数にイベントクラスを渡すことが可能になりました。(これについては後述します。) Changed * 使用するソケット関数をsocket_*からstream_socket_*に変更 * イベント名をハイフンからキャメルケースに変更しました。(例:send-ping → sendPing) これは、WebSocketServer::registerEventの第一引数でクラスが扱えるようになったための処置です。 Notice * SSL通信用のコードが埋め込まれていますが、使用しないでください。 現段階ではGoogleChromeのみでしか動作せず、他のブラウザでは内部エラーが発生します。 現在、原因究明中です。 2012/3/15: Added * draft-76に対応しました。 (確認はOperaのみ。) * バージョン情報を記載しました(Ver 2.0.0から開始) * 接続クライアントの数をリソースごととサーバー全体で管理ができるようになりました。 * WebSocketServer::setMaxServerClients ($size)を追加 * WebSocketServer::setMaxResourceClients ($resource, $size)を追加 * WebSocketServer::setMaxAllResourceClients ($size)を追加 * WebSocketServer::setDisplayExceptions ($bool)を追加 * WebSocketServer::setCheckOrigin ($bool)を追加 * WebSocketServer::getResourceConnections($resource)を追加 * WebSocketServer::getAllResourceConnections()を追加 * overflow-connection, failure-connectionイベントを追加しました。 * send-header, send-bodyイベントを削除しました。 * 特殊イベント server-connect, server-overflow-connection, server-failure-connectionイベントを追加しました。 * readmeにWebSocketServer::__construct, 特殊イベント, draft-76について記載しました。 Changed * WebSocketServer::registerResource ($resource)に引数が1つ増え、WebSocketServer::registerResource ($resource, $maxClients = -1)となりました。 * WebSocketServer::getConnects()からWebSocketServer::getConnections()に名称を変更 Fixed * 126サイズ以上のデータが送れなかった問題を修正 * [重大な問題] クライアントが接続を切った場合、正常にクライアントとの切断ができていなかった問題を修正しました。 * [重大な問題] クライアントがWebSocketに対応していない場合、triggerEventが無限ループしてしまう問題を修正しました。 * その他のバグをいろいろ修正 2012/3/14: Changed * ユニークなIDの生成の方法に問題がありましたので、生成方法を変更しました。 Fixed * 受信したOpcodeが0x02(バイナリフレーム)にもUTF-8チェック行なっていたのを修正しました。 * [セキュリティ問題] 不明なOpcodeを送信したクライアントはセッションを強制的に切断するようにしました。 * [セキュリティ問題] RFCに準拠しないデータを受信した場合は攻撃と判断し、セッションを強制的に切断するようにしました。 2012/3/13: Fixed * サーバー側からマスクしたデータを送信できなかった問題を修正しました。 この問題解決により、送信するメッセージは必ずマスクされるようになりました。 (mt_randでランダムに生成したマスクをchrするのを忘れていただけでした…) 2012/3/11: Add * クライアント個々にユニークIDが割り振られるようになりました。 * broadcast系の送信方法が一部変更になりました。詳しくは下記の「broadcast系のメソッドについて」をご覧下さい * WebSocketServer::$pingProbabilityを追加 * WebSocketServer::$callEventを追加 * WebSocketServer::getClientById ($id)を追加 * WebSocketServer::getClientBySocket ($client)を追加 * WebSocketServer::getClientSockets ()を追加 * WebSocketServer::getClientIds ()を追加 * WebSocketServer::getConnects ()を追加 * WebSocketClient::$idを追加 Fixed * 一部のメンバをprivateに変更しました。 2012/3/10: Add * WebSocketServer::broadcastBinaryMessage($message)を追加 * 一定確率でPingを送信するようにした Fixed * 127バイト以上のデータを受信した際に、稀にサーバーが停止する問題を修正 * データ受信時にOpcodeの取得, マスクの使用/不使用の切り替えが正常に行われていない問題を修正 * マスク使用のビットがセットされていないデータを受信した際に、何故か強制的に切断していた問題を修正 * 無駄な処理を除去 * 受信したデータがUTF-8以外である場合、セッションを強制的に切るように修正 * 送信するデータがUTF-8以外である場合、UTF-8に変換して送信するように修正 * 特定の条件下でエラーが出る問題を修正 * 例外が投げられた時にサーバーが停止してしまう問題を修正 * 実行中のイベントでセッションを終了(sendClose)した場合、次にイベントがある場合は次のイベントを実行しなくなりました。 ---------------------------------------- ■ 確認済み対応WebSocketバージョン draft-76(テキストのみ), 7, 8, 13 ■ 確認済みブラウザ Firefox, GoogleChrome, Opera ■ 確認済みの環境 PHP 5.4.5 (CentOS 6.3) PHP 5.3.8 (Windows 7, XAMPP) ■ サンプルの利用方法 server.bat(sh)を起動後、 sample-time, sample-chatにブラウザからオンラインで接続してみてください。 ○ Q. 起動できない A. 環境変数にphpがない可能性があります。phpのパスを通してください。環境変数の指定の仕方などは調べてください。 ■ draft-76について Operaではテストができないためテキストのみでバイナリの送受信は未対応です。 また、WebSocketClient::sendPingやWebSocketClient::sendPong, WebSocketServer::broadcastPingをしてもping及びpongは送信されません。 ■ メソッド解説 ○ WebSocketServer クラス WebSocketServer::__construct ($ip, $port, $ssl = false) [引数] $ip - 接続を待機するホスト名 $port - 接続を待機するポート番号 $ssl - SSL通信を有効にするかどうか。(true/false) Ver 1.x.x - $pingProbability pingを送信する確率を指定します。例えば10と指定したら、誰かが接続またはソケットに書き込んだ際に10%の確率で クライアント全員に送信することになります。0を指定すると送信しなくなります。デフォルトは1です。 - $callEvent イベントを発動するかの設定をします。falseを指定すると発動しなくなります。デフォルトはtrueです。 - triggerEvent ($eventName, $clientHandle) $eventNameイベントを任意のタイミングで発動させます。 [引数] $eventName - イベント名を指定します。($eventNameについては後述) $clientHandle - WebSocketClientインスタンスを指定します。nullを指定することも可能です。 - registerResource ($resource, $maxClients = -1) 動作させるリソースを登録します。 [引数] $resource - リソース名 $maxClients - 接続可能な人数 - registerEvent ($eventName, $callback) 全リソースに対するトリガーイベントを登録します。 [引数] $eventName - イベント名またはイベントを定義したクラスのインスタンスを指定します。(イベント名については後述) $callback - イベントが呼ばれた時のコールバック関数 (クラスのインスタンスを指定した場合は省略可能で、無視されます。) - registerEvent ($eventName, $resource, $callback) 特定のリソースでアクセスしてきたクライアントにのみトリガーイベントが発動するように登録します。 [引数] $eventName - イベント名またはイベントを定義したクラスのインスタンスを指定します。(イベント名については後述) $resource - リソース名 (クラスのインスタンスを指定した場合は省略可) $callback - イベントが呼ばれた時のコールバック関数 (クラスのインスタンスを指定した場合は省略可能で、無視されます。) - broadcastClose () 全クライアントにWebSocket終了のメッセージを送信します。 - broadcastPing ($message = 'HELLO') 全クライアントにpingを送信します。 [引数] $message - pingで送信する内容を指定します。基本はデフォルトで問題ありません。 - broadcastMessage ($message) 全クライアントにメッセージを送信します。 [引数] $message - 送信メッセージの内容 - broadcastBinaryMessage ($message) 全クライアントにバイナリメッセージを送信します。 [引数] $message - 送信メッセージの内容 - broadcastCommand ($message, $opcode = 0x01, $useMask = false) 全クライアントにメッセージを送信します。 (broadcastMessageと違い詳細設定が可能。) [引数] $message - 送信メッセージの内容 $opcode - Opcode $mask - マスクを指定する場合はtrue, しない場合はfalse - serverRun ($callback = null) サーバーを起動させます。 [引数] $callback - サーバー起動時に使用するコールバック関数。第一引数に自身のWebSocketクラスが参照渡しで入ります。 - getClientById ($id) $idからWebSocketClientクラスを探して返します。 [引数] $id - クライアントに割り振られたid - getClientBySocket ($socket) $socketからWebSocketClientクラスを探して返します。 [引数] $socket - クライアントのソケット - getClientSockets () 接続している全クライアントのソケットを配列で返します。 - getClientIds () 接続している全クライアントのIDを配列で返します。 - getConnections () 接続している人数を返します。 Ver 2.0.x - setMaxServerClients ($size) サーバーに接続できる最大人数を設定します。$sizeが-1の場合、最大人数を制限しません。 [引数] $size - 接続できる最大人数 - setMaxResourceClients ($resource, $size) 特定のリソースに接続できる最大人数を設定します。$sizeが-1の場合、最大人数を制限しません。 [引数] $resource - リソース名 $size - 接続できる最大人数 - setMaxAllResourceClients ($size) 全リソースに接続できる最大人数を設定します。$sizeが-1の場合、最大人数を制限しません。 [引数] $size - 接続できる最大人数 - setDisplayExceptions ($bool) 例外が投げられたとき、コンソールに表示するかの設定をします。 [引数] $bool - true/false - setCheckOrigin ($list) クライアントから送られてきたOriginが正しいかをチェックします。 $listにはチェックしたいOriginを入力します。 falseを指定した場合、チェックは行われません。デフォルトはfalseです。 [引数] $list - チェックしたいOriginを配列または1つであれば、文字列。したくない場合はfalseを設定します。 - getResourceConnections($resource) 特定のリソースにアクセスしている人数を取得します。 [引数] $resource - リソース名 - getAllResourceConnections 全リソースにアクセスしている人数をarray(リソース名1 => 接続人数, リソース名2 => 接続人数, ...)のような形の配列で返します。 - setListenInterval($interval) 接続の受け付けのインターバルを指定します。デフォルトは2000ミリ秒です。(マイクロ秒ではありません。) これに大きな数値を指定してもその分の時間を待つ必要はなく、アクセスがある場合はすぐに接続の受け付けが行われます。 詳しくは http://jp.php.net/manual/en/function.stream-select.php をご覧下さい。 0を指定した場合はアクセスがあるまで接続を待ちます。 [引数] $interval - ミリ秒で指定 - setDisplayLog($bool) サーバーの行動を出力するかの設定をします。 [引数] $bool - true/false Ver 2.1.x - isSecure () SSL通信を有効にしているかをtrue/falseで返します。 - setSSLDirectoryPath ($path) openssl.cnfを設置し、x509.crt, wss-cert.pemを設置するためのディレクトリ先を指定します。 - getSSLDirectoryPath () openssl.cnf, x509.crt, wss-cert.pemを設置してあるディレクトリを取得します。 ○ WebSocketClient クラス Ver 1.x.x - $id 割り振られたID - $address クライアントのIPアドレス - $port クライアントのポート - $timestamp 接続してきた日時をUNIXタイムスタンプで格納します。 - $resource 接続してきたリソース名 - getMessage () クライアントから送信されたメッセージを取得します。 WebSocketから使用されるもので、一般的には使用されません。 - sendClose () クライアントとの切断を形式的に行います。 - sendPing ($ping = 'HELLO') pingを送信します。 [引数] $message - pingで送信する内容を指定します。基本はデフォルトで問題ありません。 - sendPong ($pong) pongを送信します。 getMessage()から使用されるもので、一般的には使用されません。 [引数] $ping - pongで送信する内容を指定します。 - sendMessage ($message) メッセージを送信します。 [引数] $message - 送信メッセージの内容 - sendBinaryMessage ($message) バイナリメッセージを送信します。 [引数] $message - 送信メッセージの内容 - sendCommand ($message, $opcode = 0x01, $useMask = false) メッセージを送信します。 (sendMessageと違い詳細設定が可能。) [引数] $message - 送信メッセージの内容 $opcode - Opcode $mask - マスクを指定する場合はtrue, しない場合はfalse - read ($size = 0xffff) バッファを読みこむためのものです。 getMessage()から使用されるもので、一般的には使用されません。 [引数] $size = 読み込むサイズ - write ($body) バッファを書きこむためのものです。 getMessage()から使用されるもので、一般的には使用されません。 [引数] $body - 書き込む内容 Ver 2.x.x - $resourceQuery リソース名の後ろのクエリをPHPの$_GET/$_POST同様にパースした配列 例: ws://localhost/test?a=b&c=dでアクセスしてきた場合 array( 'a' => 'b', 'c' => 'd' ) のように格納されます。 - $lastAccess 最終アクセス日時を記録します。すなわち、最後にクライアントが サーバーのストリームに書き込んだ日時です。 ■ broadcast系のメソッドについて ○ 第一引数の$messageに関して $messageには送信する内容を書くものですが、下記のような配列を指定することも可能です。 0 => WebSocketClientクラスで作られたインスタンス 1 => 添字0で指定した相手にこのメッセージを送信 2 => 添字0で指定した相手以外全員にこのメッセージを送信 例えば、 $message = array( 0 => $client 1 => 'Me' 2 => 'Anyone' ); と指定した場合$clientが指すクライアントにはMeが送信され、それ以外のユーザーにはAnyoneが送信されます。 また、添字1をfalseに指定した場合、$clientが指すクライアントには送信されず、それ以外のユーザーにはAnyoneが送信され、 逆に添字2をfalseにした場合$clientが指すクライアントにのみMeが送信されるようになります。 これは、添字2をfalseにした場合は$client->sendMessage($message);と同等で、 添字1をfalseにした場合 foreach ($server->getClients() as $c) { if ($c != $client) { $c->sendMessage($message); } } と同等になります。 ■ イベント名について ○ 登録されている使用可能なイベント - overflowConnection - serverOverflowConnection (特殊イベント) クライアントが接続してきた際に、サーバーに接続しているクライアントが設定した接続許可数より多くなる時に発動するイベントです。 [コールバック関数が受け付ける引数 (overflowConnectionのみ)] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 - failureConnection - serverFailureConnection (特殊イベント) サーバーがなんらかの事情(Originチェックが失敗したなど)でクライアントのアクセスを拒否した場合に 発動するイベントです。 [コールバック関数が受け付ける引数 (failureConnectionのみ)] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 - serverConnect (特殊イベント) クライアントが接続時に発動するイベントです。 connectイベントとの違いは、ハンドシェイクを行う前に実行される点です。 よって、WebSocketClientクラスは生成されないため、引数で受け取ることができません。 [コールバック関数が受け付ける引数] なし。 - connect クライアントが接続時に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 - disconnect クライアントが切断時に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 - send データの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 送信した内容 - sendPing pingの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $ping - 送信したping - sendPong pongの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $pong - 送信したpong - sendClose クライアントに切断メッセージの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 - sendMessage メッセージの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 送信した内容 - sendMessagePlain 通常メッセージの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 送信した内容 - sendMessageBinary バイナリメッセージの送信後に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 送信した内容 - received データを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 受け取った内容 - receivedPing pingを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $ping - 受け取ったping - receivedPong pongを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $pong - 受け取ったpong - receivedClose クライアントから切断メッセージを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 - receivedMessage メッセージを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 受け取った内容のデコード結果 - receivedMessagePlain 通常メッセージを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 受け取った内容のデコード結果 - receivedMessageBinary バイナリメッセージを受け取った際に発動するイベントです。 [コールバック関数が受け付ける引数] $clientHandle - WebSocketClientクラスでインスタンスされたものを参照渡しで引き渡します。 $message - 受け取った内容のデコード結果 ○ 発動の優先度 ※ (/が含まれるものは順が不確定または、どちらかという意味をさします。) 接続 | serverOverflowConnection | serverFailureConnection/failureConnection | overflowConnection イベントトリガー発動 | リソース名ありのreceived/send | リソース名なしのreceived/send [received] received | +--------------------------------+-+----------------+------------------+ | | | | +------receivedMessage------+ receivedClose receivedPing receivedPong | | receivedMessagePlain receivedMessageBinary [send] send | +------------------------+---------+----------------+------------------+------------------+ | | | | | sendMessageBinary sendMessagePlain sendClose sendPing sendPong ○ 特殊イベントについて 特殊イベントとは、リソース名を持てない特別なイベントの事をさします。 基本的にサーバー側の動作を対象としています。 ○ 第一引数にクラスのインスタンスを渡す場合 第三引数は無視されます。 クラスは、 class クラス名 extends WebSocketEvent implements IWebSocketEvent { ... } のようにして宣言する必要があります。(extends WebSocketEventは省略できません。) WebSocketEvent::$clientにはクライアントハンドルが格納され、WebSocketEvent::$serverにはサーバーハンドルがそれぞれ参照渡しで格納されます。 また、宣言するメソッドはWebSocketEventをご覧頂ければわかりますが、文字列で定義していたイベント名をメソッド名として宣言します。 registerEventの登録イベントの個々のオーバーライドは今まで通り可能です。 例: class TimeEvent extends WebSocketEvent implements IWebSocketEvent { public function connect () { $this->handle->sendMessage((string) time()); printf("> Sent a time to %s:%d\n", $this->client->address, $this->client->port); // 終了させる。 $this->client->sendClose(); } } $serv->registerEvent(new TimeEvent(), 'time'); ■ 基本的な書式 付属されているWebSocketServer.php, WebSocketClient.php, WebSocketEvent.php, IWebSocketEvent.php, WebSocketException.phpを読み込んでください。 その後、インスタンス化、イベント登録を行い、サーバーを起動します。 単純な例: <?php include 'WebSocketServer.php'; include 'WebSocketClient.php'; include 'WebSocketEvent.php'; include 'IWebSocketEvent.php'; include 'WebSocketException.php'; $ws = new WebSocketServer('0.0.0.0', 8484); $ws->registerEvent('connect', function ($handle) { printf("%s:%dが接続しました。\n", $handle->address, $handle->port); }); $ws->registerEvent('disconnect', function ($handle) { printf("%s:%dが切断しました。\n", $handle->address, $handle->port); }); $ws->serverRun(function () { printf("Running Server!\n"); }); ■ wssプロトコルについて GoogleChrome以外のブラウザでwssプロトコルを使う場合、 自己署名証明書を使う場合において証明書を許可する必要があります。 server.bat(sh)を起動後、WebSocketServer::getSSLDirectoryPath() (デフォルトはssl)ディレクトリ内のx509.crtを 証明書としてインポートし、信頼する設定にする必要があります。 なお、第三者認証局を使う場合は生成したCRTとプライベートキーを組み合わせて、 WebSocketServer::getSSLDirectoryPath() (デフォルトはssl)ディレクトリにwss-cert.pemとして設置する必要があります。 x509.crtを生成するにはサーバー起動時にSSLを有効にする必要があります。 server.phpを開き $serv = new WebSocketServer('0.0.0.0', 8484); 上記のコードを $serv = new WebSocketServer('0.0.0.0', 8484, true); としてください。これで、SSL通信が可能になり、第三者認証局を使用していない場合は、WebSocketServer::getSSLDirectoryPath() (デフォルトはssl)ディレクトリにx509.crtが生成されます。 pemファイルの有効期間が過ぎていなければ、2度目のサーバー起動時にx509.crt及びwss-cert.pemの自動生成は行われません。 裏を返せば、有効期限が過ぎている場合は、自動生成が行われますので、第三者認証局を使用している場合は、ファイルが書き換わる可能性があります。 自動生成の場合の有効期間は3652日となっています。 ■ その他の記述について 詳しくはサンプルのserver.php, ./sample-chat/index.html, ./sample-time/index.html をご覧下さい。