この記事では主にPHP SOCKETプログラミングの詳細な説明を紹介していますので、必要な方は参考にしてください
。1.予備知識
PHP のソケット モジュールを使用して何かを行う人はあまり見かけませんが、おそらく誰もがそれをスクリプト言語の範囲内に置いていますが、実際には PHP のソケット モジュールは、ftplist、http ポスト送信、smtp 送信など、多くのことを行うことができます。 、パケットのグループ化と特別なメッセージの相互作用 (smpp プロトコルなど)、whois クエリ。これらは比較的一般的なクエリです。
特に、PHP のソケット拡張ライブラリでできることは、C よりもそれほど劣っていません。
phpソケット接続関数
1. カーネルに統合されたソケット
この一連の機能はアクティブな接続のみを実行でき、ポート監視関連の機能は実装できません。 4.3.0 より前では、すべてのソケット接続はブロッキング モードでのみ機能します。
この一連の機能には以下が含まれます
fsockopen、pfsockopen
これら 2 つの関数に関する特別な情報は、php.net のユーザー マニュアルに記載されています
これらはすべてリソース番号を返します。このリソースでは、fgets()、fwrite()、fclose() など、ファイルを操作するほぼすべての関数を使用できます。すべての関数がネットワークに直面するためにこれらの関数に従うことに注意してください。フロー パターンの例:
fread() はファイル ポインタ ハンドルから最大 length バイトを読み取ります。 この関数は、length バイトを読み取った後、EOF に達したとき、または (ネットワーク ストリームの場合) パケットが利用可能になったときのいずれかが早い時点で、ファイルの読み取りを停止します。
ネットワークフローの場合、完全なパケットが取得された時点で停止することに注意を払う必要があることがわかります。
2. php拡張モジュールが提供するソケット関数。
php4.xではextension=php_sockets.dll、Linuxではextension=php_sockets.soというモジュールが存在します。
このモジュールがオンになっている場合、PHP には、リッスン ポート、ブロッキング モードと非ブロッキング モードの切り替え、マルチクライアントの対話型処理などを含む強力なソケット機能があることを意味します。
このシリーズの関数のリストについては、http://www.php.net/manual/en/ref.sockets.phpを参照してくださいこのリストを読んだ後、これは非常に充実していると思いますか? しかし、このモジュールがまだ非常に未熟で、関連する参考資料がほとんどないのは残念です: (
私も研究中なので、今回は詳しくは触れませんが、参考記事として紹介します
http://www.zend.com/pecl/tutorials/sockets.php
2. PHPソケット拡張機能を使用する
サーバー側コード:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
/** * ファイル名server.php * サーバー側のコード * * @著者 guisu.huang * @2012-04-11 以降 * */
//クライアントへの接続時にタイムアウトがないことを確認します set_time_limit(0); //IPとポート番号を設定します $アドレス = "127.0.0.1"; $port = 2046; //デバッグ時に、プログラムをテストするためにさらに多くのポートを変更できます。 /** * ソケットを作成します * AF_INET= は ipv4 です。ipv6 が使用される場合、パラメーターは AF_INET6 です。 * SOCK_STREAM はソケットの TCP タイプです。UDP の場合は SOCK_DGRAM を使用します。 */$sock =ソケット_create(AF_INET, SOCK_STREAM, SOL_TCP) または die("socket_create() 失敗の理由は次のとおりです:" .socket_strerror(socket_last_error()) ."/n"); //ブロックモード socket_set_block($sock) または die("socket_set_block() は次の理由で失敗しました:" .socket_strerror(socket_last_error()) . "/n"); //ソケットポートにバインド $result =socket_bind($sock, $address, $port) または die("socket_bind() 失敗の理由は次のとおりです:" .socket_strerror(socket_last_error()) . "/n"); //監視を開始します $result =socket_listen($sock, 4) または die("socket_listen() 失敗の理由は次のとおりです:" .socket_strerror(socket_last_error()) . "/n"); echo "$address:$port でソケットを OKnBinding ... "; echo "OKn接続を受け入れる準備ができました。nソケットでリッスンしています...n"; do { // デーモンを決して停止しないでください //接続リクエストを受信し、サブ接続ソケットを呼び出してクライアントとサーバー間の情報を処理します $msgsock =socket_accept($sock) または die("socket_accept() が失敗しました: 理由: " .socket_strerror(socket_last_error()) . "/n");
//クライアントデータを読み取る echo "クライアント データ n を読み取ります"; //socket_read 関数は、n、t、または $buf =ソケット_読み取り($msgsock, 8192);echo "受信したメッセージ: $buf n";
//データ送信はクライアントに戻り結果を書き込みます $msg = "ようこそ"; socket_write($msgsock, $msg, strlen($msg)) または die("socket_write() が失敗しました: 理由: " .socket_strerror(socket_last_error()) ."/n"); //出力がクライアントに返されたら、socket_close($msgsock) 関数を通じて親/子ソケットを終了する必要があります socket_close($msgsock); } while (true); socket_close($sock);
|
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
/** * ファイル名:client.php * クライアントコード * * @著者 guisu.huang * @2012-04-11 以降 */ set_time_limit(0);
$host = "127.0.0.1"; $ポート = 2046; $socket =ソケット_create(AF_INET, SOCK_STREAM, SOL_TCP)or die("ソケットを作成できませんでした") // ソケットを作成します ; $connection =socket_connect($socket, $host, $port) または die("サーバーに接続できませんでした") // 接続します socket_write($socket, "helloソケット") または die("Write failedn") // データ転送によりサーバーにメッセージが送信されます ; while ($buff =socket_read($socket, 1024, PHP_NORMAL_READ)) {echo("応答は次のとおりです:" . $buff . "n"); } socket_close($socket);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
/* {{{ プロトリソースソケット_create(int ドメイン, int 型, int プロトコル) U domain で指定されたドメイン内に、type */ で指定されたタイプの通信用のエンドポイントを作成しますPHP_FUNCTION(socket_create) { long arg1、arg2、arg3; php_socket *php_sock = (php_socket*)emalloc(sizeof(php_socket));
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llll", &arg1, &arg2, &arg3) == FAILURE) { efree(php_sock); 戻る; }
if (arg1 != AF_UNIX #if HAVE_IPV6 && arg1 != AF_INET6 #endif && arg1 != AF_INET) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "AF_INET を想定し、引数 1 に無効なソケット ドメイン [%ld] が指定されました", arg1); arg1 = AF_INET; }
if (arg2 > 10) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "SOCK_STREAM を想定し、引数 2 に無効なソケット タイプ [%ld] が指定されました", arg2); arg2 = SOCK_STREAM; }
php_sock->bsd_socket = ソケット(arg1, arg2, arg3); php_sock->type = arg1;
if (IS_INVALID_SOCKET(php_sock)) { SOCKETS_G(last_error) = エラー番号; php_error_docref(NULL TSRMLS_CC, E_WARNING, "ソケット [%d] を作成できません: %s", errno, php_strerror(errno TSRMLS_CC)); efree(php_sock); RETURN_FALSE; }
php_sock->error = 0; php_sock->ブロック = 1; 1257,1-8 61% ZEND_REGISTER_RESOURCE(return_value, php_sock, le_socket); } |
Zend API は実際には、PHP で使用するために c 関数ソケットをラップします。 C ソケット プログラミングでは、次のメソッドを使用してソケットを初期化します。
?
1 2 3 4 5 |
//ソケットを初期化する if( (socket_fd = ソケット(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("ソケット作成エラー: %s(errno: %d)n",strerror(errno),errno); 終了(0); } |
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() は分散/集約配列にデータを書き込みます
5. PHPソケットシミュレーションリクエスト
stream_socket を使用してシミュレートします:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
/** * * @param $data= array=array('key'=>value) */ 関数 post_contents($data = array()) { $post = $data ? http_build_query($data) : ''; $header = "POST /test/ HTTP/1.1" . $header .= "ユーザーエージェント: Mozilla/4.0+(互換性;+MSIE+6.0;+Windows+NT+5.1;+SV1)" . $header .= "ホスト: localhost" . $header .= "受け入れる: */*" . $header .= "リファラー: http://localhost/test/" . $header .= "Content-Length: ".strlen($post) . $header .= "Content-Type: application/x-www-form-urlencoded" .$header .= "rn"; $ddd = $header . $fp = stream_socket_client("tcp://localhost:80", $errno, $errstr, 30); $response = ''; if (!$fp) { echo "$errstr ($errno) n"; } 他 { fwrite($fp, $ddd); $i = 1; while ( !feof($fp) ) { $r = fgets($fp, 1024); $response .= $r; //この行を処理します } } fclose($fp); $response を返す; }
|
上記のプログラムは無限ループに入る可能性があることに注意してください。
PHPのfeof($fp)で注意すべき点ですが、なぜ無限ループに陥るのかを分析してみましょう。?
1 2 3 4
|
while ( !feof($fp) ) { $r = fgets($fp, 1024); $response .= $r; }
|
?
1 2 3 4 5
|
$fp = fopen("myfile.txt", "r"); ながら (!feof($fp)) { $current_line = fgets($fp); // 無限ループに入らないように結果をさらに処理します }
|
1) while() はループし続けます。
2) fgetsは最後から2行目の文字列を取得します
3) feofはfalseを返し、次のループに入る
4) fgets はデータの最後の行を取得します
5) fegets 関数が呼び出されても、feof 関数は false を返します。したがって、ループを実行し続けます
6) fget は別の行を取得しようとしますが、実際の結果は空です。実際のコードはこれを認識せず、存在しない別の行を処理しようとしましたが、fgets が呼び出され、feof put back の結果は false のままでした
7) ……
8) 無限ループに入る