PHPでは、 socket_recvおよびsocket_select関数は、特に非ブロッキングI/O操作を実行する必要がある場合、ネットワークプログラミングの非常に一般的なツールです。これらの2つの機能を使用して、ネットワークデータを待機することで引き起こされる閉塞を避け、それによりプログラムの効率と応答性が向上します。この記事では、これらの2つの機能を使用してブロッキングしない受信を実現する方法を詳細に紹介します。
従来のブロッキング受信では、 socket_recvおよびその他の機能を使用してデータを受信すると、プログラムはそのコード行で停止し、データが到着したりタイムアウトが発生するまで実行され続けたりしません。これは、データが届かない場合、プログラムは「スタック」され、他の操作が実行されないことを意味します。
受信をブロックする代わりに、データを待っている間にプログラムが実行を停止しないことを意味します。データを受信するときに確認します。データがない場合、他の操作を継続できます。これにより、I/O操作の実行中にデータを待っている間、プログラムはブロックされません。
Socket_Select関数は、複数のソケットの準備が整うかどうかを確認する方法を提供します。これにより、待機のブロックを避けることができます。 Socket_Selectを呼び出すと、指定されたソケットに読み取りデータがあるかどうかが確認されます。その場合、対応するソケットオブジェクトを返します。この関数を使用して、複数のソケットで非ブロッキング受信操作を実行できます。
<span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-title function_ invoke__">socket_select</span></span><span> ( </span><span><span class="hljs-keyword">array</span></span><span> &</span><span><span class="hljs-variable">$read</span></span><span>, </span><span><span class="hljs-keyword">array</span></span><span> &</span><span><span class="hljs-variable">$write</span></span><span>, </span><span><span class="hljs-keyword">array</span></span><span> &</span><span><span class="hljs-variable">$except</span></span><span>, </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$tv_sec</span></span><span>, </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$tv_usec</span></span><span> )
</span></span>
$ read :読み取り可能で検出する必要があるすべてのソケットを含む配列。
$ write :書き込み可能であるために検出する必要があるすべてのソケットを含む配列。
$を除く:例外のために検出する必要があるすべてのソケットを含む配列。
$ TV_SECおよび$ TV_USEC :タイムアウト(秒とマイクロ秒)を指定します。 nullに設定すると、それは無限の待機です。
<span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-title function_ invoke__">socket_recv</span></span><span> ( resource </span><span><span class="hljs-variable">$socket</span></span><span> , </span><span><span class="hljs-keyword">string</span></span><span> &</span><span><span class="hljs-variable">$buf</span></span><span> , </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$length</span></span><span> , </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$flags</span></span><span> )
</span></span>
$ socket :データを受信するソケットリソース。
$ buf :受信したデータはこの変数に保存されます。
$の長さ:受信する最大バイト数。
$フラグ:受信操作を制御するフラグ、通常0。
socket_selectを使用してsocket_recvを使用して非ブロッキングレセプションを実現する完全な例を次に示します。
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-comment">// aを作成します TCP socket</span></span><span>
</span><span><span class="hljs-variable">$server</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_create</span></span><span>(AF_INET, SOCK_STREAM, SOL_TCP);
</span><span><span class="hljs-comment">// サーバーアドレスとポートの設定</span></span><span>
</span><span><span class="hljs-variable">$address</span></span><span> = </span><span><span class="hljs-string">'127.0.0.1'</span></span><span>;
</span><span><span class="hljs-variable">$port</span></span><span> = </span><span><span class="hljs-number">12345</span></span><span>;
</span><span><span class="hljs-comment">// バインド socket</span></span><span>
</span><span><span class="hljs-title function_ invoke__">socket_bind</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>, </span><span><span class="hljs-variable">$address</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>);
</span><span><span class="hljs-comment">// 監視を開始します</span></span><span>
</span><span><span class="hljs-title function_ invoke__">socket_listen</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-comment">// タイムアウトを設定します</span></span><span>
</span><span><span class="hljs-variable">$timeout</span></span><span> = </span><span><span class="hljs-number">10</span></span><span>; </span><span><span class="hljs-comment">// タイムアウト 10 2番</span></span><span>
</span><span><span class="hljs-variable">$read</span></span><span> = [</span><span><span class="hljs-variable">$server</span></span><span>]; </span><span><span class="hljs-comment">// 接続があるかどうかを検出するために使用されます</span></span><span>
</span><span><span class="hljs-variable">$write</span></span><span> = </span><span><span class="hljs-variable">$except</span></span><span> = [];
</span><span><span class="hljs-comment">// ループを入力します,クライアント接続を待っています</span></span><span>
</span><span><span class="hljs-keyword">while</span></span><span> (</span><span><span class="hljs-literal">true</span></span><span>) {
</span><span><span class="hljs-comment">// 使用 socket_select 新しい接続があるかどうかを検出します</span></span><span>
</span><span><span class="hljs-variable">$changed</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_select</span></span><span>(</span><span><span class="hljs-variable">$read</span></span><span>, </span><span><span class="hljs-variable">$write</span></span><span>, </span><span><span class="hljs-variable">$except</span></span><span>, </span><span><span class="hljs-variable">$timeout</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$changed</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"socket_select failed\n"</span></span><span>;
</span><span><span class="hljs-keyword">break</span></span><span>;
}
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$changed</span></span><span> > </span><span><span class="hljs-number">0</span></span><span>) {
</span><span><span class="hljs-comment">// 新しいクライアント接続があるかどうかを確認してください</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">in_array</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>, </span><span><span class="hljs-variable">$read</span></span><span>)) {
</span><span><span class="hljs-variable">$client</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_accept</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-variable">$read</span></span><span>[] = </span><span><span class="hljs-variable">$client</span></span><span>; </span><span><span class="hljs-comment">// 読み取り配列に新しい接続が追加されます</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Client connected\n"</span></span><span>;
}
</span><span><span class="hljs-comment">// 接続されたすべてを繰り返します socket,読み取るデータがあるかどうかを確認してください</span></span><span>
</span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$read</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$sock</span></span><span>) {
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$sock</span></span><span> !== </span><span><span class="hljs-variable">$server</span></span><span>) {
</span><span><span class="hljs-variable">$data</span></span><span> = </span><span><span class="hljs-string">''</span></span><span>;
</span><span><span class="hljs-variable">$bytes_received</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_recv</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>, </span><span><span class="hljs-variable">$data</span></span><span>, </span><span><span class="hljs-number">1024</span></span><span>, MSG_DONTWAIT);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$bytes_received</span></span><span> === </span><span><span class="hljs-number">0</span></span><span>) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Client disconnected\n"</span></span><span>;
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
</span><span><span class="hljs-variable">$read</span></span><span> = </span><span><span class="hljs-title function_ invoke__">array_diff</span></span><span>(</span><span><span class="hljs-variable">$read</span></span><span>, [</span><span><span class="hljs-variable">$sock</span></span><span>]); </span><span><span class="hljs-comment">// これを読み取りリストから削除します socket</span></span><span>
} </span><span><span class="hljs-keyword">elseif</span></span><span> (</span><span><span class="hljs-variable">$bytes_received</span></span><span> > </span><span><span class="hljs-number">0</span></span><span>) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Received data: <span class="hljs-subst">$data</span></span></span><span>\n";
}
}
}
}
</span><span><span class="hljs-comment">// より多くの処理ロジックをここに追加できます,たとえば、書き込み操作を確認してください、タイムアウト处理等</span></span><span>
}
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-meta">?></span></span><span>
</span></span>
ソケットの作成とバインド: Socket_Createを使用してTCPソケットを作成し、 Socket_Bindを使用して指定されたアドレスとポートにバインドします。
Socket_Selectは複数のソケットに耳を傾けます。Socket_Selectで聴く$読み取り配列、そして$サーバーソケットを使用して新しいクライアント接続を受け入れます。 $タイムアウトパラメーターは10秒に設定されています。つまり、10秒以内に、データが渡されない場合、または新しい接続が渡された場合、 Socket_Selectは返されます。
受信データ:読み取るデータがある場合、 socket_recvを介して非ブロッキング受信が実行されます。クライアントが切断された場合は、対応するソケットを閉じて$ read配列から削除します。
非ブロッキングモード:この場合、 MSG_DontWaitフラグを使用して非ブロッキング受信を実現しました。つまり、読み取るデータがない場合、 Socket_Recvはプログラムをブロックしません。
Socket_Selectの使用: Socket_Selectは、複数のソケットのステータスを確認するために使用されます。非ブロッキングモードでは、常にソケットからのデータを待機しないようにしますが、データが利用可能なときにすぐに処理できます。
メモリ管理: socket_selectを使用する場合は、メモリリークと無効なソケット検出を避けるために、 $ read配列から閉じたソケットを削除してください。
socket_recvとsocket_selectを組み合わせることで、非ブロッキングレセプションを非常に便利に達成できます。この方法は、データを待機しているためにプログラムをブロックするのを防ぐだけでなく、複数のソケット接続を効率的に処理し、より効率的なネットワーク通信を実現することもできます。
非ブロッキング受信は重要な技術であり、特に高度な低下ネットワークサービスを構築する場合に役立ちます。 PHPソケットプログラミングを柔軟に使用することで、アプリケーションのパフォーマンスとスケーラビリティを改善できます。