1

ゲームクライアントとなるPHPコードを書いています。ソケットを使用します。socket_create、socket_connect、socket_read の順に続きます。問題なく動作しますが、問題はサーバーがいつでもパケットを送信できることです。つまり、socket_read は「ゲーム ループ」で常に発生する必要があります。だから、このようなもの:

<?php 
$reply = ""; 
do { 
     $recv = ""; 
     $recv = socket_read($socket, '1400'); 
     if($recv != "") { 
         $reply .= $recv; 
     } 
} while($recv != ""); 

echo($reply); 
?>

ループに陥っており (クライアントがゲームを終了するまでサーバーは接続を終了しない)、受信したパケットを PHP コードで処理する必要があるため、機能しません。

したがって、PHP には実際にはスレッドがありません。これを処理する最良の方法は何ですか?

4

7 に答える 7

1

できますが、@Andreyと@DampeS8Nに同意します。最良の選択ではありません。もしあなたがこれをやりたいと思っているなら、この本をチェックしてください: PHP で何したいですか?

于 2010-11-30T15:48:26.603 に答える
1

TCP 実装は、メッセージを断片化して結合する傾向があります。ソケットが受信したデータやメッセージフラグメントがどれだけ返されるかはわかりません。メッセージがどこで終わり、新しいメッセージが始まるかを知る必要があります (これは、1 回の読み取りで返されるデータで複数回発生する可能性があります)。いくつかの簡単な解決策:

  • ある種の区切り記号を使用します。各メッセージは '\0' で終了します。
  • メッセージと一緒にメッセージ サイズを送信します。各メッセージは、「Content-length: 42\n」または 2 サイズ バイト (0x00 0x42) で開始します。
  • XML を使用します。メッセージの<message>開始と終了。</message>

ただし、PHP の XML パーサーは不完全な XML を好まないため、手動で開始タグと終了タグを一致させたい場合を除き、3 番目のオプションは使用できません。プロトコルが ASCII に基づいている場合は最初のオプションを使用し、バイナリの場合は 2 番目のオプションを使用し、既に XML の場合は 3 番目のオプションを使用します。

ここで、パケットごとに任意の数のメッセージを取得できることを思い出してください。最も複雑なケースでは、前のメッセージの終わりに多数の完全なメッセージが続き、さらに別のメッセージの始まりが 1 つのパケットに含まれる場合があります。

完全な解決策は次のとおりです。

while (connected) {
    while (messages in buffer < 1) {
        read from socket;
        add to buffer;
    }

    while (messages in buffer > 0) {
        extract message from buffer;
        process message;
    }
}

...ただし、これは非同期メッセージ ループです。「利用可能なメッセージがある場合はそれを返し、そうでない場合はそれを待つ」という同期実装は演習として残しておきます。(ヒント: メッセージを作成してバッファリングするには、クラスが必要です。)

于 2010-11-30T16:33:12.867 に答える
1

基本的に、どのソフトウェア プラットフォームもこの問題に立ち向かうことになります。あなたが理解したように、ほとんどはスレッド化で解決します。PHPではスレッド化が可能ですが。MAJORHAXXX が必要です。php 内からコマンドライン php スレッドを起動するなど。

本当に理想通りにはなりません。

ただし、これを回避する方法は他にもあります。

ただし、最初にこのリストのすべてのマークを確認する必要があります。

[] - 私のゲームでは、プレイヤーの位置や複雑な動きなど、サーバーを常にチェックし続ける必要はありません。チャット ルーム レベルのデータ転送と更新レートを超えるものは、このボックスをオフのままにしておく必要があります。

[] - 私のゲームは、サーバーから何も伝える必要はありません。クライアントが必要なものを何でも要求することはまったく問題ありません。

[] - 私のゲームでは、リクエストを完了するのにかかる時間よりも長く、複雑な世界のシミュレーションをサーバー上で実行し続ける必要はありません。チャットを追跡することと、物理学とグラフィックスの変更を行うことは別のことです。

これらのチェックボックスをすべてオンにした場合、PHP はまだゲーム内にあります。さもないと。気にしないでください。

基本的に、私がここで言いたいのは、PHP は実際にはマルチプレイヤーではなく、ターン制であるか、少なくともあまりインタラクティブではないゲームに適しているということです。しかし、プレーヤーなしで物事を進めなければならない場合、PHP は顔をしかめます。

ブードゥーレベル

しかし、単にこれをしなければならない場合。それを回避する方法があります。

A - ワールドを実行する PHP デーモンを作成し、他のすべてのトラフィックを、データベースと対話する getter または setter 要求ファイルにパイプします。そのため、ゲーム ワールドの状態の取得を要求したり、プレイヤーが実行した値を設定したりできます。他のすべてのゲーム世界関連のものはデーモンによって処理され、ゲーム自体はデータベースで行われます。

B - デーモンではなく、cron を使用します。(危険ですが、私たちはすでにあなたをリスクテイカーとして確立していますよね?)

C - デーモンのみを試行し、ソケットをリッスンしてから、(exec() を介して) スレッドを送信して応答します。上記の AndreKR のアイデアのようなものですが、寝る必要はありません。ここでの問題は、ほとんどの場合、欠落したり、切断されたりすることです。そして、デーモンが何らかの形で 2 回実行されると、すべてが爆発する可能性があります..

于 2010-11-30T15:59:44.190 に答える
1

本当にこれを行いたい場合は、しばらくスリープして、ソケットを確認し、もう一度スリープして、ソケットを確認する必要があります...

socket_set_nonblock()ブロックせずにソケットをチェックするには、またはsocket_recv()DONTWAIT フラグを持つ非ブロック I/O を使用する必要があります。

于 2010-11-30T15:52:07.060 に答える
0

あなたがしなければならないことは、socket_select()関数を使用することだけです:

http://php.net/manual/en/function.socket-select.php

スクリプトをスリープ状態にし、ソケットに読み取るデータがあると起動します。定期的なスリープ/読み取り、cron スクリプト、およびその他の提案されたすべてのソリューションよりもはるかに効率的です。

@aibは有効な点を指摘しました。サーバーは完全な「ゲーム メッセージ」を複数のパケットに分割して送信する場合があります。リターン後のコード ブロックの 1 回の実行ですべてのデータを取得できるとは思わないでくださいsocket_select()

于 2010-11-30T17:21:38.573 に答える
0

PHP にはマルチスレッドがないため、より適切な言語を使用することを検討する必要があります (コメントで言及されている Andrey のように)。

于 2010-11-30T15:48:13.170 に答える
0

この臭いブロッキング ポーリング ループを記述する代わりに、Python Twisted や Ruby EventMachine などのリアクター パターンに基づくイベント システムを確認してください。

PHP フレーバーは PHP-MIO と呼ばれていると思います: http://thethoughtlab.blogspot.com/2007/04/non-blocking-io-with-php-mio.html

于 2010-11-30T19:03:35.480 に答える