【问题标题】:Losing data with RealTimeSocket使用 RealTimeSocket 丢失数据
【发布时间】:2013-06-17 15:01:20
【问题描述】:

我正在移植一个游戏以使用支持多人游戏的 Google Play 游戏服务。我正在使用 RealTimeSocket 而不是实时消息,因为游戏已经支持套接字。

为了获取套接字,我调用 GamesClient.getRealTimeSocketForParticipant,然后我可以获得输入和输出流,就像使用普通套接字一样。

我的问题是,如果设备在调用 getRealTimeSocketForParticipant 之前接收到数据,我将无法读取这些数据。例如:

设备 A 调用 getRealTimeSocketForParticipant。 设备 A 发送“Hello”。 设备 B 调用 getRealTimeSocketForParticipant。 设备 B 什么也没有收到。 设备 A 发送“世界”。 设备 B 接收“世界”。

我已经修改了一个示例项目 (ButtonClicker) 并在此处复制了问题。我已经修改了代码以使用实时套接字,并将 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 秒。对于不休眠的设备,输出将类似于:

不睡觉,现在正在获取插座。 发送: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

第一个字节丢失。有没有办法避免这种情况?

【问题讨论】:

    标签: android google-play-services


    【解决方案1】:

    如果我对 API 的理解正确,通过实时套接字交换的消息是 unrealiable,所以你不能总是保证所有玩家都收到了你发送的所有消息。我找不到有关RealTimeSocket 使用的网络协议的信息,但我怀疑它是 UDP。

    如果真是这样,恐怕除了自己实现某种handshake 之外,您无能为力。选择一个设备(例如:ID 最低的那个)作为“同步器”,并让它与其他所有设备创建一个集合。每秒发送一条消息(“SYN”),例如“你在哪里?x y z”(当然不是字面意思),直到其他人回复“我在这里!(y)”(“ACK”)。从集合中删除发送响应的设备,直到集合为空。此时,给大家发一句“游戏开始!”继续。

    请注意,任何这些消息都可能丢失:如果“ACK”丢失,下次发送“SYN”时,设备应再次应答。如果“游戏开始”消息丢失,运气不好,设备将一直等待,直到收到不同的消息,此时它应该认为自己可以开始(虽然延迟)。

    最后一点:即使底层协议是 TCP,它仍然不是 100% 可靠的,没有协议是。如果您还不知道这一事实,请参阅this question 了解更多信息。

    【讨论】:

    • 我也有同样的表情。 documentation 然而并没有提到任何关于这个案例的具体内容,所以我希望我错过了一些东西:-)。我最初的实现是建立在 TCP 套接字上的,因此它假定数据永远不会丢失或以错误的顺序接收。然而,它似乎与 RealTimeSockets 一起工作得很好,除了握手,这是一个烦人但可以解决的问题。
    • @user1883479 没错,在这种情况下你不能有相同的假设。引用您的链接文档:“套接字是一种不可靠的消息传输,因此数据包可能会或可能不会到达目的地。但是,完整性是有保证的。也就是说,单个数据包可能会丢失,但数据包不会损坏或被截断。数据包很少会乱序到达。”
    猜你喜欢
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 2011-12-19
    • 2013-05-14
    • 2018-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多