How can I receive Multicast messages on Android? - java

I've got a very simple draw application on my PC that stores points and draws an oval for each point.
I'm trying to send this to an android application that will receive coordinates for each point and then draw the ovals.
Right now I'm using Multicast sockets, but it doesn't seem to work. I'm afraid I'm missing something.
The InetAddress and port are the same in both applications: 234.235.236.237 and 9876
In my PC application the MultiCastSocket initialization code looks like this:
private void InitiateSocket() throws IOException
{
// Create a socket and start the communication
socket = new MulticastSocket(port);
iAddr = InetAddress.getByName("234.235.236.237");
// Joins the multicastSocket group
socket.joinGroup(iAddr);
}
And when I want to send a coordinate I call this method:
public void SendMessage(int x, int y)
{
//Translate message to bytes
byte[] _data = (x+ " " + y).getBytes();
//Create and send a packet
DatagramPacket _packet = new DatagramPacket(_data, _data.length, iAddr, port);
try {
socket.send(_packet);
textArea.append(x + " " + y + " ");
} catch (IOException e) {
System.exit(0);
}
}
Then my Android application looks like this:
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("mydebuginfo");
multicastLock.acquire();
//Create a socket and start the communication
_socket = new MulticastSocket(_port);
// Joins the multicastSocket group
_socket.joinGroup(_iadr);
// Add a new Client to the socket
new Client(_socket, this);
When I want to send a message I call this, which is very similar to the one in the pc applikacation
private void SendMessage(String _s)
{
// Translate message to bytes
byte[] _data = (_name + ": "+ _s).getBytes();
// Create and send a packet
DatagramPacket _packet = new DatagramPacket(_data, _data.length, _iadr, _port);
try {
_socket.send(_packet);
} catch (IOException e) {
_field.append(e.toString());
}
}
Then the Client class listens to the socket with this code:
#Override
public void run() {
byte[] _data = new byte[1024];
while(true)
{
try
{
//Datagram for receiving
DatagramPacket packet = new DatagramPacket(_data, _data.length);
//Receive the packet, convert it and send it to the Activity class
_socket.receive(packet);
String _medd = new String(_data, 0 , packet.getLength());
_csta.input = _medd;
}
catch(IOException e)
{
//Will break when the socket is closed
break;
}
}
}
Thing is that I receive my own messages sent from the android application, but I do not receive messages sent from the PC. So I'm guessing it has something to do with the connection.
I have set android.permission.CHANGE_WIFI_MULTICAST_STATE and android.permission.INTERNET
in the AndroidManifest.
I hope the information I have given is enough.
I'm using the emulator and android 2.2.
Off topic:
I don't really understand how MulticastSockets work, how can I just pick a class D IP and port?

It seems that the emulator does not have support for multicast:
https://stackoverflow.com/a/3179939/1686442

Related

When we receive packets from an UDP server, why do we have to receive them in a seperate thread?

So my application is a very simple. If you type something through the scanner it sends it over to the server, the server sends it back to client. However, i don't understand why we have to put our code where we handle our receiving packets from the server into a thread?
The code below works fine but if i don't use use multithreading then the application doesn't work. The part where i send packets also stop working. Could you explain why this happens?
public class Client {
private static DatagramSocket socket = null;
public static void main(String[] args) {
System.out.println("Send to server:");
Scanner scanner = new Scanner(System.in);
while (true) {
try {
// port shoudn't be the same as in TCP but the port in the datagram packet must
// be the same!
socket = new DatagramSocket();
} catch (SocketException e1) {
System.out.println("[CLIENT] Unable to initiate DatagramSocket");
}
InetAddress ip = null;
try {
ip = InetAddress.getByName("127.0.0.1");
} catch (UnknownHostException e) {
System.out.println("[CLIENT] Unable to determine server IP");
}
// must be in a while loop so we can continuously send messages to server
String message = null;
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
receive();
}
});
thread.start();
while (scanner.hasNextLine()) {
message = scanner.nextLine();
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, ip, 6066);
try {
socket.send(packet);
} catch (IOException e) {
System.out.println("[CLIENT] Unable to send packet to server");
}
}
}
}
private static void receive() {
// receiving from server
byte[] buffer2 = new byte[100];
DatagramPacket ps = new DatagramPacket(buffer2, buffer2.length);
while (true) {
try {
socket.receive(ps);
} catch (IOException e) {
System.out.println("[CLIENT] Unable to receive packets from server.");
}
System.out.println("[SERVER] " + new String(ps.getData()));
}
}
}
If you type something through the scanner it sends it over to the
server, the server sends it back to client.
So the main method runs on the main thread and does some job. The job that you just referenced.
Read some user input plus the following part
while (scanner.hasNextLine()) {
message = scanner.nextLine();
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, ip, 6066);
try {
socket.send(packet);
} catch (IOException e) {
System.out.println("[CLIENT] Unable to send packet to server");
}
}
Title: receive packets from an UDP server
You want to receive packets but you don't want to block the user from typing something as input and sending it to the server.
Therefore you need to do 2 jobs simultaneously. AKA multithreading

DatagramSocket Packets IPAddress being changed by router?

I have created a server that will be run on my desktop to send and receive messages from clients. The clients app will be run on android. As of now, both programs start up, and the client is able to send the initial connection to the server. My desktop is connected to the internet via a router, which i have port-forwarded from the router to receive the info.
The server is able to receive all messages sent by the client. However, the server is unable to respond. I did a quick check on the received packet (server side), and it says the ipaddress for the packet is the IP for my router. Does that mean i will be unable to send a response, since all of my packets will get their IP changed to the local address of the router (because of the port-forwarding)?
Ill post some code below, although im not sure it will help. Thanks very much for your comments.
Server:
public class QuoteServerThread extends Thread {
protected DatagramSocket socket = null;
DatagramPacket dgp = null;
protected boolean running = true;
ArrayList<ConnectedUser> users = new ArrayList<>();
String state;
int port;
int userId;
public QuoteServerThread(String name, String state) throws IOException {
super(name);
port = 4445;
userId = 1;
socket = new DatagramSocket(port);
this.state = state;
this.state = "listening";
System.out.println("Server Socket is now listening on port: " + port);
}
public void run() {
while (running) {
try {
byte[] buf = new byte[256];
// receive request
final DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
if(packet.getData() == null){
System.out.println(packet.getData());
return;
}
else{
System.out.println(packet.getData());
}
dgp = packet;
String message = new String(packet.getData(), 0, packet.getLength());
if(state.equals("listening")) {
if(message.contains("newUser")){
ConnectedUser newUser = new ConnectedUser(userId, socket);
users.add(newUser);
System.out.println("connected users: " + users.size());
String msg = "ID/" + userId;
InetAddress IPAddress = packet.getAddress();
int _port = packet.getPort();
System.out.println(IPAddress + "," + _port);
DatagramPacket out = new DatagramPacket(msg.getBytes(), msg.length(), IPAddress, _port);
socket.send(out);
userId++;
}
else if(message.contains("ID/")){
String receivedMessage[] = message.split("/");
System.out.println(receivedMessage[0] + ", " + receivedMessage[1] + ", " + receivedMessage[2]);
}
else{
System.out.println(message);
}
}
} catch (IOException e) {
e.printStackTrace();
running = false;
}
}
socket.close();
}
}
Client:
public class QuoteServerThread extends Thread {
protected DatagramSocket socket = null;
protected boolean running = true;
TextView chatWindow;
TextView notification;
EditText chatBox;
MainActivity ma;
ScrollView sv;
public QuoteServerThread(MainActivity ma, TextView chatWindow, EditText chatBox, ScrollView sv, TextView notification) throws IOException {
this("QuoteServerThread");
this.ma = ma;
this.chatWindow = chatWindow;
this.sv = sv;
this.chatBox = chatBox;
this.notification = notification;
}
public QuoteServerThread(String name) throws IOException {
super(name);
socket = new DatagramSocket();
}
public void run() {
while (running) {
try {
byte[] buf = new byte[256];
// receive request
final DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
if(packet.getData() == null){
System.out.println("data was null!");
return;
}
String message = new String(packet.getData(), 0, packet.getLength());
final String notif = message;
notification.post(new Runnable() {
public void run() {
notification.setText("server " + "__msg: " + notif);
}
});
if(ma.state.equals("connected")) {
final String chatText = chatWindow.getText().toString() + "\n" + "Sender: " + message;
chatWindow.post(new Runnable() {
public void run() {
chatWindow.setText(chatText);
sv.post(new Runnable() {
public void run() {
sv.fullScroll(View.FOCUS_DOWN);
chatBox.post(new Runnable() {
public void run() {
chatBox.requestFocus();
}
});
}
});
}
});
}
else if(ma.state.equals("waitingconnection")){
String msgSplit[];
if(message.contains("ID/")){
msgSplit = message.split("/");
final String id = msgSplit[1];
ma.clientID = Integer.getInteger(id);
notification.post(new Runnable() {
public void run() {
notification.setText("connected to server " + "__msg: " + id);
ma.state = "connected";
}
});
}
}
} catch (IOException e) {
e.printStackTrace();
running = false;
}
}
socket.close();
}
}
the only issue here is the client not receiving a response. to state the problem again, I believe that the port forwarding on my router is changing the packets address to its local ipaddress (my phone is sending from mobile network). Any ideas on how I can fix this?
I think you are seeing effects of the network address translation (NAT) and port-forwarding. From the viewpoint of the android client it is communicating with your router. Then your router instead of processing messages itself offloads the processing to your server (port forwarding).
On your server you see the destination address. Since your android client is talking to your router, the destination IP address you see is that of your router.
If you want to reply, what you should to is take the source address of the datagram and send the reply back to that address. When you datagram will be passing the router with NAT it will change the source address to that of the router.
The issue above was due to code that I had running in the background. When sending a message, I was using a class called MsgSender. MsgSender, however, was creating a new socket and sending the data. After the data was sent, it closed the socket, no longer listening for a response from the server.
To fix this, I shared the socket that is left open, and send all data through the single socket, so that it can listen for any information that is returned.
Be sure that when you send information, if you need to listen for a response, you MUST listen through the socket it was sent from. If you are not needing to listen for a response from the data you will be sending, you can close the socket.

Why does my UDP connection stop working?

After creating an UDP connection and connecting from the client to the server, it stops working randomly. Why is this happening?
This doesn't usually happen when running the client and the server on the same computer using "localhost" as the IP, but when using different computers on the same network it happens.
When I try and connect using different computers at first it works but after some time it just stops; the connection is "terminated".
Also, ignore the game.player stuff, it is just a player of mine.
This is my code:
Client:
public class GameClient extends Thread {
private InetAddress ipAddress;
private DatagramSocket socket;
private Main game;
public GameClient(Main main, String ipAddress) {
this.game = main;
try {
this.socket = new DatagramSocket();
this.ipAddress = InetAddress.getByName(ipAddress);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
message = message.trim();
if (message.startsWith("00")) {
System.out.println("Player connected. Got server response...");
String msg = "01" + game.player.getPos();
sendData(msg.getBytes());
}
if (message.startsWith("01")) {
message = message.substring(2);
List<String> coords = Arrays.asList(message.split(","));
game.updateMP(coords);
String msg = "01" + game.player.getPos();
sendData(msg.getBytes());
}
}
}
public void sendData(byte[] data) {
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress,
1331);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
My server class:
public class GameServer extends Thread {
private DatagramSocket socket;
private Main game;
public GameServer(Main main) {
this.game = main;
try {
this.socket = new DatagramSocket(1331);
} catch (SocketException e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
message = message.trim();
if (message.startsWith("00")) {
message = message.substring(2);
game.playerConnected = true;
sendData("00".getBytes(), packet.getAddress(), packet.getPort());
}
if (message.startsWith("01")) {
message = message.substring(2);
List<String> coords = Arrays.asList(message.split(","));
game.updateMP(coords);
String msg = "01" + game.player.getPos();
sendData(msg.getBytes(), packet.getAddress(), packet.getPort());
}
}
}
public void sendData(byte[] data, InetAddress ipAddress, int port) {
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress,
port);
System.out.println(ipAddress + ", " + port);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
And my "Main" class:
(just a part of it):
if (JOptionPane.showConfirmDialog(null, "Do you want to run the server?") == 0) {
socketServer = new GameServer(this);
socketServer.start();
socketType = 0;
} else {
socketClient = new GameClient(this, JOptionPane.showInputDialog(null, "IP:"));
socketClient.start();
socketClient.sendData("00".getBytes());
socketType = 1;
}
Looks like you have a packet flow without any delays. My suspicion is, that this mass of packets just overflows the network, thus breaking the "connection" (UDP is connectionless).
Let's break the packet flow down:
Server thread is created (constructor), binding to port 1331 (UDP).
Server thread starts, first step: listen on socket (blocking).
Client thread is created, binds to a random (free) port.
Client thread is started, first step: listen on socket (blocking).
Main thread calls sendData("00") on client, which works fine of course.
Server thread receives the "00" packet, sets playerConnected to true and sends "00" back (to inform the client of successful connection to the game). Then server listens again on the socket.
Client thread receives the "00" packet, and sends a "01..." packet. Client listens again on socket.
Server thread receives the "01" packet, updates its game object and sends a "01" packet back to the client (with no delay).
Client thread receives the "01" packet, updates its game object and sends a "01" packet back (with no delay)
The last two steps repeat indefinitely, with no delay.
On the same computer it works better, because the loopback interface is virtual and can handle much more packets per second, so the overflow happens later than on a real network.
Updating a position of a player in a network game is an issue as old as network games. There are many articles on that topic. Search e.g. for "client side position prediction" or sth. like "online game compensate latency", read the Quake2 source code (https://github.com/id-Software/Quake-2/blob/master/client/cl_pred.c) etc.
Beside of that: avoid using Strings, avoid creating tons of new byte arrays, for performance. In Java you will be punished by the Garbage Collector.

Java networking - java.net.BindException

I'm creating a LWJGL Strategy Game, and I'm implementing multiplayer into it.
Right now the game is just generating a world with some different tile types.
I thought I should start implementing networking now, to make the server generate the world,and all clients joining download that world and load it (even though the game is barely a playable yet) to make it easier to implement more advanced stuff later on. Now to the problem!
I'm watching these tutorials on networking implementation, made by DesignsbyZephyr, but I'm getting this error:
java.net.BindException: Address already in use: Cannot bind
at java.net.DualStackPlainDatagramSocketImpl.socketBind(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.bind0(Unknown Source)
at java.net.AbstractPlainDatagramSocketImpl.bind(Unknown Source)
at java.net.DatagramSocket.bind(Unknown Source)
at java.net.DatagramSocket.<init>(Unknown Source)
at java.net.DatagramSocket.<init>(Unknown Source)
at java.net.DatagramSocket.<init>(Unknown Source)
at com.tdd12.eotu.net.GameServer.<init>(GameServer.java:22)
at com.tdd12.eotu.Game.<init>(Game.java:39)
at com.tdd12.eotu.Game.main(Game.java:121)
Exception in thread "Thread-3" java.lang.NullPointerException
at com.tdd12.eotu.net.GameServer.run(GameServer.java:37)
When I start the game two times with the same port. That sounds pretty weird, doesn't it?
I don't know why, maybe because I'm not very experienced with network programming (as you've maybe already understood).
Here is the code I'm using:
(The code is placed in classes and packages, so they are correctly formatted. I just didn't write that here)
GameServer.java:
// The socket
private DatagramSocket socket;
// The main game
private Game game;
// The constructor
public GameServer(Game game) {
// Assign variables
this.game = game;
try {
this.socket = new DatagramSocket(9527);
} catch (SocketException e) {
e.printStackTrace();
}
}
// Run the thread
public void run() {
while(true) {
// The data to include in the packet (data to send)
byte[] data = new byte[1024];
// The packet to send
DatagramPacket packet = new DatagramPacket(data, data.length);
// Recieve data from the server
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
// Get the message
String message = new String(packet.getData());
// Print the message
System.out.println("CLIENT [" + packet.getAddress().getHostAddress() + ":" + packet.getPort() + "] > " + new String(packet.getData()));
// If the message is equal to "ping"
if(message.trim().equalsIgnoreCase("ping")) {
// Send back a message with the text "pong"
sendData("pong".getBytes(), packet.getAddress(), packet.getPort());
}
}
}
// Send data to the server
public void sendData(byte[] data, InetAddress ipAddress, int port) {
// Create a new packet with the inputed data
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress, port);
// Send the packet to the server
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
GameClient.java:
// The IP address
private InetAddress ipAddress;
// The socket
private DatagramSocket socket;
// The main game
private Game game;
// The constructor
public GameClient(Game game, String ipAddress) {
// Assign variables
this.game = game;
try {
this.socket = new DatagramSocket();
this.ipAddress = InetAddress.getByName(ipAddress);
} catch (SocketException | UnknownHostException e) {
e.printStackTrace();
}
}
// Run the thread
public void run() {
while(true) {
// The data to include in the packet (data to send)
byte[] data = new byte[1024];
// The packet to send
DatagramPacket packet = new DatagramPacket(data, data.length);
// Recieve data from the server
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
// Print the data
System.out.println("SERVER > " + new String(packet.getData()));
}
}
// Send data to the server
public void sendData(byte[] data) {
// Create a new packet with the inputed data
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress, 9527);
// Send the packet to the server
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
I'd greatly appreciate if someone could help me with this.
Thanks!
As Diptopol Dam said, calling the
DatagramSocket.close();
method before the application is closed fixed the problem. Thanks Diptopol Dam!

Java UDP respond to broadcast request

At work we are developing an android app that communicates with set top boxes(STB).
It all works fine but I'm trying to create a "mock" STB that the app can connect to so I can control the responses for testing.
I have no access to the code in the STB to know how they set up the sockets but I do have a simplified version of the client code used by the app.
Here's the client code:
public class UDPClient {
public static void main(String[] args) throws SocketException, UnknownHostException {
DatagramSocket c = new DatagramSocket(12345);
c.setBroadcast(true);
c.setSoTimeout(20000);
String msearchData = "DATA";
byte[] sendData = mSearchData.getBytes();
try {
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.250"), 1900);
c.send(sendPacket);
System.out.println("Request packet sent");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// wait for reply
byte[] recBuf = new byte[15000];
DatagramPacket receivePacket = new DatagramPacket(recBuf, recBuf.length);
try {
c.receive(receivePacket);
System.out.println("PACKET RECEIVED!");
System.out.println(new String(receivePacket.getData()));
} catch (IOException e) {
e.printStackTrace();
}
c.close();
}
}
When I run this code on my development laptop (and I'm on a wireless network with that STB) the STB responds.
However, I have another laptop setup to pretend to be another STB on the same network(mock STB).
The "mock" STB simply refuses to pick up the broadcasts requests and I'm stuck.
Here's some code I use to act as the mock STB. I've tried various combinations of ports but nothing works.
public class MockBox {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(true);
while (true) {
System.out.println(">>>Ready to receive broadcast packets!");
byte[] recvBuf = new byte[15000];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet); // blocks
// Packet received
System.out.println(">>>Packet received from " + packet.getAddress().getHostAddress());
System.out.println(">>>Packet data: " + new String(packet.getData()));
socket.close();
}
}
}
Any help appreciated!
Disable the software firewall.
Use MulticastSocket instead of DatagramSocket. (Note: The address you specified above is a multicast address.)
Use TTL of at least 1. If that doesn't work, use 2. Repeat, but don't go above say 4 or 5 on a LAN, because at that point, you're already past reasonable. (Zero won't leave the machine. Using a value too high may cause the packet to be discarded somewhere along the route.)

Categories

Resources