Receive the data without own sent by QUdpSocket - android

I'm testing a P2P communication by udp with Qt 5.4(on Windows 10 64bit).
On Windows to Windows, this code can get a message("SendFromHost") from an another device. But on Android to Windows, this code got a own sent message("SendFromGuest") and finished the program.
Please tell me how to get a message without own sent.
void Network::start()
{
findLanSocket = new QUdpSocket(this);
connect(findLanSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(onUdpStateChanged(QAbstractSocket::SocketState)));
findLanSocket->bind(findPort, QUdpSocket::ShareAddress);
QByteArray datagram = "SendFromGuest";
findLanSocket->writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, findPort);
}
void Network::onUdpStateChanged(QAbstractSocket::SocketState s)
{
if (s == QAbstractSocket::BoundState) {
connect(findLanSocket, SIGNAL(readyRead()), this, SLOT(onReadyUdpRead()));
}
}
void Network::onReadyUdpRead()
{
QByteArray datagram; QHostAddress haddr;
datagram.resize(findLanSocket->pendingDatagramSize());
findLanSocket->readDatagram(datagram.data(), datagram.size(), &haddr);
QString rev = QString::fromUtf8(datagram);
if (rev == "SendFromHost"){
result = haddr.toString();
qDebug() << result;
success();
return;
}
}

A broadcast UDP datagram can also be received by its sender, that's the expected behavior. See How to ignore your own broadcast udp packets for instance.

Related

Listen to UDP broadcast with Cordova plugin

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.

How do you synchronize android clients with a python server?

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,))

Missing Offline messages asmack (Android)

I have offline messages option enabled in the openfire server.But I'm unable to get offline messages
User A is online ,User B is online ,in this case I'm able to get messages.
Now User B Turned off his WiFi(Note : User A waited till the user B Session completely killed in the server )
now User A send a message to User B
in this case I'm able to see the message in the openfire offline table.
Now User B Comes online again server is sending the message to user B as the server come to know that User B is online
(Message disappeared from offline messages table ).
But User B is not going to receive that message.
connection.login(userName, userPwd, UiUtility.getMyPhoneNO());
PacketFilter filter = new PacketTypeFilter(org.jivesoftware.smack.packet.Message.class);
packetListener =new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message) packet;
if (message.getBody() != null) {
String fromName = StringUtils.parseBareAddress(message
.getFrom());
Log.i("XMPPClient", "Got text [" + message.getBody()
+ "] from [" + fromName + "]");
}
}
};
connection.addPacketListener(packetListener, filter);
Again after successful login im able to chat normally.But I wonder why those offline messages are missing ? .My PacketListener unable to catch those offline messages .Please Help me
Asmack is depreceated. Use Smack. An Open Source XMPP Client Library written in Java for JVMs and Android. Add the following lines to your gradle file:
compile 'org.igniterealtime.smack:smack-android:4.1.3'
compile 'org.igniterealtime.smack:smack-tcp:4.1.3'
compile 'org.igniterealtime.smack:smack-extensions:4.1.3'
The problem is easy to be solved.
Before making connection with the XMPP server just register providers using ProviderManager class provided by ASmack library.
If this can't solve ur problem visit ur local server and search for offline messages, and select the option ALWAYS STORE setting the storage limit to be 1000 kb. It is 100 kb by default.
Hope this works.
After lot struggle, I have resolved the issue. In your openfire admin page, go to "client settings" and reduce the idle time from 360sec (by default) to 1 sec(may be). Only then when you disconnected from Internet, it can detect that you are offline and preserve rest of the messages as OFFLINE.
#Override
public void onNetworkConnectionChanged(boolean isConnected) {
if(isConnected){
new Thread() {
public void run() {
try {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
builder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
builder.setUsernameAndPassword("phone", "admin");
builder.setSendPresence(true);
builder.setServiceName(<Service name>);
builder.setHost(<Host name>);
builder.setResource("Test");
builder.setDebuggerEnabled(true);
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Available");
connection = new XMPPTCPConnection(builder.build());
connection.connect();
connection.login();
Presence presence123 = new Presence(Presence.Type.available);
presence123.setStatus("Available");
try {
connection.sendStanza(presence123);
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
StanzaFilter filter = new AndFilter(new StanzaTypeFilter(Message.class));
PacketListener myListener = new PacketListener()
{
public void processPacket(Stanza stanza)
{
retrieveMessage(stanza,userType);
}
};
connection.addPacketListener(myListener, filter);
try {
connection.sendStanza(presence);
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
} catch (SmackException | XMPPException | IOException e) {
e.printStackTrace();
}
//return connection.isConnected();
}
}.start();
The above is working fine and able to retrieve the offline messages. The method "retrieveMessage(stanza,userType);" is used to process the incoming message and update the Adapter. Make sure to send the Presence as "Available" when you reconnect. Please let me know if there are still any issues.

Problems when sending a continuous stream of data over BLE

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.

Losing data with RealTimeSocket

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.

Categories

Resources