我正在移植游戏以使用Google Play游戏服务和多人游戏支持。我正在使用RealTimeSocket而不是实时消息,因为游戏已经有套接字支持。
要获取套接字,我调用GamesClient.getRealTimeSocketForParticipant,然后我可以将输入和输出流用作普通套接字。
我的问题是,如果设备在调用getRealTimeSocketForParticipant之前接收数据,我将无法读取此数据。例如:
Device A calls getRealTimeSocketForParticipant. Device A sends "Hello". Device B calls getRealTimeSocketForParticipant. Device B receives nothing. Device A sends "World". Device B receives "World".
我修改了其中一个示例项目(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秒休眠。对于不休眠的设备,输出将类似于:
No sleep, getting socket now. Sending: 0 Sending: 1 Sending: 2 Sending: 3 Sending: 4 Received data: 0 Sending: 5 Received data: 0 1 Sending: 6 Received data: 0 1 2
这是预期的,没有数据丢失。但对于其他设备,我得到了这个:
Sleeping in 5 seconds... Received data: 4 Sending: 0 Received data: 4 5 Sending: 1 Received data: 4 5 6 Sending: 2 Received data: 4 5 6 7 Sending: 3
第一个字节丢失。反正有没有避免这个?
答案 0 :(得分:2)
如果我正确理解API,通过实时套接字交换的消息是unrealiable,因此您无法始终确保所有玩家都收到您发送的所有消息。我找不到RealTimeSocket
使用的网络协议的信息,但我怀疑它是UDP。
如果情况确实如此,我担心你自己实现某种handshake的可能性很小。选择一个设备(例如:ID最低的设备)作为“同步器”,并让它与其他所有设备一起创建一个设备。每秒发送一条消息(“SYN”),例如“你在哪里?x y z”(当然不是字面意思),直到其他人回答“我在这里!(y)”(“ACK”)。从集合中删除发送响应的设备,直到该集合为空。在这一点上,给每个人一个“游戏的开始!”然后继续。
请注意,任何这些消息都可能丢失:如果“ACK”丢失,下次发送“SYN”时设备应再次应答。如果“游戏的开始”消息丢失,运气不好,设备将一直等到收到不同的消息,此时它应该认为自己可以自由启动(虽然延迟)。
最后一点:即使底层协议是TCP,它仍然不是100%可靠,没有协议。如果您还不知道这一事实,请参阅this question了解更多信息。