2

マルチプレイヤー対応の Google Play ゲーム サービスを使用するためにゲームを移植しています。ゲームはすでにソケットをサポートしているため、リアルタイム メッセージの代わりに RealTimeSocket を使用しています。

ソケットを取得するには、GamesClient.getRealTimeSocketForParticipant を呼び出します。その後、通常のソケットとして使用する入力ストリームと出力ストリームを取得できます。

私の問題は、getRealTimeSocketForParticipant を呼び出す前にデバイスがデータを受信すると、このデータを読み取れないことです。例えば:

デバイス A が getRealTimeSocketForParticipant を呼び出します。
デバイス A が「Hello」を送信します。
デバイス B が getRealTimeSocketForParticipant を呼び出します。
デバイス B は何も受信しません。
デバイス A は「World」を送信します。
デバイス B は「World」を受信します。

サンプル プロジェクト (ButtonClicker) の 1 つを変更し、ここで問題を再現しました。リアルタイム ソケットを使用するようにコードを変更し、startGame メソッドを次のように変更しました。

String mReceivedData = "";
byte mNextByteToSend = 0;

void startGame(boolean multiplayer)
{
    mMultiplayer = multiplayer;
    updateScoreDisplay();
    switchToScreen(R.id.screen_game);

    findViewById(R.id.button_click_me).setVisibility(View.VISIBLE);

    GamesClient client = getGamesClient();

    String myid = mActiveRoom.getParticipantId(client.getCurrentPlayerId());
    ArrayList<String> ids = mActiveRoom.getParticipantIds();
    String remoteId = null;
    for(int i=0; i<ids.size(); i++)
    {
        String test = ids.get(i);
        if( !test.equals(myid) )
        {
            remoteId = test;
            break;
        }
    }

    //One of devices should sleep in 5 seconds before start
    if( myid.compareTo(remoteId) > 0 )
    {
        try
        {
            //The device that sleeps will loose the first bytes.
            Log.d(TAG, "Sleeping in 5 seconds...");
            Thread.sleep(5*1000);
        }
        catch(Exception e)
        {

        }
    }
    else
    {
        Log.d(TAG, "No sleep, getting socket now.");
    }

    try
    {
        final RealTimeSocket rts = client.getRealTimeSocketForParticipant(mRoomId, remoteId);
        final InputStream inStream = rts.getInputStream();
        final OutputStream outStream = rts.getOutputStream();

        final TextView textView =((TextView) findViewById(R.id.score0));
        //Thread.sleep(5*1000); Having a sleep here instead minimizes the risk to get the problem.

        final Handler h = new Handler();
        h.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    int byteToRead = inStream.available();

                    for(int i=0; i<byteToRead; i++)
                    {
                        mReceivedData += " " + inStream.read(); 
                    }

                    if( byteToRead > 0 )
                    {
                        Log.d(TAG, "Received data: " + mReceivedData);
                        textView.setText(mReceivedData);
                    }

                    Log.d(TAG, "Sending: " + mNextByteToSend);
                    outStream.write(mNextByteToSend);
                    mNextByteToSend++;

                    h.postDelayed(this, 1000);
                }
                catch(Exception e)
                {

                }
            }
        }, 1000);
    }
    catch(Exception e)
    {
        Log.e(TAG, "Some error: " + e.getMessage(), e);
    }
}

このコードは、getRealTimeSocketForParticipant への呼び出しの 5 秒前に、2 つのデバイスのいずれかが確実にスリープするようにします。スリープしないデバイスの場合、出力は次のようになります。

スリープなし、ソケットを取得中。
送信: 0
送信: 1
送信: 2
送信: 3
送信: 4
受信データ: 0
送信: 5
受信データ: 0 1
送信: 6
受信データ: 0 1 2

これは予想どおりで、データが失われることはありません。しかし、他のデバイスの場合、次のようになります。

5秒で眠る…
受信データ: 4
送信: 0
受信データ: 4 5
送信: 1
受信データ: 4 5 6
送信: 2
受信データ: 4 5 6 7
送信: 3

最初のバイトは失われます。とにかくこれを回避する方法はありますか?

4

1 に答える 1

2

API を正しく理解している場合、リアルタイム ソケットを介して交換されるメッセージは現実的はないため、送信したすべてのメッセージをすべてのプレイヤーが受信したことを常に保証できるとは限りません。で使用されているネットワーク プロトコルに関する情報は見つかりませんでしたがRealTimeSocket、UDP であると思われます。

それが本当なら、何らかのハンドシェイクを自分で実装する以外にできることはほとんどないのではないかと思います。1 つのデバイス (例: ID が最も小さいデバイス) を「シンクロナイザー」として選択し、他のすべてのデバイスとセットを作成します。「どこにいるの? xy z」(もちろん文字通りではありません) などのメッセージ (「SYN」) を毎秒送信し、他の人が「私はここにいます! (y)」(「ACK」) と応答するまで続けます。セットが空になるまで、応答を送信したデバイスをセットから削除します。この時点で、全員に「ゲーム開始!」を送ります。そして続けてください。

これらのメッセージはいずれも失われる可能性があることに注意してください。「ACK」が失われた場合、次に「SYN」が送信されたときに、デバイスは再度応答する必要があります。「ゲームの開始」メッセージが失われた場合、幸運なことに、デバイスは別のメッセージを受信するまで待機し続けます。

最後の注意点: 基礎となるプロトコルが TCP であっても、100% 信頼できるわけではなく、信頼できるプロトコルはありません。この事実をまだ知らない場合は、詳細についてこの質問を参照してください。

于 2013-06-16T18:47:31.097 に答える