I'm porting a game to use Google Play Game Services with multiplayer support. I'm using RealTimeSocket instead of realtime message because the game already has socket support.
To get the socket I call GamesClient.getRealTimeSocketForParticipant, and then I could get input and output streams as use it as a usual socket.
My problem is that if a device receives data before the call to getRealTimeSocketForParticipant, I will not be able to read this data. For instance:
Device A calls getRealTimeSocketForParticipant.
Device A sends "Hello".
Device B calls getRealTimeSocketForParticipant.
Device B receives nothing.
Device A sends "World".
Device B receives "World".
I have modified one of the example projects (ButtonClicker) and replicated the problem here. I have modified the code to use realtime socket, and modified the startGame method to this:
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);
}
}
The code ensures that one of the two devices sleeps 5 seconds before the call to getRealTimeSocketForParticipant. For the device that doesn't sleep the output will be something like:
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
That's expected, no data lost. But for the other device I get this:
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
The first bytes are lost. Is there anyway to avoid this?
If i'm understanding the API correctly, the messages exchanged through a real time socket are unrealiable, so you can't always have assurance that all players received all messages you sent. I couldn't find info about the network protocol used by RealTimeSocket, but I suspect it's UDP.
If that's really the case, I'm afraid there's little you can do short of implementing some sort of handshake yourself. Choose one device (ex.: the one with the lowest ID) to be the "synchronizer", and have it create a set with every other device. Send a message ("SYN") such as "where are you? x y z" (not literally, of course) every second, until the others respond "I'm here! (y)" ("ACK"). Remove from the set the devices that sent a response, until the set is empty. At this point, send everyone a "game's starting!" and go on.
Note that any of these messages can be lost: if the "ACK" is lost, next time the "SYN" is sent the device should answer again. If the "game's starting" message is lost, tough luck, the device will keep waiting until it receives a different message, at such point it should consider itself free to start (though delayed).
One last note: even if the underlying protocol is TCP, it's still not 100% reliable, no protocol is. See this question for more info, if you don't know this fact already.
Related
I am developing an application in which i need to send 100+ of messages. After going through few threads i came to know there is limitation on sending messages like 100 messages can be send in an hour. To do so i divide my recipient list into chunks and place delay of 5 seconds between each chunk and 3 seconds delay in every message. The delay between chunks increase after every chunk and when it gets to 100 seconds it will reset to 5 seconds. After that it worked OK for 50 messages but when i raise recipient list it causing issues some messages didn't go at first place and shown as error messages in native.
Is there any standard way to achieve this i may need to send 100+ messages , how can i send multiple messages without any failure at once. If i need to place delay what should be the appropriate delay between chunks or messages.
Thanks in advance.
private final int MAX_SMS_IN_ONE_TIME = 10;
private final int DELAY_BETWEEN_CHUNKS = 5000;
public void sendMessage(arguments){
// Send long messages in chunk of 20 messages and put gap of increasing 5 seconds till 50 seconds and then reset.
final Iterator iterator = messageChunks.iterator();
new Thread(new Runnable() {
#Override
public void run(){
int interval =1;
while (iterator.hasNext()) {
for (final Contact contact :
(List<Contact>) iterator.next()) {
sendSMS(body, contact.getmMobileNumbers().get(0));
App.trackEvent("Message", "Sent", "Messages from our sms app");
}
}
try {
Log.i("chunk", "chunk # " + interval + " delay is " + DELAY_BETWEEN_CHUNKS);
Thread.sleep(DELAY_BETWEEN_CHUNKS * interval);
interval++;
if (interval == 10) {
interval = 1;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void sendSMS(final String message, final String phoneNo) {
try {
String SENT = "com.ebryx.smscustommessagegeneration"+""+System.currentTimeMillis()+""+((int)this.getmMessageId());
Intent intentMessageASendStatus = new Intent(SENT);
final PendingIntent pi = PendingIntent.getBroadcast(App.getContext(), ((int)this.getmMessageId()),
intentMessageASendStatus, PendingIntent.FLAG_CANCEL_CURRENT);
final ArrayList<PendingIntent> sentPI = new ArrayList<PendingIntent>(){{add(pi);}};
App.getContext().registerReceiver(new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent arg1) {
switch (getResultCode())
{
case Activity.RESULT_OK:
Log.i("tag","sent successfully ");
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Log.i("tag","Generic Failure");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Log.i("tag","No service failure");
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Log.i("tag","Airplane mode failure");
break;
}
}
}, new IntentFilter(SENT));
final SmsManager smsManager = SmsManager.getDefault();
final ArrayList<String> parts = smsManager.divideMessage(message);
new Timer().schedule(new TimerTask() {
#Override
public void run() {
smsManager.sendMultipartTextMessage(phoneNo, null, parts, sentPI, null);
}}, 3000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
There appears to be no official documentation for SMS send limits, from what I could find and the following site either.
Unfortunately there appears to be no public documentation from
Google’s Android development team on the issue.
This is from May 17, 2017
The only figures on sms limits I've been able to find are from Commonsware's site:
SMS Sending Limitations
Apps running on Android 1.x and 2.x devices are limited to sending 100 SMS messages an hour, before the
user starts getting prompted with each SMS message request to confirm
that they do indeed wish to send it.
Apps running on Android 4.x devices, the limits are now 30 SMS
messages in 30 minutes .../...
There appears to be no way around increasing that limit without rooting the phone. Where you need to change the following settings. The following will allow 1000 SMS to be sent per 180000ms == 30 minutes.
SMS_OUTGOING_CHECK_MAX_COUNT 1000
SMS_OUTGOING_CHECK_INTERVAL_MS 1800000
The usual (frustrating) Android issues with varying performance across devices also applies. One phone may perform at a different level to another.
This company has determined the maximum SMS capacities for some handsets with their product. SMS sending limits with FrontlineSync and Android. They also advise that rooting the phone may be needed to increase limits.
Related resources:
Check android package SMS limit?
is there a limit to the number of numbers to send an SMS?
Android Java: How To Send SMS & Receive SMS & Get SMS Messages dated 30 August, 2017.
I'm really new to the whole UDP concept, so I'm sorry If I say something stupid.
I want my app that runs on Android only to listen for UDP broadcasts.
I installed this plugin Cordova Chrome UDP
So my problem starts with that question:
If I want only to listen for messages do I have to create a socket or I just have to add listener like that:
chrome.sockets.udp.onReceive.addListener(receiveListener);
Maybe I have to create a socket first so it will know where to listen for broadcasts?
And is there an easy way to test the connection. I tried several things one of which was to connect one of the devices to the other's hotspot. Then I tried to send broadcast signal from one of the devices (after using setBroadcast to enable the broadcast of the socket) but the other didn't receive it. The onReceive event actually fired but only on the device which sent the broadcast message (which is pretty obvious but I don't know if that could be a good news so I mention it).
Here is some code :
function sendBroadcast() {
console.log("Sending broadcast");
var port = 1667;
var arr = new Uint8Array(256);
for (var i = 0; i < arr.length; i++) {
arr[i] = i;
}
chrome.sockets.udp.create(function(createInfo) {
chrome.sockets.udp.bind(createInfo.socketId, '0.0.0.0', port, function(result) {
chrome.sockets.udp.setBroadcast(createInfo.socketId, true, function(result){
chrome.sockets.udp.send(createInfo.socketId, arr.buffer, '255.255.255.255', port, function(result) {
if (result < 0) {
chrome.sockets.udp.close(createInfo.socketId);
} else {
chrome.sockets.udp.close(createInfo.socketId);
}
});
});
});
});
}
function createSocket(){
chrome.sockets.udp.create(function(createInfo) {
chrome.sockets.udp.bind(createInfo.socketId, '0.0.0.0', 0, function(result) {
chrome.sockets.udp.setBroadcast(createInfo.socketId, true, function(result){
console.log("The socket with id: " + createInfo.socketId + " is created");
});
});
});
}
On both devices I first call createSocket() and then from one of them I call sendBroadcast() but nothing is received on the other device.
I have a python server and about 10 android clients, using sockets. It is really important that when the server sends a message, all clients receive it at the same time (say 1/10th of a second of difference).
But the connection is over Wifi, and some devices get the message later than others, which gives a very messy result. I don't want to get the latency of every device because this is a very unreliable approach. I want something as accurate as possible.
For example, in FPS games, it is common to have a countdown at the start of the round, and every player can start playing at the same time. What kind of logic lies behind this?
As for what my code currently looks like:
I use a BufferedReader in android to read every line sent by the server. The server is a console application in which you can type a message, and when you press enter, every listed client receives it with a new thread for every client.
java method receiving messages:
private void readMessage() throws IOException {
String data;
while ((data = mBufferedReader.readLine()) != null) {
data = data.toUpperCase();
if (data.startsWith("POSITION")) {
String[] splitData = data.split("/");
Log.d(Constants.TAG, splitData[1]);
mMainActivity.setDevicePosition(Integer.parseInt(splitData[1]));
} else {
String message = data.substring(data.indexOf('/') + 1, data.length());
int devices = Integer.parseInt(data.substring(0, data.indexOf('/')));
if (message.length() >= devices) {
message += " ";
} else {
int difference = devices - message.length();
for (int i = 0; i < difference; i++) {
message += " ";
}
}
mMainActivity.printMessage(message);
}
}
}
python line :
for cl in clients_list:
start_new_thread(send_message_thread, (cl, message,))
I'm wondering if anybody can help me figure out what is causing the data I am sending to become corrupt.
My setup is currently an Arduino pro mini with a HM-10 bluetooth module connected (I have also tried HM-11 Module too) and an Android application to receive the bluetooth data.
Module setup: http://letsmakerobots.com/node/38009
If I send data with big enough intervals then the data is fine, but if I send the data continuously I see messages getting mixed up and lost. To test this I send "$0.1,0.2,0.3,0.4,0.5" to the Android application from the Arduino, sometimes the stream of data appears to send fine but other times it is really quite scrambled. Please see the below graphs that demonstrate this:
Good case:
Bad case:
Arduino code:
String inputString = ""; //Hold the incoming data.
boolean stringComplete = false; //Determines if the string is complete.
boolean realtime = false;
void setup()
{
Serial.begin(9600);
delay(500);
Serial.print("AT+START");
delay(500);
}
void loop()
{
if(stringComplete)
{
if(inputString.equals("rStart"))
{
Serial.println("$startACK");
realtime = true;
}
else if(inputString.equals("stop"))
{
Serial.println("$stopACK");
realtime = false;
}
else{
Serial.print(inputString);
}
inputString = "";
stringComplete = false;
}
if(realtime)
{
Serial.println("$0.1,0.2,0.3,0.4,0.5,0.6");
delay(10);
}
}
void serialEvent() {
while (Serial.available())
{
// get the new byte:
char inChar = (char)Serial.read();
if (inChar == '\n')
{
stringComplete = true;
}
else
{
inputString += inChar;
}
}
}
The Android side just receives the data and then parses it in an IntentService:
#Override
protected void onHandleIntent(Intent intent) {
//Incoming command.
String rawData = intent.getStringExtra(DataProcessingIntentService.REQUEST);
//Append our new data to our data helper.
Log.i(this.getClass().getName(), "Previous Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
DataProcessingHelper.getInstance().appendData(rawData);
Log.i(this.getClass().getName(), "New Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
commandStartIndex = DataProcessingHelper.getInstance().getData().indexOf("$");
commandEndIndex = DataProcessingHelper.getInstance().getData().indexOf("\n");
//Set this as the data starting point.
if(commandStartIndex != -1){
DataProcessingHelper.getInstance().offsetData(commandStartIndex);
}
//Ensure that a command has been found and that the end index is after the starting index.
if(commandStartIndex != -1 && commandEndIndex > commandStartIndex){
//Remove the command structure from the command.
command = DataProcessingHelper.getInstance().getData().substring(commandStartIndex+1, commandEndIndex-1); //Remove the \r\n end command.
DataProcessingHelper.getInstance().offsetData(commandEndIndex+1);
if(command.length() > 1){
//Split the data out of the comand.
splitData = command.split(",");
Log.i(this.getClass().getName(), "Broadcasting the processed data. (" + command + ")");
//Broadcast data.
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(DataProcessingIntentService.RESPONSE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(DataProcessingIntentService.RESPONSE, splitData);
sendBroadcast(broadcastIntent);
}else{
Log.e(this.getClass().getName(), "Command is less than 1 character long!");
}
}
}
Thank you for any help!
I have now figured out what was causing this problem. It appears that BLE only supports a maximum of 20 bytes per a transaction. The time between these transactions is different depending on what you are using. I'm currently using notifications which means that I can send 20 bytes every 7.5 milliseconds maximum. I have opted for 10 milliseconds to be safe. I will now need to look into breaking up packets into 20 bytes maximum to ensure no data corruption.
I'm trying to communicate with a USB device that uses interrupt transfer for communications.
it is not a polled device, either side may send at any time. All the examples I find seem to be poll-response where you send data first, wait for the send to complete, then wait for the response, process it and then go back to sending data again.
My code is modeled after the following which I found here on stackoverflow (I'm showing the original I based it on because my own code has a lot more going on and is less compact)
boolean retval = request.queue(buffer, 1);
if (mConnection.requestWait() == request) {
// wait for confirmation (request was sent)
UsbRequest inRequest = new UsbRequest();
// URB for the incoming data
inRequest.initialize(mConnection, mEndpointIn);
// the direction is dictated by this initialisation to the incoming endpoint.
if(inRequest.queue(buffer, bufferMaxLength) == true){
mConnection.requestWait();
// wait for this request to be completed
// at this point buffer contains the data received
}
}
The second requestWait() will block until something arrives, so I can't do another TX operation until I receive something! What am I missing?
You said: "The second requestWait() will block until something arrives, so I can't do another TX operation until I receive something"
Having written code of my own also based on the example you show, I think I understand where you are confused: The second requestWait() will return for any USB operation, not just the one that preceeded it. (from the Android API documentation "Note that this may return requests queued on multiple UsbEndpoints")
So if you queue a Send request even while you are waiting, your "receive waitRequest" will return, but for the Send endpoint. You should always check the endpoint of the result of waitRequest, or compare it to the initial request itself. If it matches inRequest, then it's actually the receive operation you were blocking on. If it doesn't match, compare it to your Send request (or in my example code below, I simply assume that it's a send response and ignore it)
You will need to queue send and receive requests from different methods or threads however rather than in the same loop as is implied by the code you supplied.
Here is the code from my own project (be aware that my app is running into heap corruptions, so the code below may not be perfect, but it does allow me to send even while a receive operation is pending)
So here is my receive loop, you'll see the similarities with your code:
while(mUsbDevice != null ) {
if (inRequest.queue(buffer, BUFFER_SIZE) == true) {
// (mUsbConnection.requestWait() is blocking
if (mUsbConnection.requestWait() == inRequest){
// this is an actual receive
// do receive processing here (send to conusmer)
} else{
Log.d(TAG, "mConnection.requestWait() returned for a different request (likely a send operation)");
}
} else {
Log.e(TAG, "failed to queue USB request");
}
buffer.clear();
}
I do the sending form another thread which uses messages to queue incoming send requests:
mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 1) { // 1 means send a 64 bytes array in msg.obj
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
final byte[] array = (byte[]) msg.obj;
buffer.clear();
buffer.put( array );
UsbRequest outRequest = new UsbRequest();
outRequest.initialize(mUsbConnection, mUsbEndpointOut);
outRequest.queue(buffer, BUFFER_SIZE);
Log.d(L.TAG, "Queueing request:"+outRequest);
// don't do a mConnection.requestWait() here, ReceiveThread is already listening
} else if (msg.what == 2) { // 2 means exit
Log.d(L.TAG, "SenderThread::handleMessage(): terminate");
Looper.myLooper().quit();
} else {
Log.e(L.TAG, "SenderThread::handleMessage(): unknow message type: " + msg.what);
}
}
};