【问题标题】:RealTimeMultiplayer.sendReliableMessage messages received out of orderRealTimeMultiplayer.sendReliableMessage 收到乱序的消​​息
【发布时间】:2014-12-10 23:05:31
【问题描述】:

我正在使用 Games.RealTimeMultiplayer.sendReliableMessage 在 Android 上发送消息,但它们似乎无序通过。 concept document 声明:

可靠的消息传递。通过可靠的消息传递,可以保证数据传递、完整性和排序。

我将一个大数据包分成多个 1400 字节的消息,并使用 sendReliableMessage 发送每个消息,这是在一个循环中完成的。在该循环之后,我使用 sendReliableMessage 发送另一条消息,但在接收设备上,最后一条消息在其他一些消息之前通过。发送数据包的代码是:

public void sendPacket(Packet packet)
{
    try {

        Log.d(getPackageName(), "Sending packet: " + packet.getClass().getSimpleName());

        ByteArrayOutputStream byteOutpuStream = new ByteArrayOutputStream();
        ObjectOutputStream outputStream = new ObjectOutputStream(byteOutpuStream);
        outputStream.writeObject(packet);
        byte[] bytes = byteOutpuStream.toByteArray();
        outputStream.close();

        if(m_Participants != null) {
            for (Participant participant : m_Participants) {
                if (participant.getParticipantId().equals(m_LocalParticipantID) == false) {

                    Log.d(getPackageName(), "Sending object size " + bytes.length + " to participant " + participant.getParticipantId());
                    byte[] objectSize = ByteBuffer.allocate(4).putInt(bytes.length).array();
                    int sendResult = Games.RealTimeMultiplayer.sendReliableMessage(m_GoogleClient, this, objectSize, m_RoomID, participant.getParticipantId());
                    Log.d(getPackageName(), "Send object size result: " + sendResult);

                    Log.d(getPackageName(), "Sending packet to " + participant.getParticipantId());
                    int bytesRemaining = bytes.length;
                    if(bytesRemaining <= Multiplayer.MAX_RELIABLE_MESSAGE_LEN)
                    {
                        Log.d(getPackageName(), "Sending full packet " + bytesRemaining + " bytes");
                        sendResult = Games.RealTimeMultiplayer.sendReliableMessage(m_GoogleClient, this, bytes, m_RoomID, participant.getParticipantId());
                        Log.d(getPackageName(), "Send packet result: " + sendResult);
                    }
                    else
                    {
                        int sentAmount = 0;
                        byte[] bufferToSend = new byte[Multiplayer.MAX_RELIABLE_MESSAGE_LEN];
                        while(bytesRemaining > 0) {
                            int byteCountSent = 0;
                            if(bytesRemaining >= Multiplayer.MAX_RELIABLE_MESSAGE_LEN)
                            {
                                byteCountSent = Multiplayer.MAX_RELIABLE_MESSAGE_LEN;
                                for(int byteIndex = 0; byteIndex < byteCountSent; ++byteIndex)
                                {
                                    bufferToSend[byteIndex] = bytes[sentAmount + byteIndex];
                                }
                            }
                            else
                            {
                                byteCountSent = bytesRemaining;
                                bufferToSend = Arrays.copyOfRange(bytes, sentAmount, sentAmount + bytesRemaining);
                            }
                            Log.d(getPackageName(), "Sending " + bufferToSend.length + " bytes");
                            sendResult = Games.RealTimeMultiplayer.sendReliableMessage(m_GoogleClient, this, bufferToSend, m_RoomID, participant.getParticipantId());
                            Log.d(getPackageName(), "Send packet result: " + sendResult);

                            bytesRemaining -= byteCountSent;
                            sentAmount += byteCountSent;
                        }
                    }
                }
            }
        }
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

它的作用是首先发送包含数据包大小的消息,然后在必要时将数据包拆分并发送。日志输出如下:

First call to sendPacket:

Sending object size 62234 to participant p_CImSt4aNy73IiQEQAQ
Send object size result: 1

Sending packet to p_CImSt4aNy73IiQEQAQ
Sending 1400 bytes
Send packet result: 2
Sending 1400 bytes
Send packet result: 3
Sending 1400 bytes
...
Send packet result: 44
Sending 1400 bytes
Send packet result: 45
Sending 634 bytes
Send packet result: 46

Next call to sendPacket:

Sending packet: StartGamePacket
Sending object size 267 to participant p_CImSt4aNy73IiQEQAQ
Send object size result: 47

Sending packet to p_CImSt4aNy73IiQEQAQ
Sending full packet 267 bytes
Send packet result: 48

接收函数是,它获取 4 字节的数据包大小,然后在消息进入时重新组合数据包,如下所示:

@Override
public void onRealTimeMessageReceived(RealTimeMessage realTimeMessage)
{
    try {

        byte[] data = realTimeMessage.getMessageData();
        Log.d(getPackageName(), "onRealTimeMessageReceived: " + data.length + " bytes");

        // Are we expecting a packet?
        if(m_CurrentPacketData == null)
        {
            // Get the packet size
            if(data.length != 4)
            {
                Log.d(getPackageName(), "Error! 4 bytes expected for packet size, got " + data.length);
            }
            else
            {
                int packetSize = ByteBuffer.wrap(data).getInt();
                m_CurrentPacketData = new byte[packetSize];
                m_CurrentPacketReceiveCount = 0;
            }
        }
        else
        {
            // Append the data to the packet
            Log.d(getPackageName(), "Received data of " + data.length + " bytes");
            for(int byteIndex = 0; byteIndex < data.length; ++byteIndex)
            {
                m_CurrentPacketData[m_CurrentPacketReceiveCount + byteIndex] = data[byteIndex];
            }
            m_CurrentPacketReceiveCount += data.length;

            // Have we received it all?
            if(m_CurrentPacketReceiveCount == m_CurrentPacketData.length)
            {
                Log.d(getPackageName(), "Full packet received");

                ByteArrayInputStream byteInputStream = new ByteArrayInputStream(m_CurrentPacketData);
                ObjectInputStream inputStream = new ObjectInputStream(byteInputStream);
                Packet packet = (Packet) inputStream.readObject();
                inputStream.close();
                m_ReceivedPackets.add(packet);

                Log.d(getPackageName(), "Packet received: " + packet.getClass().getSimpleName());

                m_CurrentPacketData = null;
                m_CurrentPacketReceiveCount = 0;

                processReceivedPackets();
            }
            else
            {
                Log.d(getPackageName(), "Still " + (m_CurrentPacketData.length - m_CurrentPacketReceiveCount) + " bytes remaining");
            }
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

但是接收端的日志是这样的:

onRealTimeMessageReceived: 4 bytes
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 60834 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 59434 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 58034 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 56634 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 55234 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 53834 bytes remaining
...
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 17434 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 16034 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 14634 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 13234 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 11834 bytes remaining
onRealTimeMessageReceived: 634 bytes
Received data of 634 bytes
Still 11200 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 9800 bytes remaining
onRealTimeMessageReceived: 4 bytes
Received data of 4 bytes
Still 9796 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 8396 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 6996 bytes remaining
onRealTimeMessageReceived: 267 bytes
Received data of 267 bytes
Still 6729 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 5329 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 3929 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 2529 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes
Still 1129 bytes remaining
onRealTimeMessageReceived: 1400 bytes
Received data of 1400 bytes

正如您在接近尾声时所见,数据包乱序进入。其他人在 Android 上使用实时多人游戏时遇到过类似的问题吗?

【问题讨论】:

  • 您能否与您的解决方案分享一个示例? (即“最终更改了数据包结构以在标头中包含一个 ID,该 ID 随每个数据包递增,以便接收方可以跟踪数据包的顺序并重新排序无序到达的数据包。”)跨度>

标签: android google-play-services


【解决方案1】:

你解决了这个问题吗? 看起来实施并不能保证顺序。所以你可能会做的是检查这个消息ID(称为令牌并由sendReliableMessage重新调整)的重新回调结果(onRealTimeMessageSent),只有当结果OK时,才发送下一个数据包。

【讨论】:

  • 我最终将数据包结构更改为在标头中包含一个 ID,该 ID 随着每个数据包递增,以便接收方可以跟踪数据包的顺序并重新排序到达的数据包命令。这比等待每个数据包的确认要快一点。我认为你是对的,要么实现中存在错误,要么文档不正确。
  • 我也遇到过同样的问题,经过一些测试,我得出结论sendReliableMessage 不能保证数据包会以相同的顺序传送。我只是决定等待onRealTimeMessageSent 回调在发送方强制执行排序。
猜你喜欢
  • 1970-01-01
  • 2015-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
  • 2021-07-13
相关资源
最近更新 更多