I'm writing an android source(but just java) to communicate with some embedded device which acts as a server.
I am sure that my UDP datagrams are arriving to the device since I can observe the device state changing.
But the problem is that I am failing to get response from the server. Not receiving nothing, but I just get an echo of what I sent. My source is as below.
public void sendSnapShot(View view) {
//send a udp datagram to the server.
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
try {
Log.e("Test", "send sendSnapShot onLine");
DatagramSocket clientSocket = new DatagramSocket();
byte[] sendData = new byte[1024];
String sentence = "$SNAPSHOT";
sendData = sentence.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("192.168.5.255"), 9999);
clientSocket.send(sendPacket);
Log.e("Test", "Sent sendSnapShot REQUEST");
} catch (Exception e) {
Log.e("Test", "e", e);
}
return null;
}
}.execute();
}
The above code is about transfering datagram to the server. On the start of the application, the below thread will be started to listen for any datagram sent by the server.
private class ListenerThread implements Runnable {
//listen for incoming datagrams.
#Override
public void run() {
DatagramSocket serverSocket = null;
try {
InetAddress serverAddr = InetAddress.getByName("192.168.5.255");
serverSocket = new DatagramSocket(9999, serverAddr);
while (true) {
try {
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
receivePacket.getLength();
String receivedData = new String(receivePacket.getData(), 0, receivePacket.getLength());
if (!receivedData.startsWith("!HEARTBEAT")) {
Log.e("Test", "Received : " + receivedData);
} else {
Log.e("Test", "Received : HEARTBEAT");
}
} catch (Exception e) {
Log.e("Test", "FROM SERVER ", e);
}
}
} catch (Exception e) {
Log.e("Test", "Exception", e);
}
}
}
The guy who wrote the server code(probably written in c++) says that he is getting response in his test case, so what may I be missing? Is there a possiblity that the above code will ignore any datagram from the server and echo bytes sent from my code?
=============================================================================
I've changed my code according to the answer. I no longer use the listening thread anymore. Here's my code.
public void sendBattery(View view) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
try {
Log.e("Test", "send Battery onLine");
DatagramSocket clientSocket = new DatagramSocket(9999, InetAddress.getByName("0.0.0.0"));
byte[] sendData = new byte[1024];
byte[] receivedata = new byte[1024];
String sentence = "$S0B255";
DatagramPacket receivePacket = new DatagramPacket(receivedata, receivedata.length);
sendData = sentence.getBytes();
String receivedData = " ";
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("192.168.5.1"), 9999);
clientSocket.send(sendPacket);
do{
clientSocket.receive(receivePacket);
receivedData = new String(receivePacket.getData(), 0, receivePacket.getLength());
Log.e("Test", receivedData + ", IP CHECK Sender: : " + receivePacket.getAddress().toString() + ", port : "+ receivePacket.getPort());
}while(true);
// Log.e("Test", "Sent BATTERY REQUEST COMPL");
} catch (Exception e) {
Log.e("Test", "e", e);
}
return null;
}
}.execute();
}
According to the heartbeat byte(making it to a string will make it "!HEARTBEAT") i'm receiving, the sender is 192.168.5.1. But still, I only get heartbeat bytes only. What shall I be missing?
You are receiving your own datagrams, because you are broadcasting (to 192.168.5.255). You should use the device's actual IP address.
You should not create a new datagram socket per send. (You are never closing that socket, so you are also leaking sockets.)
You should send the datagrams from the same socket you are receiving on. The device will (should) reply to the IP:port the request came from, and at present that's different every time, and you're losing the sending socket that's bound to that port, and the sending IP:port isn't the IP:port you're listening on, so you don't hear it.
You should not bind your listening socket to a specific IP address, and certainly not to a broadcast address. I'm surprised it works.
What EJP was saying was right about everything.
The fixed code above(the most below code snippet) which included tips from him will work, without having to receive any broadcast by myself.
I thought the issue was unresolved since I still didn't get any response from the device, but it was the device(not made by me) which was malfunctioning. The above code will work good.
Related
I have a Multicast socket open and is receiving Multicast message. From this thread, it seems that the same multicast socket should also be able to receive unicast messages. However, I'm not able to get anything.
Edit: the port number seems the be problem. Port 3702 is used by ws-discovery for unicasting which is related to what I'm trying to do. I'm tracking down a problem where the client's probe to the service is not caught by the service's multicast socket. I'm running this on windows.
My multicast server:
class Server extends Thread {
MulticastSocket multicastSocket;
final Logger LOG;
final int PORT = 3702;
final String MULTICAST_ADDR = "239.255.255.250";
InetAddress multicastGroup;
public Server() {
LOG = Logger.getLogger("Server");
try {
multicastGroup = InetAddress.getByName(MULTICAST_ADDR);
multicastSocket = new MulticastSocket(PORT);
multicastSocket.setInterface(InetAddress.getLocalHost());
multicastSocket.joinGroup(multicastGroup);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
while (!Global.exit) {
byte[] buf = new byte[1000];
DatagramPacket recv = new DatagramPacket(buf, buf.length);
try {
multicastSocket.receive(recv);
String msg = new String(recv.getData(), StandardCharsets.UTF_8);
LOG.log(Level.INFO, "got: " + msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
And the client code:
public void directMsgTest(){
try {
DatagramSocket datagramSocket = new DatagramSocket( 8080,InetAddress.getLocalHost());
String msg = "direct msg";
byte[] buf = msg.getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), DST_PORT);
datagramSocket.send(packet);
datagramSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
It seems that this is Window's fault. It uses WS discovery in some of its services, thus using port 3702 and eating unicast packets send to port 3702 instead of giving it to my server.
I tried running this on Linux and it was fine.
I'm currently trying to follow this sample code that Michiel De Mey created on detecting LAN IP'.
See link for his code: https://demey.io/network-discovery-using-udp-broadcast/
Anyways, i tried following it and i basically got what he got. It works when i run both server and client on my pc, but when i run the server from my PC, and the client from my android application, the client doesn't register a reply from the server, or maybe doesn't even get anything out there. I'm rather unsure really.
I just figured that it might be because my stationary computer is linked to the network via a cable, while the the laptop im currently running the android emulator on is connected to the network by wifi, with a router separating the two.
Why does it not work via my local area network? (I'm connected correctly, and i set the StrictMode ThreadPolicy in android to permitAll), so i know that aint the problem.
Thanks in advance!
For the server side i have this piece of code:
public class DiscoveryThread implements Runnable {
public static void main(String[] args){
Thread discoveryThread = new Thread(DiscoveryThread.getInstance());
discoveryThread.start();
}
DatagramSocket socket;
#Override
public void run() {
try {
//Keep a socket open to listen to all the UDP trafic that is destined for this port
socket = new DatagramSocket(6789, InetAddress.getByName("0.0.0.0"));
socket.setBroadcast(true);
while (true) {
System.out.println(getClass().getName() + ": Ready to receive broadcast packets!");
//Receive a packet
byte[] recvBuf = new byte[15000];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet);
//Packet received
System.out.println(getClass().getName() + ": Discovery packet received from: " + packet.getAddress().getHostAddress());
System.out.println(getClass().getName() + ": Packet received; data: " + new String(packet.getData()));
//See if the packet holds the right command (message)
String message = new String(packet.getData()).trim();
if (message.equals("DISCOVER_FUIFSERVER_REQUEST")) {
byte[] sendData = "DISCOVER_FUIFSERVER_RESPONSE".getBytes();
//Send a response
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, packet.getAddress(), packet.getPort());
socket.send(sendPacket);
System.out.println(getClass().getName() + ": Sent packet to: " + sendPacket.getAddress().getHostAddress());
}
}
} catch (IOException ex) {
Logger.getLogger(DiscoveryThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static DiscoveryThread getInstance() {
return DiscoveryThreadHolder.INSTANCE;
}
private static class DiscoveryThreadHolder {
private static final DiscoveryThread INSTANCE = new DiscoveryThread();
}
}
Client side:
public class UDPClient {
DatagramSocket c = null;
public void connect(){
try{
c = new DatagramSocket();
c.setBroadcast(true);
byte[] sendData = "DISCOVER_FUIFSERVER_REQUEST".getBytes();
try{
byte[] buf = new byte[256];
InetAddress ip = InetAddress.getByName("255.255.255.255");
DatagramPacket packet = new DatagramPacket(sendData,sendData.length,ip,6789);
c.send(packet);
System.out.println(getClass().getName() + ": Request packet sent to: 255.255.255.255 (DEFAULT)");
}catch(IOException e){
e.printStackTrace();
}
// Broadcast the message over all the network interfaces
Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = (NetworkInterface) interfaces.nextElement();
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
continue; // Don't want to broadcast to the loopback interface
}
for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
InetAddress broadcast = interfaceAddress.getBroadcast();
if (broadcast == null) {
continue;
}
// Send the broadcast package!
try {
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, broadcast, 6789);
c.send(sendPacket);
} catch (Exception e) {
}
System.out.println(getClass().getName() + ": Request packet sent to: " + broadcast.getHostAddress() + "; Interface: " + networkInterface.getDisplayName());
}
}
System.out.println(getClass().getName() + ": Done looping over all network interfaces. Now waiting for a reply!");
//Wait for a response
byte[] recvBuf = new byte[15000];
DatagramPacket receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
c.receive(receivePacket);
//We have a response
System.out.println(getClass().getName() + ": Broadcast response from server: " + receivePacket.getAddress().getHostAddress());
//Check if the message is correct
String message = new String(receivePacket.getData()).trim();
if (message.equals("DISCOVER_FUIFSERVER_RESPONSE")) {
//DO SOMETHING WITH THE SERVER'S IP (for example, store it in your controller)
System.out.println("Server's IP: "+receivePacket.getAddress());
}
//Close the port!
c.close();
} catch (IOException ex) {
// Logger.getLogger(LoginWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I have an UDP send and receive which works in my device Samsung Galaxy Ace Plus (S7500) but the same code doesn't work in other devices, for example Samsung Galaxy S4. I don't have any error.
Send :
public class SendThread extends Thread {
byte[] receiveData = new byte[1024];
DatagramSocket serverSocket = null;
public SendThread() {
this.start();
}
public void run() {
DatagramSocket serverSocket = null;
byte[] receiveData = new byte[1024];
byte[] sendData = new byte[1024];
try {
serverSocket = new DatagramSocket("MY SOCKET PORT");
InetAddress IP = InetAddress.getByName("MY IP");
String send= "I am Android";
sendData = send.getBytes();
DatagramPacket send = new DatagramPacket(sendData, sendData.length, IP, "MY SEND PORT");
serverSocket.send(send);
serverSocket.close();
} catch (Exception e) {
}
}
}
Receive :
public class ReceiveThread extends Thread {
byte[] receiveData = new byte[1024];
DatagramSocket serverSocket = null;
boolean isActive = true;
public ReceiveThread() {
this.start();
}
public void run() {
DatagramSocket serverSocket = null;
byte[] receiveData = new byte[1024];
while (isActive) {
try {
serverSocket = new DatagramSocket("MY RECEIVE PORT");
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
String sentence = new String( receivePacket.getData());
System.out.println("RECEIVED: " + sentence);
serverSocket.close();
} catch (Exception e){
}
}
}
}
This problem ocurred because some devices lock the Datagram receiver because the protocol security implemented by factory.
Your code is not wrong, but you need change the DatagramSocket for MulticastSocket.
For this your need execute some steps:
First, it's needed to add the uses-permission:
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
On AmdroidManifest.xml
Second, it's necessary create a MulticastLock; Without this the MulticastSocket is not work properly;
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
multicastLock = wifi.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
Thirdy, replace the DatagramSocket by MulticastSocket. Only on receive methods was needed put the code below or similar:
MulticastSocket ms = new MulticastScoket("Your socket port");
ms.joinGroup("Your IP");
It's not needed any modifies to send messages.
I use the multcast ip equals to 239.255.255.255. Attempt to range of multicast ip because the wrong ip will block the method flow correctly.
Finally, before use MulticastSocket it's needed to execute MulticastLock.acquire(), and after use execute MulticastLock.release();
It could be puted on service, and acquire or release MulticastLock on start or stop service.
I am currently exploring UDP packet transmission in Java to create a multiplayer game on Android.
I succeeded at exchanging packets within my Nexus 4 by using the usual "127.0.0.1" and I also succeeded at exchanging packets between my PC server and my Android client in my local network.
But since I will want my game to be playable on the Internet, I want my Android client to be able to exchange packets with my PC server when they aren't on the same local network. This is where I am struggling.
My setup : A PC server connected with my home Internet connection and a Nexus 4 connected with a 3G network.
First, my PC server starts listening on the port 10000 and my Android client opens a socket to receive server's packets on port 10001. Then, the Android client sends a packet to the PC server to its current public address "173.246.12.125" on port 10000. The PC server receives the packet and sends a response to the sender on port 10001. But the Android client never receives the response.
Here is my PC server code :
public class UDPServer {
private final static int SERVER_PORT = 10000;
private final static int CLIENT_PORT = 10001;
public static void main(String[] args) {
InetAddress clientAddr = null;
DatagramSocket socket = null;
try {
//Initializing the UDP server
System.out.println(String.format("Connecting on %s...", SERVER_PORT));
socket = new DatagramSocket(SERVER_PORT);
System.out.println("Connected.");
System.out.println("====================");
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
}
while(true){
try {
//Listening
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
System.out.println("Listening...");
socket.receive(packet);
//Getting client address from the packet we received
clientAddr = packet.getAddress();
System.out.println("Received: '" + new String(packet.getData()).trim() + "' from "+clientAddr.toString());
//Sending response
byte[] message = ("Hello Android").getBytes();
DatagramPacket response = new DatagramPacket(message, message.length, clientAddr, CLIENT_PORT);
DatagramSocket clientSocket = new DatagramSocket();
System.out.println("Sending: '" + new String(message) + "'");
clientSocket.send(response);
System.out.println("Response sent.");
System.out.println("--------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
And here are my Android client classes :
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Receiver()).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Client()).start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
public class Receiver implements Runnable {
private final static int LISTENING_PORT = 10001;
#Override
public void run() {
try {
//Opening listening socket
Log.d("UDP Receiver", "Opening listening socket on port "+LISTENING_PORT+"...");
DatagramSocket socket = new DatagramSocket(LISTENING_PORT);
socket.setBroadcast(true);
socket.setReuseAddress(true);
while(true){
//Listening on socket
Log.d("UDP Receiver", "Listening...");
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
Log.d("UDP", "Received: '" + new String(packet.getData()).trim() + "'");
}
} catch (Exception e) {
Log.e("UDP", "Receiver error", e);
}
}
}
public class Client implements Runnable {
private final static String SERVER_ADDRESS = "173.246.12.125";//public ip of my server
private final static int SERVER_PORT = 10000;
#Override
public void run() {
try {
//Preparing the socket
InetAddress serverAddr = InetAddress.getByName(SERVER_ADDRESS);
DatagramSocket socket = new DatagramSocket();
//Preparing the packet
byte[] buf = ("Hello computer").getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, serverAddr, SERVER_PORT);
//Sending the packet
Log.d("UDP", String.format("Sending: '%s' to %s:%s", new String(buf), SERVER_ADDRESS, SERVER_PORT));
socket.send(packet);
Log.d("UDP", "Packet sent.");
} catch (Exception e) {
Log.e("UDP", "Client error", e);
}
}
}
The server's console is showing the IP of the client :
Connecting on 192.168.1.126:10000...
Connected.
====================
Listening...
Received: 'Hello computer' from /204.48.72.68
Sending: 'Hello Android'
Response sent.
--------------------
Listening...
The packet seems to come from the address 204.48.72.68, but if I go on whatismyip.com on my Android, it shows me 96.22.246.97... I don't know where 204.48.72.68 is coming from...
I am not sure if the problem is that my listening socket on my Android client is not good or if the PC server does not send the response to the correct address.
Could someone points me what am I doing wrong?
Thank you
I came across a similar issue, but I was using TCP Sockets instead of UDP. My intense was sending files directly to a mobile. In LAN this worked pretty much.
It appears, that's not possible to send data to listening sockets, when your phone is connected to internet using the mobile connection.
I've read on some pages (sry dont have any links anymore), that incoming connections on a mobile phone is blocked by the telecommunications provider.
My workaround was to create outgoing connections to the server and use the bidirectional possiblities of tcp sockets.
Maybe you can use your "working" datagram socket, to exchange data with your mobile.
Here is an example, which i had found:
http://itucet.blogspot.de/2011/03/java-bidirectional-data-transfer-using.html
Same code working well for me, Have an issue when tried with emulator but its works fine if you use any android mobile .
The reason for the issue is android emulator and your computer are not in same subnet .
my android device is connected to my home-wireless-network. Also a special UDP-device is connected to it. My android app successfully can send commands to the UDP-device. But if I open a socket it does not receive data. Can you see what is wrong? I know the ip of the UDP-device from the iphone-APP which is working
Here is how the app send commands:
public static final String SERVERIP = "192.168.2.114";
public static final int PORT = 44444;
public void run() {
try {
serverAddr = InetAddress.getByName(SERVERIP);
DatagramSocket socket = new DatagramSocket();
byte[] buf = message.toByteArray();
DatagramPacket packet = new DatagramPacket(buf, buf.length, serverAddr, PORT);
socket.send(packet);
socket.close();
} catch (Exception e) {
Log.e("UDP", "Sender/Client: Error", e);
}
}
Whereas I have two approaches for receiving data:
public static final String SERVERIP = "192.168.2.114";
public static final int SERVERPORT = 44445;
private InetAddress serverAddr;
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
DatagramSocket socket = new DatagramSocket(SERVERPORT, serverAddr);
byte[] buf = new byte[65213];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
} catch (Exception e) {
Log.e("UDP", "Receiver: Error", e);
}
try {
serverAddr = InetAddress.getByName(SERVERIP);
DatagramChannel channel = DatagramChannel.open();
DatagramSocket socket = channel.socket();
byte[] buf = new byte[65213];
DatagramPacket packet = new DatagramPacket(buf, buf.length, serverAddr, SERVERPORT);
socket.receive(packet);
socket.close();
} catch (Exception e) {
Log.e("UDP", "Sender/Client: Error", e);
}
}
The approach in the first try block leads to an exception:
java.net.BindException: Cannot assign requested address
at org.apache.harmony.luni.platform.OSNetworkSystem.bind(Native Method)
at dalvik.system.BlockGuard$WrappedNetworkSystem.bind(BlockGuard.java:268)
at org.apache.harmony.luni.net.PlainDatagramSocketImpl.bind(PlainDatagramSocketImpl.java:81)
at java.net.DatagramSocket.createSocket(DatagramSocket.java:193)
at java.net.DatagramSocket.<init>(DatagramSocket.java:95)
at de.myappname.connection.Receiver.run(Receiver.java:29)
at java.lang.Thread.run(Thread.java:1019)
The second approach just blocks the thread by socket.receive(packet) which does not receive data. From the iphone and specification I know the device sends data via UDP 44445 over WLAN. Any suggestions what is wrong?
Thank you!
UDP port 44445 is used by eMule protocol. Do you have any other eMule clients active on your device?
Update:
The problem seems to be the address you bind to - it must be an address on the localhost, i.e. IP address of your device, not remote device. See DatagramSocket(port, InetAddress) constructor.
I guess you need to put the receive() function inside a while loop since you current code looks like it receives message only once; it doesn't guarantee that it will contain any valid data.