I'm making an application where there is a certain thread (MulticastListenerThread) which has a MulticastSocket and is listening for UDP (datagram) packets sent to the multicast group the socket is listening too.
This works. I can join a multicast group, send a message to that group and receive it through the MulticastSocket.
However, I would like to determine, from the receiver point of view, from which multicast group he received the packet. The following code gives me the address of the originator of the packet, not the multicast group:
DatagramPacket packet = new DatagramPacket(buf, buf.length);
mlcSenderSocket.receive(packet);
String src_addr = packet.getAddress().getHostAddress();
The code for sending the packet is as follows:
InetAddress address = InetAddress.getByName(dest);
packet = new DatagramPacket(payload, payload.length,
address, mlcEventPort);
LLog.out(this,"[NC] MLC packet Sent to ev port MLC " + mlcEventPort
+ " and to addr " + address);
mlcSenderSocket.send(packet);
Is it at all possible to determine which group sent the packet?
Edit:
It appears this isn't possible.
In terms of performance impact (I'm working for IoT devices), would assigning a socket per multicast group (and hence, a listener thread per group) be viable? Potentially many groups may be joined (in terms of tens or hundreds even). If it is viable, then I just need to keep the joined group address somewhere manually and refer to it as necessary.
Suggestions for other work arounds are welcome!
No group sent the packet. A socket at a specific IP address sent the packet, and the source IP address is available in the DatagramPacket. Multicast packets aren't sourced from multicast groups, they are addressed to multicast groups.
Yes it's true that you can join a MulticastSocket to multiple groups, for example:
InetAddress group;
MulticastSocket s=new MulticastSocket(12345);
NetworkInterface ni=NetworkInterface.getByName("eth1");
group=InetAddress.getByName("239.255.10.10");
s.joinGroup(new InetSocketAddress(group,12345),ni);
group=InetAddress.getByName("239.255.10.11");
s.joinGroup(new InetSocketAddress(group,12345),ni);
You then receive datagrams like this:
DatagramPacket datagram=s.receive(datagram);
Unfortunately there is no java API call in the DatagramPacket object that will allow you to determine which of the two groups was targetted by the sender, all you can get is the IP address of the network interface on which it was received (from the socket) and the sender's IP address (from the datagram).
To achieve what you want to do you're going to need to create multiple MulticastSocket objects and listen to one group per socket. You could use your own threads or NIO to listen on them all simultaneously.
Related
I would like to bind a MulticastSocket to the address 127.0.0.1 (Socket should only be reachable within the current host) but with the following code example i got a
java.net.SocketException: Network is unreachable: Datagram send failed exception
Is there a way to fix the problem? Here is my code
int port = 6677;
InetAddress group = InetAddress.getByName("232.0.1.10");
try(MulticastSocket s = new MulticastSocket(new InetSocketAddress(InetAddress.getByName("127.0.0.1"),port))){
String msg = "Hello";
s.joinGroup(group);
DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(),group, port);
s.send(hi);
}
Multicast is a little odd when compared to traditional UDP communication. The whole point is to share data on a known "channel", simultaneously, to anyone who wants access. This sharing is "signaled" to the network by using an IP address in the range 224.0.0.1 to 239.255.255.255. If you try to bind to 127.0.0.1, you just aren't doing Multicast anymore. And if you take a minute and think about it, that makes sense - you can't share the internal interface with other computers.
I'm new to Java socket programming and I'm currently developing a small peer to peer UDP chatting room application which allow multiple clients to chat with each other.
My question is how do I make a client discover all other connected clients once he hit the connect button providing only one of the connected clients ip and port? The program only runs on local network.
You can use a unique feature of UDP which is broadcasting
On IPv4 (which you are probably using) the address for broadcasting is 255.255.255.255. Any datagram sent to that address will be sent to all UDP clients on the network for that port.
What you can do for your chat application is to have each client send a packet to the UDP broadcast identifying itself, such as maybe the nickname of the user. All the other clients will see that packet, and you will be able to parse the packet and display a list of all the chat clients on the network.
Here is an example of sending a Datagram to broadcast:
DatagramSocket s = new DatagramSocket();
s.setBroadcast(true);
DatagramPacket dp = new DatagramPacket("insert data here".getBytes(), "insert data here".length(), new InetSocketAddress("255.255.255.255", 5000));
s.send(dp);
Another client can receive it like this:
DatagramSocket s = new DatagramSocket();
s.setBroadcast(true);
DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
s.receive(dp);
The received DatagramPacket will contain the IP and port of the client who had broadcasted it.
One simple possibility would be that every client stores the other peers it knows of and passes the list to any new clients connecting.
Don't forget of authenticating your peers. You can try to use the OpenSSL (very easy) to generate some certificates and use it in association to ssl.
Edit: a bot told me to be more specific so here it is:
Local Networks (LANs) are not always safe, so, to be sure, I would recommend you use OpenSSL to generate certificates for authentication and private keys for encryption, in this way, you can communicate safely.
Python ssl module is a good example.
I'm using multicast to implement a simple discovery service. Several instances on the application must be able to run on the same host, I therefore end up with several multicast sockets that are members of the same group on the same host.
On Linux, this works as expected. Every instance of the application on a host receives the messages sent to the multicast address.
On Windows however, only the first application to join the multicast group receives the messages sent.
Here is the code I'm using to create my multicast socket:
socket = new MulticastSocket(PORT);
InetAddress group = InetAddress.getByName(ADDRESS);
socket.joinGroup(group);
socket.setTimeToLive(TTL);
Is there some option I need to set? I've tried scouring the API but I can't find anything.
EDIT: According to this website, I need to set SO_REUSEADDR, but this can only be set before the socket binds. It seems the constructor for MulticastSocket seems to bind the socket upon creation.
you do this by creating the socket with no arguments and then call bind on that object.
socket = new MulticastSocket();
socket.setReuseAddress(true);//redundant, already set with empty constructor
SocketAddress sockAddr = new InetSocketAddress(PORT);
socket.bind(sockAddr);
InetAddress group = InetAddress.getByName(ADDRESS);
socket.joinGroup(group);
socket.setTimeToLive(TTL);
While working on a Java application under Mac OS X (Lion, 10.7.2, Java version "1.6.0_29"), I'm running into a strange problem while trying to send multicast UDP datagrams. The packets are only being sent on one NIC, and I have no control over which one.
The following example code illustrates what I am trying to do:
public class MCast {
public static void main(String[] args) throws IOException {
InetAddress multicastAddr = InetAddress.getByName("224.0.0.1");
int port = 58680;
byte[] data = "test".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, multicastAddr, port);
MulticastSocket socket = new MulticastSocket();
socket.joinGroup(multicastAddr);
socket.setNetworkInterface(NetworkInterface.getByName("en0"));
socket.send(packet);
System.out.println("Packet sent.");
}
}
Whilst executing this code, I am using Wireshark to examine all the traffic going out on en0. No packets are sent on the port specified. However, said packets do appear on the trace for en1.
When I disable en1, the packets go out on en0 properly.
I'm at a loss here. Does anyone know what's going on?
Multicast output interface is decided by the current routing table at the time you do the group join. In most cases that means default route, hence the en0. Adding an explicit route would help, but you can just reverse the two lines in your code to be:
socket.setNetworkInterface(NetworkInterface.getByName("en0"));
socket.joinGroup(multicastAddr);
Also, you don't need to join (nor bind()) the group if you are only sending and not listening to that multicast traffic. Instead, connect() to the group address.
You can pass a an address to the constructor of MulticastSocket. You can use it to bind it to the address you want.
We use socket.send(packet) function in java to send a "packet" to a given port.
My problem is that i have to send a packet to a shutdown system using UDP protocol.
The problem with send() function is that first it verifies whether the host IP is
multicast or not. Now my local area network is of broadcast type. So i am having
problem is using this function.
can anyone please give me a way ?
Do you know how send udp packets in java?
First of all, broadcasting must be enabled in your network.
Then, all your servers must join to agreed multicast address.
InetAddress address = InetAddress.getByName( "230.0.0.1" );
MulticastSocket socket = new MulticastSocket( 12345 );
socket.joinGroup( address );
When you want to activate the system shutdown, send an agreed message (for example, "die") to the multicast address.
DatagramPacket packet = new DatagramPacket( buf, buf.length );
socket.receive( packet );
String received = new String( packet.getData(), 0, packet.getLength() );
The servers when accept that message should initiate a shutdown flow.