Losing message when using multicast in local machine - java

I wrote a java program that is able to send/receive messages via a multicast address.
My code for multicast sender:
public boolean multicastSender(DataTransferObject message) {
try {
InetAddress group = InetAddress.getByName(ip);
MulticastSocket s = new MulticastSocket(port);
s.joinGroup(group);
String msg = jsh.dtoToJsonString(message);
DatagramPacket data = new DatagramPacket(
msg.getBytes(), msg.length(), group, port);
s.send(data);
s.leaveGroup(group);
return true;
} catch (IOException e) {
System.out.println(e.toString());
e.printStackTrace();
return false;
}
}
My code for multicast receiver:
public DataTransferObject multicastReceiver() {
try {
InetAddress group = InetAddress.getByName(ip);
MulticastSocket s = new MulticastSocket(port);
s.joinGroup(group);
byte[] buffer = new byte[1000 * 1024];
DatagramPacket data = new DatagramPacket(buffer, buffer.length);
String jsonData;
DataTransferObject dto;
while (true) {
s.receive(data);
jsonData = new String(buffer, 0, data.getLength());
dto = jsh.dtoFromJson(jsonData);
if (dto == null) {
continue;
} else {
return dto;
}
}
} catch (IOException e) {
System.out.println(e.toString());
e.printStackTrace();
return null;
}
}
My program has 4 parallel threads that listen and receive messages from four different multicast addresses (224.0.0.[1->4]) and ports [66601->66604]. In the test, I run two programs at the same time to test the communication between them. Yet I discovered that sometimes, program A sends a message to a multicast address, in a correct case, A will also receive it and so will B. But sometimes, I see that A announced that it sent the message but after that, neither A or B receive it again. I run the test on my local machine (Mac OS 10.9 with core 2 duo) . Should this problem because of my computer or something else?
Thank you very much.

I've seen this happen in a Windows environment. I understand that it's because the network layer is separate from the JVM.
What this means is one JVM may read and process/discard this message, yet other JVM's won't read the same message as the network layer has already delivered the UDP message to the first consumer, no matter how many JVM's have subscribed to the same group. This may or may not be a bug in the operating system - it's very subjective. Personally I think the OS should track registrants in order to replicate the delivery, as that's the intention of clients - it's a bit misleading to think otherwise given that an application is joining a group to receive multicasts. The operating system should take this into consideration.
I have come to this conclusion through empirical evidence, and may not be true for other platforms.

UDP, which I guess underlies your multicast implementation, does not guarantee delivery. The receiver or network will drop packets when overloaded. You'll need a higher-level protocol to ensure delivery (google reliable multicast).

Related

Router does not forward multicast packet across interfaces

My router has three network interfaces:
Ethernet;
2.4GHz antenna;
5GHz antenna;
I have a program that sends and receives packets on a multicast address (I set it to 239.5.6.7) on port 10468.
For my tests I have two devices (one sender and one receiver) that exchange information.
If both devices are connected to the same interface of the router (let's say, both connected to the 5GHz WiFi) then the devices will be able to communicate.
On the other hand, if the devices are on two different interfaces (one 5GHz and one Ethernet), packets will not be exchanged (as if the router does not forward multicast traffic to one interface to the others).
All three interfaces support multicast (examining traffic with WireShark shows that every host is able to join the multicast group), but it seems like the router is blocking this cross-interface exchange.
It really is a shitty router issue? Or I did something wrong on my end?
Both the sender and receiver communicate with the multicast group with all their interfaces (as long as they have a link-local IP address).
getActiveInterfaces() is a method that gathers such interfaces and saves them in an array list.
Sender snippet (getActiveInterfaces() returns only the ethernet connection):
static final int port = 10468;
public static void main(String[] args) {
try {
ArrayList<NetworkInterface> interfaces = getActiveInterfaces();
// Initialize sockets
ArrayList<MulticastSocket> sockets = new ArrayList<>(interfaces.size());
for (NetworkInterface networkInterface : interfaces) {
MulticastSocket socket = new MulticastSocket();
socket.setOption(StandardSocketOptions.IP_MULTICAST_TTL, 1);
socket.setNetworkInterface(networkInterface);
sockets.add(socket);
}
InetSocketAddress group = new InetSocketAddress(InetAddress.getByName("239.5.6.7"), port);
byte[] buf = new byte[128];
String msg = "Hello";
DatagramPacket toSend = new DatagramPacket(msg.getBytes(), msg.getBytes().length, group);
while (true) {
Thread.sleep(1000);
for (MulticastSocket socket : sockets)
socket.send(toSend);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Receiver snippet (getActiveInterfaces() returns only the 2.4GHz wireless connection):
static final int port = 10468;
public static void main(String[] args) {
try(MulticastSocket s = new MulticastSocket(port)) {
InetSocketAddress group = new InetSocketAddress(InetAddress.getByName("239.5.6.7"), port);
ArrayList<NetworkInterface> activeInterfaces = getActiveInterfaces();
for (NetworkInterface networkInterface : activeInterfaces) {
s.joinGroup(group, networkInterface);
}
DatagramPacket rec = new DatagramPacket(new byte[128], 128);
while(true) {
s.receive(rec);
System.out.println(new String(rec.getData(), StandardCharsets.UTF_8).replaceAll("\0", ""));
}
}catch (Exception e) {
e.printStackTrace();
}
}
After some investigations, I came to a conclusion that this is a router issue.
It seems that there is some sort of whitelisting going on.
For example, packets sent to the reserved Local Control Block multicast addresses (224.0.0.1-224.0.0.255) do not have this issue. Well-known multicast IPs are also whitelisted (like 239.255.255.250, used for SSDP).
Even changing to the next multicast address (239.255.255.249 or 239.255.255.251) leads to the same issue.
The only solution I could come up with is to use one of these whitelisted addresses and to use a different port number (not one linked with the actual address).
you may want to up your ttl to 3,
each time a multicast packet is received by a router the ttl is decremented by 1, when the ttl is 0 the packet is dropped.
Does it work with the sender and receiver on the same machine?
If it does, next as root, run
tcpdump multicast
on both machines
you should see something like
16:26:22.085822 IP debian.attlocal.net.51429 > 239.35.3.5.3535: UDP, length 1316

how much time java.net.DatagramSocket will wait for a client request if inactive for long time?

I am experiencing problems when using a UDP datagram packet, in the program I bind a UDP port and listen messages on it. This normally works fine, but if the port remains idle for a long time, the program automatically terminates the UDP socket. Unfortunately, the log file is huge and it is difficult to find the exception. Please help me find a way to keep the UDP port alive forever. Thanks in advance.
Here is my code:
socket = new DatagramSocket(port);
setBindSocket(true);
socket.setSoTimeout(60000);
while(isBindSocket()) {
try {
byte[] buffur = new byte[512];
DatagramPacket inputPacket = new DatagramPacket(buffur, buffur.length);
inputPacket.setLength(buffur.length);
socket.receive(inputPacket);
byte [] bString = inputPacket.getData();
String hString = new String(bString);
} catch (SocketTimeoutException ste) {
} catch (Exception e) {
e.printStackTrace();
}
}
The following statement changes the socket's behavior when receiving - if no datagram arrives in 60 seconds, a SocketTimeoutException is thrown.
socket.setSoTimeout(60000);
Maybe I have misunderstood your question.
You're going to have to find that exception. Unless you set a read timeout, the read method will block forever.

Multicasting message from Android app to local server

I'm developing an Android app that at some points, sends a multicast message. I'm running this on an emulator device so far.
On the same machine, I have a server (not Android, a plain Java app) that is expecting the multicast message, but it never gets it. When I start the server, since it is on my local machine, I start it the loopback interface (127.0.0.1). I must say that I've done this with regular Java apps and it works perfectly.
Here's the code for the Android App:
try {
InetAddress group = InetAddress.getByName(MULTICAST_HOST);
byte[] data = DISCOVER_MESSAGE.getBytes();
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(60000);
DatagramPacket dp = new DatagramPacket(data, data.length, group, TcpipSIBDiscoverer.PORT);
ds.send(dp);
byte[] buf = new byte[1024];
dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
if (dp.getLength() > 0) {
byte[] tmp = new byte[dp.getLength()];
System.arraycopy(dp.getData(), 0, tmp, 0, tmp.length);
String received = new String(tmp);
Logger.debug(this, "Received from SIB: " + received);
SIBDescriptor sibDescriptor = createSIBDescriptor(received);
this.discoveryListener.connectorSIBDiscovered(sibDescriptor);
}
} catch (SocketTimeoutException e) {
Logger.error("Socket time excedeed while waiting a response when discovering SIBs. Trying again");
} catch (IOException e) {
Logger.error("There was some kind of IO error while waiting for a response when discovering SIBs. Trying again");
}
As you can see, I'm using a regular DatagramSocket instead of MulticastSocket. This works in plain Java apps, since the listening server address is 235.0.0.1:5555.
Not really sure if the code is not working or I have to do something in the emulator device so it can truly reach my loopback interface... Any ideas?
Thanks!
Alex
127.0.0.1 on android refers to the device's localhost (or the emulators).
To reach localhost of your 'local machine' you should use 10.0.2.2.
This is discussed in a lot of topics.

Java: Receiving an UDP datgram packet with multiple DatagramSockets

I am trying to implement a method that sends an UDP packet to multiple receivers. I thought that this should be doable setting setReuseAddress(true) on the receiving DatagramSocket instances.
My problem is that in certain conditions I need to limit the communication to the local computer - hence the localhost interface (useLocalhost=true in the demo code below). In such a case suddenly only the first receiver socket gets the incoming packet, the two other don't see anything.
I tested this on Windows (oracle 64bit) and Linux (OpenJDK 64bit), therefore I only see three possibilities:
This is an intended and known behavior (and I don't understand the whole mechanism - aka "bug in my brain")
There is a bug in the Java JRE
There is a bug in my code.
Does somebody have any experience on that topic and can me help to identify where the problem is located?
See below a minimal working example that demonstrates this. Note that I am using the broadcast address for simulating network packets that come from a real external host.
If everything goes right you should see three lines at the end (in this or a different order):
Thread-0 - packet received
Thread-1 - packet received
Thread-2 - packet received
 
public static void main(String[] args) throws Exception {
boolean useLocalhost = true;
InetSocketAddress addr;
String sendPacketTo = "192.168.1.255"; // we use broadcast so that packet comes from an real external address
if (useLocalhost)
sendPacketTo = "localhost"; // does not work (only listener 1 received packet)
addr = new InetSocketAddress(15002);
new MyThread(addr).start(); // Datagram socket listener 1
new MyThread(addr).start(); // Datagram socket listener 2
new MyThread(addr).start(); // Datagram socket listener 3
DatagramSocket so = new DatagramSocket();
so.setBroadcast(true); // does not change anything
so.connect(new InetSocketAddress(sendPacketTo, 15002));
so.send(new DatagramPacket("test".getBytes(), 4));
Thread.sleep(1000);
System.exit(0);
}
public static class MyThread extends Thread {
DatagramSocket socket;
public MyThread(InetSocketAddress addr) throws SocketException {
super();
setDaemon(true);
socket = new DatagramSocket(null);
socket.setReuseAddress(true);
socket.setBroadcast(true); // does not change anything
socket.bind(addr);
System.out.println("Listener started: " + socket.getLocalAddress());
}
public void run() {
byte[] buf = new byte[10];
DatagramPacket p = new DatagramPacket(buf, buf.length);
try {
socket.receive(p);
System.out.println(Thread.currentThread().getName() + " - packet received");
} catch (IOException e) {
e.printStackTrace();
}
}
}
192.168.1.255 is a broadcast address, so the datagram is broadcast, under the rules for UDP broadcast. 127.0.0.1 is a unicast address, so the packet is unicast. So you get different behaviour.
As #DavidSchwartz commented, your code is a mixture. Connecting to a broadcast address for example doesn't have a lot of meaning, and neither does binding to it. I think what you are looking for is multicast.
You can use multicast on localhost
However, there are several things you need to be careful of to make it work.
example:
lo0 (127.0.0.1)
en0 (192.168.0.111)
en1 (10.1.0.111)
for each interface 2 separate sockets, one for receiving, one for
sending. In the above example this means creating a total of 6
sockets.
Never bind() a socket that will send multicast UDP packets.
Always bind() a socket that will receive multicast UDP packets.
Never try to setsockopt() or reconfigure multicast sockets after you call bind()
Instead, when machine's interfaces change due to cables being unplugged/plugged,
destroy all send/receive multicast sockets and recreate them.
sample code:
iMulticastSocketInterfaceIPAddress would be one of the three interfaces
/* use setsockopt() to request that the kernel join a multicast group */
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr=inet_addr( "239.192.0.133" );
myAddress.sin_addr.s_addr = mreq.imr_multiaddr.s_addr;
mreq.imr_interface.s_addr=( htonl(iMulticastSocketInterfaceIPAddress) );
theErr = setsockopt( CFSocketGetNative( mSocketBroadcast ) ,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

Listening for TCP and UDP requests on the same port

I am writing a Client/Server set of programs
Depending on the operation requested by the client, I use make TCP or UDP request.
Implementing the client side is straight-forward, since I can easily open connection with any protocol and send the request to the server-side.
On the servers-side, on the other hand, I would like to listen both for UDP and TCP connections on the same port. Moreover, I like the server to open new thread for each connection request.
I have adopted the approach explained in: link text
I have extended this code sample by creating new threads for each TCP/UDP request.
This works correctly if I use TCP only, but it fails when I attempt to make UDP bindings.
Please give me any suggestion how can I correct this.
tnx
Here is the Server Code:
public class Server {
public static void main(String args[]) {
try {
int port = 4444;
if (args.length > 0)
port = Integer.parseInt(args[0]);
SocketAddress localport = new InetSocketAddress(port);
// Create and bind a tcp channel to listen for connections on.
ServerSocketChannel tcpserver = ServerSocketChannel.open();
tcpserver.socket().bind(localport);
// Also create and bind a DatagramChannel to listen on.
DatagramChannel udpserver = DatagramChannel.open();
udpserver.socket().bind(localport);
// Specify non-blocking mode for both channels, since our
// Selector object will be doing the blocking for us.
tcpserver.configureBlocking(false);
udpserver.configureBlocking(false);
// The Selector object is what allows us to block while waiting
// for activity on either of the two channels.
Selector selector = Selector.open();
tcpserver.register(selector, SelectionKey.OP_ACCEPT);
udpserver.register(selector, SelectionKey.OP_READ);
System.out.println("Server Sterted on port: " + port + "!");
//Load Map
Utils.LoadMap("mapa");
System.out.println("Server map ... LOADED!");
// Now loop forever, processing client connections
while(true) {
try {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
// Iterate through the Set of keys.
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext();) {
SelectionKey key = i.next();
i.remove();
Channel c = key.channel();
if (key.isAcceptable() && c == tcpserver) {
new TCPThread(tcpserver.accept().socket()).start();
} else if (key.isReadable() && c == udpserver) {
new UDPThread(udpserver.socket()).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println(e);
System.exit(1);
}
}
}
The UDPThread code:
public class UDPThread extends Thread {
private DatagramSocket socket = null;
public UDPThread(DatagramSocket socket) {
super("UDPThread");
this.socket = socket;
}
#Override
public void run() {
byte[] buffer = new byte[2048];
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String inputLine = new String(buffer);
String outputLine = Utils.processCommand(inputLine.trim());
DatagramPacket reply = new DatagramPacket(outputLine.getBytes(), outputLine.getBytes().length,
packet.getAddress(), packet.getPort());
socket.send(reply);
} catch (IOException e) {
e.printStackTrace();
}
socket.close();
}
}
I receive:
Exception in thread "UDPThread" java.nio.channels.IllegalBlockingModeException
at sun.nio.ch.DatagramSocketAdaptor.receive(Unknown Source)
at server.UDPThread.run(UDPThread.java:25)
10x
It should work. One of the problems with this code, it seems, is that the ByteBuffer size is set to 0, meaning that the datagram is discarded (as it mentions in the comments). If you need to receive any information over UDP and you are on a reliable network, you can set the size quite big and receive big datagrams made up of multiple packets. Otherwise, on an unreliable network, set this to the MTU size. Make sure you flip() the ByteBuffer after receiving anything in it.
Also, creating new threads for each request is a bad idea, create a 'session' thread for each different IP you receive in a HashMap or something, and then do a guarded block on the session object. Wake up the thread sleeping on that object when you receive a message after passing in new information. The selector code you have is designed to avoid the creation of threads in this way.
Edit: based on the above code, you're create a datagram channel and then using the socket to receive datagrams directly? That's doesn't make sense. Use the channel methods only after binding the channel. Also, don't do this in a separate thread. Your code isn't thread-safe and will bust itself up. Hand the received information off to the separate 'session' thread as mentioned earlier. The selector is designed to tell you what channels can be read from without blocking (although blocking is disabled anyway, so it will tell you what channels have data to be read from).
AFAIK, you should be able to listen for both TCP connections and UDP messages on the same port. It would help if you posted your UDP code, and the exception + stacktrace that you are seeing.
You can't use DatagramSocket.receive() in non-blocking mode. You have to use the read() or receive() methods of your DatagramChannel directly.
In fact as you are using non-blocking mode and a Selector, it is quite impossible to see why you're also using a UDPThread at all. Just call udpserver.receive() instead of starting the thread.

Categories