UDP Packets (via WiFi Direct) never arrive - android

those of you who read my previous questions may already know this: I'm currently trying to get a hold on the whole WiFi Direct stuff. What I've done so far is creating the connection.
What I'm trying to do is sending a packet via UDP to the groupOwner (which's IP is of course known) - but it seems to get lost on it's way each time..
Here's a bit of my code, I've got a service for receiving files and an intentservice for submitting them in the background:
FileReceiverService.java
#Override
public void run() {
Log.d(TAG, "Thread starting...");
try {
app.log("Opening UDP socket to receive files.");
DatagramSocket socket = new DatagramSocket(PORT);
app.log("Socket open!");
socket.setSoTimeout(5000);
app.log("Waiting for packet..");
while (isRunning && !isInterrupted()) {
DatagramPacket packet = new DatagramPacket(
new byte[WiFiPacket.PACKET_SIZE],
WiFiPacket.PACKET_SIZE);
try {
socket.receive(packet);
app.log("Received " + packet.getLength()
+ " bytes, trying to parse!");
parsePacket(packet);
} catch (SocketTimeoutException e) {
} catch (Exception e) {
app.log("Something went wrong: " + e.getMessage());
e.printStackTrace();
}
}
socket.close();
app.log("Closing UDP socket");
} catch (Exception e) {
e.printStackTrace();
}
}
Where the constant WiFiPacket.PACKET_SIZE is set to 1024*32 (32 KBytes, because I've received "ERRBLABLA MSG too long" errors with higher values).
FileTransferService.java
#Override
protected void onHandleIntent(Intent intent) {
App app = (App) getApplication();
Context context = getApplicationContext();
boolean rightIntent = false;
WiFiFile file = null;
if (intent.getAction().equals(ACTION_SEND_TEXT)) {
rightIntent = true;
file = WiFiFile.fromText(intent.getExtras().getString(EXTRAS_TEXT));
} else if (intent.getAction().equals(ACTION_SEND_FILE)) {
rightIntent = true;
file = WiFiFile.fromFile(intent.getExtras().getString(
EXTRAS_FILE_PATH));
}
if (rightIntent && file != null) {
app.getOnWiFiTransmissionChangedListener().onNewOutgoingTransfer(
file);
String text = intent.getExtras().getString(EXTRAS_TEXT);
String host = intent.getExtras().getString(
EXTRAS_GROUP_OWNER_ADDRESS);
DatagramSocket socket = null;
int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
Log.d(TAG, "Sending packets to " + host + ":" + port);
try {
socket = new DatagramSocket();
int bytesSent = 0;
for (WiFiPacket p : file) {
Log.d(TAG, "Preparing another packet..");
byte[] payload = p.getBytes();
DatagramPacket packet = new DatagramPacket(payload,
payload.length, InetAddress.getByName(host), port);
Log.d(TAG, "Sending packet..");
socket.send(packet);
bytesSent += payload.length;
Log.d(TAG, "Packet send! Contained " + payload.length
+ " bytes! All over we've sent about " + bytesSent
+ " bytes!");
List<WiFiFile> list = new ArrayList<WiFiFile>();
list.add(file);
app.getOnWiFiTransmissionChangedListener()
.onTransferProgressChanged(list);
}
app.getOnWiFiTransmissionChangedListener()
.onFileTransferSuccessful(file);
Log.d(TAG, "Client: Data written");
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
} finally {
if (socket != null) {
if (socket.isConnected()) {
socket.close();
}
}
}
}
}
The code may not be the best, but as for right now the only thing I care about is to receive the damn packet. :)
For your information: I'm splitting instances of the class WiFiFile into WiFiPackets which do not exceed the PACKET_SIZE limit, thus contain some sort of own header information about the file's offset, the total length, the sender (username/inetaddress) and stuff like that.
My logging tells me that about 25KBytes were sent, the socket.send does not throw any errors but instead calls my listeners (also the one telling me the transfer is completed).
As far as I know, UDP packets can easily be dropped on their way, but I've tried it roughly 10 times and I think the probability of a 25KB packet getting lost EVERYTIME is very small.
Do you see anything I'm missing? I'm staring at my code since hours, trying to figure it out, putting more and more debug/log statements in it, but without any progress.
Thanks!
PS:
IP addresses and port are correct.
PS2:
AndroidManifest.xml
<uses-feature
android:name="android.hardware.wifi.direct"
android:required="true" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />

Okay, I solved this problem.
I sent the messages directly after receiving the callback for a successful group creation, thinking that the group really would be "ready"... Solved this by sending out an initial message in a TimerTask every ~200-500ms, cancelling the Task after receiving ack.
Hope this helps some of you, facing the same problem. :)

Related

Android send udp broadcast silently fails

I want to implement service discovery by using the network's broadcast address. I am sniffing packets with WireShark to confirm that my UDP packets are not being sent. The network code is not being run on the UI thread. The DatagramSocket.send call returns with no exception thrown, but nothing is seen by other programs including WireShark. I have verified that the address returned by getWifiBroadcastAddress actually is the broadcast address of my network.
I have verified that the network supports broadcast by writing a C# program, run on another machine, and WireShark is detecting broadcast packets from this program.
Here is my Android Java code:
try {
DatagramSocket socket = new DatagramSocket(Protocol.INQUIRY_PORT);
socket.setBroadcast(true);
InetAddress broadcastAddr = getWifiBroadcastAddress();
byte[] data = new byte[10];
for(int i = 0; i < data.length; i++) {
data[i] = (byte) i;
}
DatagramPacket packet = new DatagramPacket(data, data.length,
broadcastAddr, Protocol.INQUIRY_PORT);
while(true) {
// Loops indefinitely, no errors/exceptions
socket.send(packet);
try {
Thread.sleep(5000);
} catch(InterruptedException ie) {
break;
}
}
} catch(IOException ioe) {
// Not logged
Log.d("Broadcast", "Error sending inquiry.");
}
The getWifiBroadcastAddress() method is as seen here: https://lab.dyne.org/AndroidUDPBroadcast
Does anyone know why this would fail silently? Like I said my C# program running on another box is working just fine, doing the same thing, sending the same data every 5s, and WireShark sees those packets, but nothing from the Android phone.
The following works for me, where I can broadcast a particular string value to a specified port (in your case Protocol.INQUIRY_PORT) on the other end(s), and all of the devices on the local subnet that are monitoring UDP on that port can recognize that string value, and accordingly can respond. I am broadcasting from the main thread, but listening for responses in an async task.
public void sendBroadcast(String messageStr) {
// Hack Prevent crash (sending should be done using an async task)
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
byte[] sendData = messageStr.getBytes();
try {
sendSocket = new DatagramSocket(null);
sendSocket.setReuseAddress(true);
//sendSocket.bind(new InetSocketAddress(Protocol.INQUIRY_PORT));
sendSocket.setBroadcast(true);
//Broadcast to all IP addresses on subnet
try {
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("255.255.255.255"), Protocol.INQUIRY_PORT);
sendSocket.send(sendPacket);
System.out.println(getClass().getName() + ">>> Request packet sent to: 255.255.255.255 (DEFAULT)");
} catch (Exception e) {
}
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
}
}
Following is the corresponding UDP response listener code inside an async task class:
protected String doInBackground(String... params) {
serverIP = "";
try {
//Keep a socket open to listen to all the UDP trafic that is destined for this port
InetAddress myHostAddr = InetAddress.getByName("0.0.0.0");
rcvSocket = new DatagramSocket(null);
rcvSocket.setReuseAddress(true);
rcvSocket.bind(new InetSocketAddress("0.0.0.0",Protocol.INQUIRY_PORT));
rcvSocket.setBroadcast(true);
while (true) {
Log.i("VIS","Ready to receive broadcast packets!");
//Receive a packet
byte[] recvBuf = new byte[15000];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
rcvSocket.receive(packet);
//Packet received
serverIP = packet.getAddress().getHostAddress();
Log.i("VIS", "Packet received from: " + serverIP);
String data = new String(packet.getData()).trim();
Log.i("VIS", "Packet received; data: " + data);
if (!data.equals("") && !data.equals(myInquiryString)) {
//break while loop and return IP address of server
break;
}
}
} catch (IOException ex) {
Log.i("VIS", "ServerDiscovery" + ex.getMessage());
}
return serverIP;
}

ObjectInputStream Android

I have some trouble with the ObjectInputStream in an little android project. On my Laptop I have a Server running, which respondes an int to a String, which comes from my client (running on my andoid device).
Here is my Server:
ServerSocket socketmaker = new ServerSocket(30001);
Socket tSocket = socketmaker.accept();
ObjectInputStream in = new ObjectInputStream(tSocket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(tSocket.getOutputStream());
String text = "";
int hashed = 0;
text = (String) in.readObject();
System.out.print("The Client asks with \""+text+"\"");
hashed = hash(text);
out.writeUTF(String.valueOf(hashed));
System.out.print(" and we answer with the hash \""+hashed+"\"\n");
out.close();
in.close();
tSocket.close();
And here comes the app:
public void onClick(View v) {
String server = host.getText().toString();
int portNr = Integer.parseInt(port.getText().toString());
try {
Socket tSocket = new Socket(server, portNr);
ObjectOutputStream out = new ObjectOutputStream(tSocket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(tSocket.getInputStream());
String text = content.getText().toString();
out.writeObject(text);
int re = Integer.parseInt(in.readUTF());
Toast.makeText(ClientActivity.this, re, Toast.LENGTH_SHORT).show();
out.close();
in.close();
tSocket.close();
} catch(Exception e) {
//print ex here...
}
}
host, port, conent are EditText's and allow the client to put in the serverinfo.
I run the server on a console and it receives the text from the client and prints out:
The Client asks with "test" and we answer with the hash "48"
But then the app closes with an error.
So the error has to be with the server sending the int and the app receiving it.But I also set the permissions in the Manifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
I also tried it with:
writeObject(int) - Integer.parseInt(readObject())
writeObject(String) - readObject()
writeInt(int) - readInt()
but none of them worked.
So please help me, and excuse my bad english (non native).
Thanks

Checking server availability doesn't work

I'm trying to check if server is available but when running this code I get error that server is not available or sometimes application freezes even server is properly running:
InetAddress in;
in = null;
try {
in = InetAddress.getByAddress(new byte[] { (byte)192, (byte)168, (byte)16, (byte)48});
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (in.isReachable(5000)) {
loadProduct();
} else
{
showAlertBox("Warning", "Server not available!");
}
} catch (IOException e) {
showAlertBox("Warning", "Server not available!");
}
Is there any better way to check if server is online?
Is there any better way to check if server is online
Yes. Try to connect to it. That is an infallible test. Don't do it until you need to, of course. Don't try to predict the future. Just connect and handle the failure.
I have tried with this
Socket socket = null;
boolean reachable = false;
try {
socket = new Socket("192.168.16.48", 80);
reachable = true;
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
showAlertBox("Warning", "Server not available!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
showAlertBox("Warning", "Server not available!");
} finally {
if (socket != null) try { socket.close(); } catch(IOException e) { }
}
It works, but sometimes I get meesage that that applications is not responding, even if socket timeout is adjusted.
You can try to locate the server using its host name. The advantage: unlike the dynamic ip address, the host name probably has a longer validity. In the following example, both network interfaces can refer to the same host.
byte[] b = { 10, 0, 0, 1 };
String name = "Hostname";
InetAddress ia = Inet4Address.getByAddress(b);
InetAddress ia2 = Inet4Address.getByName(name);
Another possible solution: before using network operations on android, you must get the corresponding permissions from the android manifest. I don't know exactly, which of the following are must-haves:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

Android TCP app hanging on inStream.readline()

This is a continuation of this question because it my orginal question was answered, but it did not solve the bug.
Question:
How do I fix the code hanging on this line inStream.readline()
My Intent:
This is in a thread that will loop through checking if there is an outMessage, if there is, it will send the message.
Next it will check it if there is anything in the in-stream, if there is, it will send it to the handler in my main activity.
Lastly, it will sleep for 1 second, then check again.
This should allow me to read/write multiple times without needing to close and open the socket.
Problem:
It is reading and writing better, but still not working properly
What is happening now:
If outMessage is initialized with a value, upon connection with the server, the socket:
writes and flushes the value (server receives & responds)
updates value of outMessage (to null or to "x" depending on how i have it hard-coded)
reads and shows the response message from the server
re-enters for the next loop
IF i set outMessage to null, it skips over that if statements correctly then hangs; otherwise, if i set outMessage to a string (lets say "x"), it goes through the whole if statement, then hangs.
The code it hangs on is either of the inStream.readline() calls (I currently have one commented out).
Additional info:
- once connected, I can type in the "send" box, submit (updates the outMessage value), then disconnect. Upon re-connecting, it will read the value and do the sequence again until it get stuck on that same line.
Changes since the referenced question:
- Made outMessage and connectionStatus both 'volatile'
- added end-of-line delimiters in neccesary places.
Code:
public void run() {
while (connectionStatus != TCP_SOCKET_STATUS_CONNECTED) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while (connectionStatus == TCP_SOCKET_STATUS_CONNECTED) {
try {
if (outMessage != null){
OutStream.writeBytes(outMessage + "\n");
OutStream.flush();
sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "OUT TO SERVER: " + outMessage);
outMessage = "x";
}
Thread.sleep(100);
// if (InStream.readLine().length() > 0) {
String modifiedSentence = InStream.readLine();
sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "IN FROM SERVER: " + modifiedSentence);
// }
Thread.sleep(1000);
} catch (IOException e) {
connectionLost();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The thread that makes the socket:
public void run() {
setName("AttemptConnectionThread");
connectionStatus = TCP_SOCKET_STATUS_CONNECTING;
try {
SocketAddress sockaddr = new InetSocketAddress(serverIP, port);
tempSocketClient = new Socket(); // Create an unbound socket
// This method will block no more than timeoutMs. If the timeout occurs, SocketTimeoutException is thrown.
tempSocketClient.connect(sockaddr, timeoutMs);
OutStream = new DataOutputStream(tempSocketClient.getOutputStream());
InStream = new BufferedReader(new InputStreamReader(tempSocketClient.getInputStream()));
socketClient = tempSocketClient;
socketClient.setTcpNoDelay(true);
connected();
} catch (UnknownHostException e) {
connectionFailed();
} catch (SocketTimeoutException e) {
connectionFailed();
} catch (IOException e) {
// Close the socket
try {
tempSocketClient.close();
} catch (IOException e2) {
}
connectionFailed();
return;
}
}
Server:
public static void main(String[] args) throws IOException {
String clientSentence;
String capitalizedSentence;
try {
ServerSocket welcomeSocket = new ServerSocket(8888);
SERVERIP = getLocalIpAddress();
System.out.println("Connected and waiting for client input!\n Listening on IP: " + SERVERIP +"\n\n");
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
while(true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clientSentence = inFromClient.readLine();
System.out.println("clientSentance == " + clientSentence);
String ip = connectionSocket.getInetAddress().toString().substring(1);
if(clientSentence != null)
{
System.out.println("In from client ("+ip+")("+ System.currentTimeMillis() +"): "+clientSentence);
capitalizedSentence = clientSentence.toUpperCase() + '\n';
outToClient.writeBytes(capitalizedSentence + '\n');
System.out.println("Out to client ("+ip+"): "+capitalizedSentence);
}
}
} catch (IOException e) {
//if server is already running, it will not open new port but instead re-print the open ports information
SERVERIP = getLocalIpAddress();
System.out.println("Connected and waiting for client input!\n");
System.out.println("Listening on IP: " + SERVERIP +"\n\n");
}
}
Thanks in advance!
Edits:
added the server code after updating
I tried messing around with setting the SoTimout for the socket but took that back out
Your server is specifically designed to receive exactly one line from a client and send exactly one line back. Look at the code:
while (true) {
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient = new BufferedReader(
new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(
connectionSocket.getOutputStream());
clientSentence = inFromClient.readLine();
String ip = connectionSocket.getInetAddress().toString()
.substring(1);
System.out.println("In from client (" + ip + "): "
+ clientSentence);
if (clientSentence != null) {
capitalizedSentence = clientSentence.toUpperCase() + '\n';
System.out.println("Out to client (" + ip + "): "
+ capitalizedSentence);
outToClient.writeBytes(capitalizedSentence + "\n");
}
Notice that inside the loop it accepts a new connection, reads exactly one line, and then writes exactly one line. It doesn't close the connection. It doesn't sanely end the conversation. It just stops reading.
A client that worked with this server would have to connect, send exactly one line, read exactly one line back, and then the client would have to close the connection. Your client doesn't do that. Why? Because you had no idea that's what you had to do. Why? Because you had no design ... no plan.
So that's your specific issue. But please, let me urge you to take a huge step back and totally change your approach. Before you write a single line of code, please actually design and specify a protocol at the byte level. The protocol should say what data is sent, how messages are delimited, who sends when, who closes the connection, and so on.
Otherwise, it's impossible to debug your code. Looking at the server code above, is it correct? Well, who knows. Because it's unclear what it's supposed to do. When you wrote the client, you assumed the server behaved one way. Was that assumption valid? Is the server broken? Who knows, because there's no specification of what the server is supposed to do.
You need to check if there is data available:
if (InStream.available > 0) {
String modifiedSentence = InStream.readLine();
sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "IN FROM SERVER: " + modifiedSentence);
}
But to be honest, even that is not ideal because you have no gurantee that the eond-of-line will have been received. If the server sends a few bytes but never sends the end-of-line then you will still be blocking forever. Production socket code should never rely on readLine but instead read into a buffer and check that buffer for end-of-line (or whatever criteria your protocol needs).
Didn't read closely enough, I thought InStream was an InputStream instance. InputStream has available. InputStreamReader has ready (which in turn calls InputStream.available. As long as you keep a refernce to either of these then you can see if data is available to be read.

Android UDP Client doesn't receive DatagramPacket

WiBro, which is used as a server module and client Nexus Galaxy ICS.
Both machines are 3g UDP communication.
The server will return only the received data has been implemented.
dSocket.send (sendPacket) is a smooth communication.
However, we have a problem.
dSocket.receive (recvPacket) does not receive the data.
The server sends the packet is wrong? If the client receives the packet is wrong?
The server sends data but, telecom equipment is taken to preserve the situation?
When only sending a packet, the client has not a problem.
but after receive code implementation, the client has a problem.
private class ClientThread implements Runnable{
#SuppressWarnings("finally")
#Override
public void run() {
// TODO Auto-generated method stub
DatagramSocket dSocket = null;
try{
InetAddress serverAddr = InetAddress.getByName(serverIpAddress.toString());
Log.d(TAG, "Connecting");
Log.d(TAG, "IP :::: " + serverAddr.toString());
dSocket = new DatagramSocket(SERVERPORT);
dSocket.setSoTimeout(5000);
byte[] arr_RecvPacket = new byte[1024];
connected = true;
while(connected){
try{
Log.d(TAG, "Sending Command :::: ( " + String.valueOf(i));
String strPacket = "Hey Server ( " + String.valueOf(i);
byte[] arr_Packet = strPacket.getBytes();
sendPacket = new DatagramPacket(arr_Packet, arr_Packet.length, serverAddr, SERVERPORT);
dSocket.send(sendPacket);
Log.d(TAG, "C:Send");
i++;
Log.d(TAG, "C:Make Recv Packet....");
recvPacket = new DatagramPacket(arr_RecvPacket, arr_RecvPacket.length);
Log.d(TAG, "C:Recving...");
dSocket.receive(recvPacket);
handler.sendEmptyMessage(0x01);
Thread.sleep(4000);
}catch(Exception e){
e.printStackTrace();
Log.d(TAG, "S:Error");
}
}
Log.d(TAG, "S:DataSocket close");
dSocket.close();
}catch(Exception e){
Log.d(TAG, "C:Error");
e.printStackTrace();
connected = false;
}finally{
connected = false;
if(dSocket != null){
dSocket.close();
}
return;
}
}
}
The server sends the packet is wrong?
Could be. Hard to say without seeing it. It should send the reply back to the IP:port embedded in the incoming datagram. Easiest way to do that is to reuse it, just change the data.
the client receives the packet is wrong?
It looks OK.
The server sends data but, telecom equipment is taken
to preserve the situation?
I don't know what this means.

Categories

Resources