Java peer to peer udp chatting room - java

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.

Related

Should i use the same port numbers when sending data through UDP?

When we send data (in this case) to a client/server, does this really matter to use the same port number?
My guess is no, because it doesn't matter which port you are using when sending data to. (The protocol gives it to you randomly internally - this is the idea?) The only thing has to be kept, the port has to be any availabe one on the receiver machine(above 1000, because those are reserverd by the system), and if that receiver decides to send something back, he or she will have enough information about sender: his IP address, port number ect. As far as i know, a received packed provides with all of that info.
Below is just an illustration of what i've said above.
public class Server {
public static void main(String[] args) {
GameServer server = new GameSever(9822);
server.start();
InetAddress address = null;
int port = 7877;
try {
address = InetAddress.getByName("192.168.0.2");
} catch (UnknownHostException e) {
e.printStackTrace();
}
server.send(new byte[] { 1, 2, 3 }, address, port);
}
}
When a server listens on a computer, it specifies a port it wants it's connections coming in from , so ports are important for setting up servers. This is useful as you can have multiple applications listening on different ports without the different applications accidentally talking to eachother. So you should decide on a port that isn't a standard( 80 is for HTTP for example) to exclusively use for you gameserver so the client knows which port to send the requests to.
If you want to handle multiple connections at once the best thing to do is threading.
When we send data (in this case) to a client/server, does this really
matter to use the same port number? My guess is no, because it doesn't
matter which port you are using when sending data to.
Firstly, use the terms client and server distinguishly(as generally client initiates by sending the message, to which the server responds).
Next, the port which you're using is logically of no significance, the reason being server uses request.getPort() to determine the port while seding the response; (request is a DatagramPacket sent by the client).
Though you can hardcode the port at server(if known beforehand), but, it is a bad idea. What in those applications where you've no idea about who sent the datagram packet?
Java documentation by Oracle also uses an example of client-server where client's port number is left for the constructor to pick. It mentions :
Mostly, the client uses a constructor that does not require a port number. This constructor just binds the DatagramSocket to any available local port.
It doesn't matter what port the client is bound to because the DatagramPackets contain the addressing information. The server gets the port number from the DatagramPackets and send its response to that port.
MORE INFO (taken from Java Network Programming) :
public DatagramSocket() throws SocketException
This constructor creates a socket that is bound to an anonymous port. For example:
DatagramSocket client = new DatagramSocket();
Pick this constructor for a client that initiates a conversation with a server. In this scenario, you don’t care what port the socket is bound to because the server will send its response to the port from which the datagram originated. Letting the system assign a port means that you don’t have to worry about finding an unused port. If, for some reason, you need to know the local port, you can find out with the getLocalPort() method.
NOTE : The same socket can receive the datagrams that a server sends back to it(underlying implementation).
Whereas, the below constructor creates a socket that listens for incoming datagrams on a particular port, specified by the port argument :
public DatagramSocket(int port) throws SocketException
Use this constructor to write a server that listens on a well-known port.
Short answer:
Your guess is correct.
Longer answer:
Client gets a random port number when sending data to a server and each packet has a header which contains the info about client's port, ip address ect. So server can easily retreive this information and send anything back.
Whereas the server needs to be bind to a specific port in order to clients be able to send data to that server because when you use UDP, you need to specify an IP address and a port.

Java UDP multicast, determine which group sent packet

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.

DatagramSocket Broadcast Behavior (Windows vs. Linux)

Backstory:
I have a wireless device which creates it's own SSID, assigns itself an IP address using auto-ip, and begins broadcasting discovery information to 255.255.255.255. (unfortunately, it does not easily support multicast)
What I'm trying to do:
I need to be able to receive the discovery information, then send configuration information to the device. The problem is, with auto-ip, the "IP negotiation" process can take minutes on Windows, etc (during which time I can see the broadcasts and can even send broadcast information back to the device).
So I enumerate all connected network interfaces (can't directly tell which will be used to talk to the device), create a DatagramSocket for each of their addresses, then start listening. If I receive the discovery information via a particular socket, I know I can use that same socket to send data back to the device. This works on Windows.
The problem:
On Linux and OSX, the following code does not receive broadcast packets:
byte[] addr = {(byte)169, (byte)254, (byte)6, (byte)215};
DatagramSocket foo = new DatagramSocket(new InetSocketAddress(InetAddress.getByAddress(addr), PORT_NUM));
while (true)
{
byte[] buf = new byte[256];
DatagramPacket pct = new DatagramPacket(buf, buf.length);
foo.receive(pct);
System.out.println( IoBuffer.wrap(buf).getHexDump() );
}
In order to receive broadcast packets (on Linux/OSX), I need to create my DatagramSocket using:
DatagramSocket foo = new DatagramSocket(PORT_NUM);
However, when I then use this socket to send data back to the device, the packet is routed by the OS (I'm assuming) and since the interface of interest may be in the middle of auto-ip negotiation, fails.
Thoughts on the following?
How to get the "working" Windows behavior to happen on Linux/OSX
A better way to handle this process
Thanks in advance!
I do not think this is the problem with the code. Have you checked if OSX/Linux has correctly allowed those address / port number through their firewalls? I had this simple problem too in the past =P..
FYI, there is a nice technology called Zero-configuration which was built to solve this problem. It is very easy to learn so I recommend you to having a look at that as well.
Good luck.

Communiation with multiple clients via Sockets

I have a Server Socket and 3-4 android devices as clients. I'm using TCP/IP for communications. Which is the best method. Should I use multiple ports for each client? Or should I use same port. If using same function then how should I identify the communication addressed to different devices?
No, you do not need several ports.
ServerSocket server = new ServerSocket(port);
while (true)
{
Socket socket = server.accept();
// do something with this socket - aka 1 client
new SomeClientClass(socket);
InputStream in = socket.getInputStream();
in.read(byte[]);
OutputStream out = socket.getOutputStream;
// out will only write response to its own client.
// when this new SomeClientClassis created, method returns to this point
// in while loop and waits for the next client
}
You can use one port. The client can send you its id. If it can't you can look at the clients IP address to workout which one it is.
There are thousands of TCP client/server code examples on the web, but I would start with the sample code which comes with the JDK,

Multiplayer networking only works on private network

So I'm making a simple multiplayer online applet game, and I was testing it using multicast UDP sockets instead of the typical client-server connection. This is not meant to be efficient or safe for that matter, just an experiment. Only problem is, when I try to have other people join the game from their house, it won't connect them to me, but when I use two separate computers, one that's wired in and one that's on the wifi, it works seemlessly. They can join their own game and connect to their own network, but not other peoples. Am I missing something big here? I'll post the relevant code.
InetAddress group;
DatagramPacket packet;
DatagramPacket messagePacket;
MulticastSocket socket;
socket = new MulticastSocket(4446); //random port
group = InetAddress.getByName("228.5.6.7"); //multicast address
socket.joinGroup(group);
//typical code for sending a packet
packet = new DatagramPacket(messageBuf, messageBuf.length, group, 4446);
Any ideas? I'm rather new to networking but find it a fun challenge and would like to continue learning more about it..if you have any other tips on top of helping me to solve this problem it would be appreciated.
You probably figured this out by now, but yes there is a huge problem you are missing. It will always work if you are local because your router doesn't mind distributing packets behind the private LAN. Anything outside the network will not want to work because the client will try to send a packet to the server, but the server is behind a NAT(Network address translation) and since the router didn't see the server send out a packet first, the router will just discard the client's packet and never connect. However if the server sends out a udp packet to try to connect, the router likes to switch ports so you don't know which port the packet will come out of. That's what packet forwarding is for on the router. So when the client sends a packet, it goes to a different port than expected and the router still discards it. There are solutions such as "hole punching". The easiest solution tho is to have a dedicated server outside of any NAT to handle the requests.

Categories

Resources