◆ソケットの基本
PHP は、Berkley のソケット ライブラリを使用して接続を作成します。ソケットは単なるデータ構造にすぎません。このソケット データ構造を使用して、クライアントとサーバー間のセッションを開始します。このサーバーは常にリッスンし、新しいセッションを生成する準備をしています。クライアントがサーバーに接続すると、サーバーがセッションを待機しているポートが開きます。このとき、サーバーはクライアントの接続要求を受け入れ、サイクルを実行します。これで、クライアントはサーバーに情報を送信し、サーバーはクライアントに情報を送信できるようになります。
ソケットを生成するには、プロトコル、ソケット タイプ、パブリック プロトコル タイプの 3 つの変数が必要です。ソケットを生成するときに選択できるプロトコルは 3 つあります。詳細なプロトコルの内容については、以下を読み続けてください。
パブリック プロトコル タイプの定義は、接続の重要な要素です。以下の表では、一般的なプロトコルの種類を示します。
表 1: プロトコル名/定数の説明
AF_INET これは、ほとんどのソケットで使用されるプロトコルで、送信に TCP または UDP を使用し、IPv4 アドレスで使用されます。
AF_INET6 は上記と似ていますが、IPv6 アドレスに使用されます。
AF_UNIX ローカル プロトコル。Unix および Linux システムで使用されます。通常、クライアントとサーバーが同じマシン上にある場合に使用されます。
SOCK_STREAM このプロトコルは、シーケンシャルで信頼性の高い、データ統合されたバイト ストリーム ベースの接続です。これは最も一般的に使用されるソケットのタイプです。このソケットは送信に TCP を使用します。
SOCK_DGRAM このプロトコルは、コネクションレス型の固定長転送呼び出しです。このプロトコルは信頼性が低く、接続に UDP を使用します。
SOCK_SEQPACKET このプロトコルは、送信用の固定長データ パケットを送信する 2 回線の信頼性の高い接続です。このパケットは、読み取られる前に完全に受け入れられる必要があります。
SOCK_RAW このソケット タイプは、ICMP パブリック プロトコルを使用して単一のネットワーク アクセスを提供します。 (pingとtracerouteはこのプロトコルを使用します)
SOCK_RDM このタイプはほとんど使用されず、ほとんどのオペレーティング システムでは実装されていません。データ リンク層で使用するために提供されており、パケットの順序は保証されません。
ICMP インターネット制御メッセージ プロトコル。主にゲートウェイとホストでネットワーク状態を確認し、エラー メッセージを報告するために使用されます。
UDP ユーザー データグラム プロトコル。コネクションレスで信頼性の低い伝送プロトコルです。
TCP 伝送制御プロトコルは、最も一般的に使用されている信頼性の高いパブリック プロトコルであり、伝送プロセス中にエラーが発生した場合に、データ パケットが受信者に確実に到達できるようにします。
ソケットを生成する 3 つの要素がわかったので、PHP のsocket_create() 関数を使用してソケットを生成します。このsocket_create()関数には、プロトコル、ソケットタイプ、パブリックプロトコルの3つのパラメータが必要です。 ocket_create() 関数は、正常に実行された場合はソケットを含むリソース タイプを返し、失敗した場合は false を返します。
リソースソケット_create(int プロトコル、int ソケットタイプ、int 共通プロトコル);
ソケットを作成したら、その後はどうなるでしょうか? PHP には、ソケットを操作するための関数がいくつか用意されています。ソケットを IP にバインドし、ソケットの通信をリッスンし、ソケットを受け入れることができます。次に、関数がソケットを生成、受け入れ、リッスンする方法を理解するための例を見てみましょう。
<?php
$commonProtocol = getprotobyname("tcp");//パブリック プロトコル名を使用してプロトコル タイプを取得します
$socket =ソケット_create(AF_INET, SOCK_STREAM, $commonProtocol);//ソケットを生成し、ソケットリソースのインスタンスを返します。
socket_bind($socket, 'localhost', 1337);//ソケットをローカルコンピュータにバインドします
socket_listen($socket);//すべての受信ソケット接続をリッスンします
// さらなるソケット機能が追加される予定
?>
上記の例では、独自のサーバー側を生成します。例の最初の行は、
$commonProtocol = getprotobyname("tcp");
パブリック プロトコル名を使用してプロトコル タイプを取得します。ここでは TCP パブリック プロトコルが使用されます。UDP または ICMP プロトコルを使用する場合は、getprotobyname() 関数のパラメータを「udp」または「icmp」に変更する必要があります。もう 1 つの方法は、getprotobyname() 関数を使用する代わりに、socket_create() 関数で SOL_TCP または SOL_UDP を指定することです。
$socket = ソケット作成(AF_INET, SOCK_STREAM, SOL_TCP);
例の 2 行目はソケットを作成し、ソケット リソースのインスタンスを返します。ソケット リソースのインスタンスを取得したら、ソケットを IP アドレスとポートにバインドする必要があります。
ソケットバインド($socket, 'localhost', 1337);
ここでは、ソケットをローカル コンピューター (127.0.0.1) にバインドし、ソケットを 1337 ポートにバインドします。次に、すべての受信ソケット接続をリッスンする必要があります。
ソケットリッスン($ソケット);
4 行目以降は、すべてのソケット関数とその使用法を理解する必要があります。
表 4: ソケット関数の関数名の説明
socket_accept() はソケット接続を受け入れます
socket_bind() はソケットを IP アドレスとポートにバインドします
socket_clear_error() ソケットエラーまたは最後のエラーコードをクリアします
socket_close() はソケットリソースを閉じます
socket_connect() はソケット接続を開始します
socket_create_listen() は、指定されたポートでリッスンするソケットを開きます
socket_create_pair() は、区別できないソケットのペアを配列に生成します。
socket_create() はソケットを生成します。これはソケット データ構造を生成するのと同じです。
socket_get_option() ソケットオプションを取得します
socket_getpeername() リモートの同様のホストの IP アドレスを取得します
socket_getsockname() はローカルソケットの IP アドレスを取得します
socket_iovec_add() は、新しいベクトルを分散/集約配列に追加します。
socket_iovec_alloc() この関数は、送信、受信、読み取り、書き込みができる iovec データ構造を作成します。
socket_iovec_delete() は割り当てられた iovec を削除します
socket_iovec_fetch() は、指定された iovec リソースのデータを返します。
socket_iovec_free() は iovec リソースを解放します
socket_iovec_set() は iovec データの新しい値を設定します
socket_last_error() は、現在のソケットの最後のエラー コードを取得します。
socket_listen() は、指定されたソケットからのすべての接続をリッスンします。
socket_read() は指定された長さのデータを読み取ります
socket_readv() は分散/集約配列からデータを読み取ります
socket_recv() はソケットからキャッシュへのデータを終了します。
socket_recvfrom() は、指定されたソケットからデータを受け取ります。指定されていない場合は、デフォルトで現在のソケットが使用されます。
socket_recvmsg() は iovec からメッセージを受信します
socket_select() 複数選択
socket_send() この関数は、接続されたソケットにデータを送信します。
socket_sendmsg() はソケットにメッセージを送信します
socket_sendto() は、指定されたアドレスのソケットにメッセージを送信します。
socket_set_block() はソケットをブロックモードに設定します
socket_set_nonblock() ソケットを非ブロック モードに設定します
socket_set_option() はソケット オプションを設定します
socket_shutdown() この関数を使用すると、読み取り、書き込み、または指定されたソケットを閉じることができます。
socket_strerror() は、指定されたエラー番号を持つ詳細なエラーを返します。
socket_write() はソケット キャッシュにデータを書き込みます
socket_writev() は、分散/集約配列にデータを書き込みます。これらの関数を使用するには、ソケットを開いていない場合は、php.ini ファイルを編集して削除してください。この行の前に次のコメントがあります:
拡張子=php_sockets.dll
コメントを削除できない場合は、次のコードを使用して拡張ライブラリをロードします。
<?php
if(!extension_loaded('ソケット')) {
if(strtoupper(substr(PHP_OS, 3)) == “WIN”) {
dl('php_sockets.dll');
}それ以外{
dl('ソケット.so');
}
}
?>
ソケットが開いているかどうかがわからない場合は、phpinfo() 関数を使用してソケットが開いているかどうかを判断できます。ソケットが開いているかどうかは、phpinfo 情報を確認することで確認できます。
phpinfo() のソケットに関する情報を表示します。 ◆ サーバーを生成します。 それでは、最初の例を改良してみましょう。特定のソケットをリッスンし、ユーザー接続を処理する必要があります。
<?php
$commonProtocol = getprotobyname("tcp");
$socket =ソケット_create(AF_INET, SOCK_STREAM, $commonProtocol);
ソケットバインド($socket, 'localhost', 1337);
ソケットリッスン($ソケット);
// サーバーへの受信接続を受け入れます
$connection = ソケット受け入れ($socket);
if($connection){
socket_write($connection, "ソケットに接続しました...nr");
}
?>
この例を実行するには、コマンド プロンプトを使用する必要があります。その理由は、ここでは Web ページではなくサーバーが生成されるためです。 Web ブラウザを使用してこのスクリプトを実行しようとすると、30 秒の制限を超える可能性が高くなります。以下のコードを使用して無限の実行時間を設定できますが、コマンド プロンプトを使用して実行することをお勧めします。
set_time_limit(0);
コマンド プロンプトでこのスクリプトをテストするだけです。
Php.exe example01_server.php
システムの環境変数に php インタープリターへのパスを設定していない場合は、php.exe へのパスを指定する必要があります。サーバーを実行するときは、Telnet 経由でポート 1337 に接続してサーバーをテストできます。
上記のサーバー側には 3 つの問題があります。 1. 複数の接続を受け入れることができません。 2. 1 つのコマンドのみを完了します。 3. Web ブラウザからこのサーバーに接続することはできません。
この最初の問題は解決が簡単で、アプリケーションを使用して毎回サーバーに接続できます。しかし、次の問題は、Web ページを使用してサーバーに接続する必要があることですが、これはさらに困難です。サーバーに接続を受け入れさせ、クライアントにデータを書き込み (書き込む必要がある場合)、接続を閉じて次の接続を待つことができます。
前のコードを改良し、次のコードを生成して新しいサーバーを作成します。
<?php
// ソケットを設定します
$commonProtocol = getprotobyname("tcp");
$socket =ソケット_create(AF_INET, SOCK_STREAM, $commonProtocol);
socket_bind($socket, 'localhost', 1337); //socket_bind() はソケットを IP アドレスとポートにバインドします
ソケットリッスン($ソケット);
//バッファを初期化する
$buffer = "データなし";
while(true) {
// このソケットで受信するすべての接続を受け入れます
$connection =socket_accept($socket);//socket_accept() はソケット接続を受け入れます
printf("ソケットが接続されましたrn");
// バッファーに何かがあるかどうかを確認します
if($buffer != ""){
printf("バッファ内に何かがあります...データを送信中...rn");
socket_write($connection, $buffer . "rn"); //socket_write() はソケット キャッシュにデータを書き込みます
printf("ソケットに書き込みましたrn");
}それ以外 {
printf("バッファにデータがありませんrn");
}
//入力を取得します
while($data =socket_read($connection, 1024, PHP_NORMAL_READ))//socket_read() は、指定された長さのデータを読み取ります
{
$buffer = $data;
socket_write($connection, "受信した情報rn");
printf("バッファ: " . $buffer . "rn");
}
socket_close($connection); //socket_close() はソケット リソースを閉じます
printf("ソケットを閉じましたrnrn");
}
?>
このサーバーは何をすべきでしょうか?ソケットを初期化し、データを送受信するためにキャッシュを開きます。接続を待機し、接続が確立されると、サーバー側の画面に「ソケットが接続されました」と表示されます。このサーバーはバッファをチェックし、バッファ内にデータがある場合は、接続されているコンピュータにデータを送信します。次に、このデータに対する受け入れメッセージを送信し、メッセージを受け入れると、メッセージをデータに保存し、接続されているコンピュータにメッセージを認識させ、最後に接続を閉じます。接続が閉じられると、サーバーは次の接続の処理を開始します。
◆ 2 番目の問題に対処するクライアントを生成するのは簡単です。 PHP ページを生成し、ソケットに接続し、データをそのキャッシュに送信して処理する必要があります。その後、処理されたデータが待機しているので、データをサーバーに送信できます。別のクライアント接続では、そのデータが処理されます。
次の例は、ソケットの使用法を示しています。
<?php
// ソケットを作成して接続する
$socket = ソケット作成(AF_INET, SOCK_STREAM, SOL_TCP);
$connection = ソケット接続($socket,'localhost', 1337);
while($buffer =socket_read($socket, 1024, PHP_NORMAL_READ)) {
if($buffer == "データなし") {
echo(“<p>データがありません</p>”);
壊す;
}それ以外{
// バッファ内のデータを使って何かを行う
echo(“<p>バッファ データ: “ . $buffer . “</p>”);
}
}
echo(“<p>ソケットに書き込み中</p>”);
// テスト データをソケットに書き込みます
if(!socket_write($socket, “一部のデータrn”)){
echo(“<p>書き込みに失敗しました</p>”);
}
// ソケットからの応答を読み取ります
while($buffer =socket_read($socket, 1024, PHP_NORMAL_READ)){
echo(“<p>送信されたデータは次のとおりです: 一部のデータ<br> 応答は次のとおりです:” . $buffer . “</p>”);
}
echo(“<p>ソケットからの読み取りが完了しました</p>”);
?>
このコード例は、サーバーに接続するクライアントを示しています。クライアントはデータを読み取ります。これがこのサイクルで到着する最初の接続である場合、サーバーは「NO DATA」をクライアントに送り返します。これが発生した場合、クライアントは接続の最上位にあります。クライアントはデータをサーバーに送信し、データはサーバーに送信され、クライアントは応答を待ちます。応答を受信すると、応答を画面に書き込みます。