I'm facing a very weird problem with receiving data using UDP in Android.
I'm writing an application to control a wifi module from an android device. I'm able to successfully send data to this remote wifi device. But I'm not able to receive 'complete' data packet from this wifi device.
My code in android is:
public static void receivePacket(int receiverPort, Context context) {
DatagramSocket socket = null;
String text = "";
try {
byte[] message = new byte[1500];
DatagramPacket packet = new DatagramPacket(message, message.length);
socket = new DatagramSocket(receiverPort);
//socket.setSoTimeout(5000);
for (int i = 0; i < 12; i++) {
socket.receive(packet);
text += new String(message, 0, packet.getLength()) + "\n";
}
socket.close();
Log.d("Received Message", text);
} catch (Exception e) {
Log.e("UDP", "S: Error", e);
} finally {
if(null != socket){
socket.close();
}
}
}
So if I'm expecting the data "$BEG1;$PID2;$PIP192.168.15.245;$PPN80;$DAT987654321;$END1;" I'm only getting "$BEG1;$PID2;$PIP192.168.15.245;$PPN80;$DAT98"
I tried to use UDP WinChat application to see if it's able to get the message from the wifi module and I'm able to get the entire data.
Also if i try sending a really long message to the android device using UDP Win Chat Application I'm able to get the entire data!
I'm totally confused! Please Help.
I was able to isolate the problem. (Still havent found the fix though :(...)
From the above code I'm making use of the same packet.getLength() for every iteration assuming that it will change each time according to the data it has received. But sadly that's not the expected behavior. The getLength() makes use of the previous value and truncates the newly arrived messages.
[Please note: This is a random behavior and doesn't happen all the time]
Now the question is, how do I change or refresh this attribute everytime I receive a new message within the loop?
You need to reset the DatagramPacket length before every receive. Otherwise it keeps shrinking to the smallest packet received so far.
Related
I am currently trying to get a little network communication going between a qt server and a java client.
In my example, the client wants to send an image to the Server. My problem is now, that the server never sees the data, so bytesAvailable() returns 0.
I already tried QDataStream, QTextStream and readAll(), still no data.
Server:
QTcpServer* tcpServer;
QTcpSocket* client;
tcpServer = new QTcpServer();
if(!tcpServer->listen(QHostAddress::Any, 7005)){
tcpServer->close();
return;
}
...
tcpServer->waitforNewConnection();
client = tcpServer->nextPendingConnection();
client->waitForConencted();
while(client->state()==connected){
// Syntax here might be iffy, did it from my phone
if(client->bytesAvailable()>0){
//do stuff here, but the program doesnt get here, since bytesAvailable returns 0;
}
}
CLient:
public SendPackage() {
try {
socket = new Socket(ServerIP, Port);
socket.setSoTimeout(60000);
output = new BufferedOutputStream(socket.getOutputStream());
outwriter = new OutputStreamWriter(output);
} catch (ConnectException e) {
System.out.println("Server error, no connection established.");
} catch (Exception e) {
e.printStackTrace();
}
}
public void Send(BufferedImage img) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, GUI.imageType, baos);
baos.flush();
byte[] imgbyte = baos.toByteArray();
System.out.println(imgbyte.length);
System.out.println("sending");
outwriter.write(imgbyte.length);
outwriter.flush();
// here i'd send the image, if i had a connection ...
output.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
The connection and everything builds up fine, the code even tells me when the socket was disconnected when trying to send, so I guess connection isn't a problem.
I just started using Qt, so if you guys have any idea to why this wouldn't work, I'd be pleased to try it.
client->waitForConencted();
// At this point the client is connected, but it is likely that no data were received yet
client->waitForReadyRead(-1); // <- Add this
// Now there should be at least 1 byte available, unless waitForConencted or waitForReadyRead failed (you should check that)
if(client->bytesAvailable() > 0) {
// ...
}
Note that you can not expect all the data to arrive at once. The TCP stream can get fragmented in any way and the data will be received in randomly sized pieces. You must repeat waiting and reading until you receive everything. This also means that you must somehow know when you did receive everything. So you need to know how much data is coming, or somehow recognize the end of it. You can for example disconnect right after the data transfer, or send the data length first. Depends on your application.
Also have a look at QIDevice::readyRead signal which would allow you to handle reading asynchronously.
I've written a basic udp client server where one android sends message to the server and server relays it to the rest of the clients.
The issue is, the incoming udp messages get to the server and server relays them back but they never reach the rest of the devices.
Whats really amazing is that if I use the server as echo server (i.e relaying only to sender) then everything works. All client and server sockets use same port 2706
Server code
while (true) {
DatagramPacket packetToReceive = new DatagramPacket(new byte[2048], 2048);
try {
listenerSocket.receive(packetToReceive);
InetAddress senderAddress = packetToReceive.getAddress();
relayedBytes += packetToReceive.getLength();
if (!connectedClients.contains(senderAddress)) {
connectedClients.add(senderAddress);
}
for (InetAddress addr : connectedClients) {
// commenting this line will make it an echo server
if (!addr.equals(senderAddress))
{
//The following actually prints the ips of the android
//devices so it knows where to send
System.out.println(senderAddress.getHostAddress().toString() +
" to " + addr.getHostAddress().toString());
byte[] data = packetToReceive.getData();
packetToReceive.setData(data, 0, packetToReceive.getLength());
packetToReceive.setAddress(addr);
listenerSocket.send(packetToReceive);
}
}
} catch (IOException) {
e.printStackTrace();
}
}
android sender logic:
mainSocket=new DatagramSocket(homePort);
//targetAddressString is the public IP of server
target = InetAddress.getByName(targetAddressString);
while (true) {
byte[] data = getdata();
if (data == null)
continue;
DatagramPacket packet = new DatagramPacket(data, data.length, target, targetPort);
mainSocket.send(packet);
}
meanwhile on other thread the reciever just waits with the same udp socket:
while (true) {
Log.d("Player", "Waiting for data");
DatagramPacket packet = new DatagramPacket(new byte[2048], 2048);
try {
mainSocket.receive(packet);
Log.d("Player", "packet received");
//do something with the packet
} catch (IOException e) {
e.printStackTrace();
}
}
It never moves further than waiting for data since it'll block until it receives a packet
Moreover I can also see it in the wifi and Mobile data icons that no data is ever received but data sending is always on and is seen received on the server
**EDIT:- Echo server **
while (true) {
DatagramPacket receivedPacket = new DatagramPacket(new byte[2048], 2048);
try {
listenerSocket.receive(receivedPacket);
InetAddress senderAddress = receivedPacket.getAddress();
if (!connectedClients.contains(senderAddress)) {
connectedClients.add(senderAddress);
}
for (InetAddress addr : connectedClients) {
byte[] data = receivedPacket.getData();
DatagramPacket sendPacket= new DatagramPacket(data, 0, receivedPacket.getLength(), addr, receivedPacket.getPort());
listenerSocket.send(sendPacket);
}
} catch (IOException e) {
// ...
}
}
Basically it should relay the message to every single client who've ever sent any data but somehow it only sends its to its original sender the rest of clients miss it. the code is trolling with me
This is the NAT traversal problem as you have figured out.
Here's a few hints:
The server can be hardcoded to listen on port 2706. But it shouldn't make any assumptions about what source port of received packets are. From your code, it doesn't look like you ever attempt to call setPort. So even if the NAT wasn't mapping your port number differently, I'm not sure how your original code was even getting the any destination port set. But I think you figured this out based on your own answer and your updated code.
Don't hardcode a client port on the client's socket. Choose port "0" (or don't set one) on your socket to let the OS choose the first available port for you. If you have multiple clients behind the same NAT, this behavior will allow for quicker client restarts, multiple devices behind the same NAT, and other nice things.
Holly Molly! I finally figured it out , it was my own fault I wasn't considering my cellphone provider's Nat and was assuming 2706 port in public IP as well.
Turns out actually I was under my cellphone network's NAT and my port 2706 was converted to some hideous port number by the time it reached the server. So I had to consider the port no of the actual packet received rather than the one set on the phone.
In a nutshell it was like this
cellphone (port 2706)-> NAT (port 40234) -> Server (port 40234)
and so I was actually trying to send back data to 2706 instead of 40234 -_-'
Once I start to send back the packets at 40234 (or whatever came with the packet) instead of 2706 , it gracefully followed the path back to my android cellphone and everything was fine.
I am trying to write a very simple SSDP discovery routine for a certain UPnP-enabled TV. Here is a stripped-down version of my code:
private void discover() {
String header = "M-SEARCH * HTTP/1.1";
String[][] fields = new String[][] {
{"ST", "ssdp:all"},
{"MAN", "\"ssdp:discover\""},
{"HOST", "239.255.255.250:1900"},
{"MX", "10"}};
String p=this.make_packet(header, fields);
MulticastSocket s = null;
ArrayList<String> devices=new ArrayList<String>();
String[] ret;
String[] loc;
try {
InetAddress addr=InetAddress.getByName("239.255.255.250");
s = new MulticastSocket(1900);
s.setReuseAddress(true);
s.setSoTimeout(3000);
s.joinGroup(addr);
DatagramPacket pack=new DatagramPacket(p.getBytes("UTF-8"), p.length(), addr, 1900);
s.send(pack);
byte[] buffer=new byte[1024];
DatagramPacket packrec=new DatagramPacket(buffer, 1024);
for(;;) {
System.out.println("Waiting for response...");
s.receive(packrec);
System.out.println(new String(buffer, 0, packrec.getLength()));
}
} catch (Exception e) {
System.out.println(e);
}
}
I compile and run the code on a Win8 machine via console. The socket always received exactly one response: a 1:1 copy of the message to be sent. This is probably correct, I guess, since the socket joins the multicast group in the beginning. However, no other UPnP devices reply, although I can see them in 3rd party UPnP inspectors.
When watching the network traffic with WireShark, no package seems to get sent by my code at all, although no exception is thrown. When scanning the network with a different UPnP inspector from the same machine, outbound packages are logged in WireShark (although oddly the replies of some devices are not, even though the inspector finds them).
I am messing around with this for four days now, but to no avail. Any ideas?
Thanks,
Eric
P.S.: JDK 1.8.0_45 (64bit)
My code can't receive UDP messages from outside my home net. The communication is between Android and Java computer application, with IP inside my LAN (for example 192.168.0.3) the code works, if I put my Java computer application inside my online server (and obviously I changed every IP with external IPs) this doesn't work; Android can send but it can't receive.
Android code :
#Override
protected Integer doInBackground(Void... params) {
DatagramSocket socket = null;
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
socket = new DatagramSocket(25565);
} catch (Exception e) {
Log.i("Ex ", "");
}
while (true) {
try {
socket.receive(packet);
String message = new String(packet.getData(), 0,packet.getLength());
Log.i("message", "" + message);
} catch (IOException e) {
Log.i("IO Ex", "");
}
catch (Exception e){
}
}
}
Java computer application code :
http://pastebin.com/2hVGeP6R
192.168.0.X is an internal NAT address. Any network can use it, but it can't be reached from anywhere outside. You either need to configure your router to pass it through to your PC and hit the router's external IP, or you need a real network address.
Read carefully this example. I suppose you that you are trying to read and write in the same socket while it is open. In case it not working paste some more code in order to help you
I am working on a application on MEGACO protocol on Controller side. I am sending MEGACO messages to the Media Gateway via UDP prptocol. And the Media gateway is answering the requests. When I run wireshark with specified port and IP filter wireshark shows all the captured MEGACO packets. But in my application (written in JAVA) some of the packets are not reaching. More specifically saying to my application only Transaction Reply and Transaction Reply Acknowledgement (Reference: RFC 3015) messages are not reaching.
I have tried a lot of permutations and combinations. Even I have allocated new Datagram Packet and buffer space for each receiving messages as test. But no result. My code for the udp receiver is following.
while (running) {
//do work here
try {
byte[] dpBuffer = new byte[MAX_BUFFER_SIZE];
DatagramPacket dp = new DatagramPacket(dpBuffer, MAX_BUFFER_SIZE);
this.socket.receive(dp);
byte[] temp = new byte[dp.getLength()];
System.arraycopy(dp.getData(), 0, temp, 0, dp.getLength());
System.out.println("Read data");
for(int i=0;i<temp.length;i++)
{
System.out.print((char)(temp[i]));
}
ByteArrayUtil msg = new ByteArrayUtil(temp, dp.getLength());
msgParser.parseMsg(msg);
} catch (Exception e) {
logger.error("Megaco Reader Failed to read Packet due to :" ,e);
}
}
Any help??
THanks E_net4.
As I have mentioned in my comment, I was using wrong filter in wireshark. in wireshark if you use only
"udp.port == x"
as the filter it will filter those packets having either source or destination port x. To filter those packets that have either source port == x or destination port ==x, you should use udp.srcport == x and udp.destport==xrespectively.
Thanks everyone.