UDP Broadcast in Java - java

Morning.
I'm pretty new in Java and socket connections but I'm trying to send out a UDP packet/broadcast on 255.255.255.255 on port 8001 to a device. I can get the data to send just fine, however when it comes time to receive the data the connection times out. I have a packet sniffer and I can see the packet send and then the device respond.
I'm pretty sure it is a rookie mistake that I'm missing in my code but I've been stuck on it for awhile and any help would be appreciated.
m_Socket = new DatagramSocket(m_SERVERPORT);
InetAddress address = InetAddress.getByName(m_SERVERIP);
m_DataPack = new DatagramPacket(m_SERVERCMD.getBytes(), m_SERVERCMD.getBytes().length,
address, m_SERVERPORT);
m_Socket.setBroadcast(true);
m_Socket.connect(address, m_SERVERPORT);
m_Socket.send(m_DataPack);
m_DataPack = new DatagramPacket(data, data.length,
address, m_SERVERPORT);
m_Socket.receive(m_DataPack); // This is where it times out
data = m_DataPack.getData();
String received = data.toString();
System.out.println("Received: " + received);
m_Socket.close();
Thanks and Gig'Em.
EDIT:
I'm not sure if this helps but when I watch the m_Socket object I can see the following right before it sends:
bound = true;
close = false;
connectedAddress = Inet4Address (id = 32) (-1,-1,-1,-1);
connectedPort = 8001;
connectState = 1;
created = true;
impl = PlainDatagramSocketImpl;
oldImpl = false;
and the m_DataPack object is the following:
address = Inet4Address (id = 32) (-1,-1,-1,-1);
bufLength = 6 (size of packet I'm sending is 6 char long);
offset = 0;
port = 8001;

This doesn't make sense. You are broadcasting, which is 1-to-many, and you are also connecting, which is 1-to-1. Which is it?
Lose the connect. And lose the 255.255.255.255. This has been heavily deprecated for about 20 years. Use a subnet-local broadcast address, e.g. 192.168.1.255.

You can also take a look at MulticastSocket described at Broadcasting to Multiple Recipients. Hope this helps.

If you want to receive a datagram you need to bind() to the local endpoint (address + port).

You are sending and receiving the packet on same device. You could separate send and receive ports (e.g send on 8001, receive on 8002). And run send and receive codes as separate threads. If both device must find each other (or one device find itself).
import java.io.IOException;
import java.net.*;
Receiving:
private DatagramSocket getReceiveSocket() throws UnknownHostException, SocketException {
if (receiveSocket == null) {
receiveSocket = new DatagramSocket(8002, InetAddress.getByName("0.0.0.0")); // 0.0.0.0 for listen to all ips
receiveSocket.setBroadcast(true);
}
return receiveSocket;
}
public void receive() throws IOException {
// Discovery request command
byte[] buffer = "__DISCOVERY_REQUEST__".getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
getReceiveSocket().receive(packet);
System.out.println("Discovery package received! -> " + packet.getAddress() + ":" + packet.getPort());
// Get received data
String data = new String(packet.getData()).trim();
if (data.equals("__DISCOVERY_REQUEST__")) { // validate command
// Send response
byte[] response = new byte["__DISCOVERY_RESPONSE".length()];
DatagramPacket responsePacket = new DatagramPacket(response, response.length, packet.getAddress(), packet.getPort());
getReceiveSocket().send(responsePacket);
System.out.println("Response sent to: " + packet.getAddress() + ":" + packet.getPort());
} else {
System.err.println("Error, not valid package!" + packet.getAddress() + ":" + packet.getPort());
}
}
Sending:
private DatagramSocket getSendSocket() throws UnknownHostException, SocketException {
if (sendSocket == null) {
sendSocket = new DatagramSocket(8001, InetAddress.getLocalHost());
sendSocket.setBroadcast(true);
}
return sendSocket;
}
public void send() throws IOException {
// Discovery request command
byte[] data = "__DISCOVERY_REQUEST__".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("255.255.255.255"), 8002);
getSendSocket().send(packet);
System.out.println("Discovery package sent!" + packet.getAddress() + ":" + packet.getPort());
// Discovery response command
byte[] response = new byte["__DISCOVERY_RESPONSE__".length()];
DatagramPacket responsePacket = new DatagramPacket(response, response.length);
getSendSocket().receive(responsePacket);
System.out.println("Discovery response received!" + responsePacket.getAddress() + ":" + responsePacket.getPort());
String responseData = new String(responsePacket.getData()).trim();
if (responseData.equals("__DISCOVERY_RESPONSE__")) { // Check valid command
System.out.println("Found buddy!" + responsePacket.getAddress() + ":" + responsePacket.getPort());
}
}
Of course should put this code in a loop in a thread.
Based on this example: https://demey.io/network-discovery-using-udp-broadcast/
UPDATE
The link above is dead. But same method described here also: https://www.baeldung.com/java-broadcast-multicast

Related

Java UDP packet loss. 50%

I am making an online java game. Right now I am using UDP packets. I have about a 50% packet drop rate right now at updating 60 15 times a second. This both happen when it's being hosted on my local machine and hosted on my server. I have tried sending fewer packets and sending them slower neither have helped. Thanks in advance.
Sending packets:
while(true) {
try {
socket = new DatagramSocket();
if(id >= 0) {
data = (PacketID.POS.toString() + tempobj.x + "/" + tempobj.y + "/" + tempobj.getHelth() + "/" + id + "/").getBytes();
}else {
data = (PacketID.POS.toString() + tempobj.x + "/" + tempobj.y + "/" + tempobj.getHelth() + "/" + tempobj.isSheild + "/" + tempobj.name + "/" + tempobj.size + "/" + (int)(tempobj.getVelx()) + "/" + (int)(tempobj.getVely())).getBytes();
}
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
if(tempobj.x != lx || tempobj.y != ly || tempobj.helth != lh) {
packetssent++;
System.out.println(packetssent);
lx = tempobj.x;
ly = tempobj.y;
lh = tempobj.helth;
socket.send(packet);
}
Thread.sleep(66);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
receiving packets:
byte[] arr = new byte[40];
DatagramPacket data = new DatagramPacket(arr, arr.length);
while(true) {
socket.receive(data);
String s = new String(data.getData(),0, data.getLength());
if(s.substring(0, 3).equalsIgnoreCase(PacketID.POS.name())) {
String[] split = s.substring(3).split("/");
System.out.println(reciver);
for(int i = 0; i < players.size(); i++) {
if((players.get(i).id - 1) == Integer.parseInt(split[3].trim())) {
players.get(i).x = Integer.parseInt(split[0].trim());
players.get(i).y = Integer.parseInt(split[1].trim());
players.get(i).helth = Integer.parseInt(split[2].trim());
}
}
}
s = null;
}
There is probably nothing you can do about UDP packet loss that you haven't already tried. Except ... maybe ... try and get a better end-to-end network connection.
Packet loss typically happens as a result of network congestion on the most congested links. Unless you own and manage those links, there is nothing you can do about it.
Maybe you should be using TCP instead. Or changing your application so that it can cope with 50% packet loss ...
There is one problem I noticed with your code. Your sending code is creating a brand new DatagramSocket to send each UDP message, and then not closing the socket. That's inefficient, and could lead to resource leak problems. (It shouldn't cause packet loss though.)
If this is a linux host you may try & check if you're not exceeding any buffers. I've been bitten by similar problem in the past. Check the output of netstat -s and checkout the udp section for any drops.

UDP Packet content changes while transmitting

I'm programming an Server-Client Software, where the client connects to the server with an port request, the server opens a new port and sends back the new port number. Then the client communicates with an RSA shared AES key encryption and the port from the port request.
Well, it should be like that.
I had the program already running, only one client could connect to the server, and everything worked fine. But now I'm sending an "portreq" String to the server which should give a port reply. But when i check, if the incoming request is a "portreq" it gives me false. If i do same with .contains it gives me true. That's the first problem, and secondly:
When the server converts the port-Integer into an String and sends it, i can't transform it into a int again on the client-side:
java.lang.NumberFormatException: For input string: "6002������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at application.PowerPanelController.addDataToCpu(PowerPanelController.java:360)
at application.PowerPanelController.lambda$0(PowerPanelController.java:143)
at application.PowerPanelController$$Lambda$200/357277047.handle(Unknown Source)
at com.sun.scenario.animation.shared.TimelineClipCore.visitKeyFrame(TimelineClipCore.java:226)
at com.sun.scenario.animation.shared.TimelineClipCore.playTo(TimelineClipCore.java:167)
at javafx.animation.Timeline.impl_playTo(Timeline.java:176)
at javafx.animation.AnimationAccessorImpl.playTo(AnimationAccessorImpl.java:39)
at com.sun.scenario.animation.shared.InfiniteClipEnvelope.timePulse(InfiniteClipEnvelope.java:110)
at javafx.animation.Animation.impl_timePulse(Animation.java:1102)
at javafx.animation.Animation$1.lambda$timePulse$25(Animation.java:186)
at javafx.animation.Animation$1$$Lambda$186/1977464318.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javafx.animation.Animation$1.timePulse(Animation.java:185)
at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:344)
at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:267)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:447)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:431)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$363(QuantumToolkit.java:298)
at com.sun.javafx.tk.quantum.QuantumToolkit$$Lambda$42/317723766.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Which i don't get because the String looks pretty much the same as i send it.
Here's my client side send and receive function
private String sendCommandNoAES(String command)
{
byte[] outData = new byte[1024];
byte[] inD = new byte[1024];
String message = "";
try
{
// create Socket
DatagramSocket socket = new DatagramSocket();
// build Packet
InetAddress serverIP = InetAddress.getByName(PowerPanelMain.ip);
outData = command.getBytes();
DatagramPacket out = new DatagramPacket(outData,outData.length, serverIP, PowerPanelMain.port); // send packet
socket.send(out);
logger.info("Sent an " + command + " Request non encrypted to " + PowerPanelMain.ip + ":" + PowerPanelMain.port);
// Receive answer
byte[] inData = new byte[1024];
DatagramPacket in = new DatagramPacket(inData,inData.length);
socket.receive(in);
inD = in.getData();
message = new String(inD,0,inD.length);
// Close Socket
socket.close();
logger.info("Go answer " + message + " from " + in.getAddress().toString() + ":" + in.getPort());
}
catch(Exception ee)
{
logger.error("Error while requesting data from server \n" + getStackTrace(ee));
return "offline";
}
return message;
}
This is where i send the portreq:
String portString = sendCommandNoAES("portreq");
int port = Integer.parseInt(portString);
Thats the server code:
byte[] inData = new byte[1024];
byte[] outData = new byte[1024];
String message;
DatagramSocket socket = null;
try
{
socket = new DatagramSocket(port);
System.out.println("Bound to " + port);
//JOptionPane.showMessageDialog(null, "Bound to " + String.valueOf(port), "Yeay", JOptionPane.OK_CANCEL_OPTION);
}
catch(Exception ee)
{
JOptionPane.showMessageDialog(null, "Error occured! \n#002", "Error #002", JOptionPane.OK_CANCEL_OPTION);
}
...
DatagramPacket in = new DatagramPacket(inData,inData.length);
socket.receive(in);
InetAddress senderIP = in.getAddress();
int senderPort = in.getPort();
byte[] inc = in.getData();
message = new String(inc,0,inc.length);
System.out.println("Got " + message + " from " + senderIP + ":" + senderPort + " (byte array: " + inc.toString());
...
if(message.contains("portreq"))
{
System.out.println("Creatinfg answer");
outData = String.valueOf(portcount).getBytes();
DatagramPacket out = new DatagramPacket(outData,outData.length, senderIP, senderPort);
socket.send(out);
System.out.println("Sent " + String.valueOf(portcount));
UDPTraffic udpt = new UDPTraffic(portcount, this.mode);
udpt.start();
portcount++;
}
I'm really thankful to anyone who can solve/explain me why this is happening :D
inD = in.getData();
This merely reasserts the same value. Remove.
message = new String(inD,0,inD.length);
Wrong. You're ignoring the actual length of the received datagram. Change to:
message = new String(in.getData(), in.getOffset(), in.getLength());
You are sending a string of one size, yet on the other side you are reading the string of 1024 bytes - which you never sent. This will not do. You should either serialize string using serialize() method, or, if you want to send raw string bytes, send string length as a separate part of the message.

PingIDs (Data) is different on different sides

I am working on a server software for Minecraft: Pocket Edition. Right now I am working on the MOTD for the server, and it works Just fine. When sending a response to a ping packet (0x01) with a 0x1c. It shows up in the world list with the name just fine. But, for some reason, If I send the same data from another program, the Ping ID and ServerID will show different in the consoles. Why is this?
Ping response code:
public PingResponse(DatagramPacket Packet, long ServerID) throws IOException {
// Data from Ping
ByteBuffer ReceivedPing = ByteBuffer.wrap(Packet.getData());
// Set variables
this.ServerID = ServerID;
this.ServerName = ServerPropertiesHandler.getMOTD();
this.PingID = ReceivedPing.getLong();
// Server Name
String Identifier = "MCCPP;MINECON;" + ServerPropertiesHandler.getMOTD();
ByteBuffer PingResponseBuffer = ByteBuffer.allocate(35 + (short) Identifier.length());
// Put Packet ID
PingResponseBuffer.put(PacketIDList.ID_UNCONNECTED_PING_OPEN_CONNECTIONS);
// Ping ID
PingResponseBuffer.putLong(this.PingID);
System.out.println("Ping ID: " + this.PingID);
// Server ID
PingResponseBuffer.putLong(this.ServerID);
System.out.println("Server ID: " + this.ServerID);
// Sugar Spice and everything nice
PingResponseBuffer.put(PacketIDList.MAGIC);
// Server Name
PingResponseBuffer.putShort((short) Identifier.length());
PingResponseBuffer.put(Identifier.getBytes());
// Send
PacketHandler.Socket.send(new DatagramPacket(PingResponseBuffer.array(), PingResponseBuffer.array().length), Packet.getAddress(), Packet.getPort());
}
Client Example:
public static void main(String[] args) {
try {
// SEND
final long PacketID = new Random().nextLong();
DatagramSocket ClientSocket = new DatagramSocket();
ByteBuffer PingBuffer = ByteBuffer.allocate(25);
PingBuffer.put(PacketIDList.ID_CONNECTED_PING_OPEN_CONNECTIONS);
PingBuffer.putLong(PacketID);
PingBuffer.put(PacketIDList.MAGIC);
ClientSocket.send(new DatagramPacket(PingBuffer.array(), PingBuffer.array().length, InetAddress.getByName("localhost"), 19132));
// RECEIVE
byte[] buffer = new byte[1535];
DatagramPacket PongPacket = new DatagramPacket(buffer, buffer.length);
ClientSocket.receive(PongPacket);
byte[] PongPacketData = PongPacket.getData();
ByteBuffer PongBuffer = ByteBuffer.wrap(PongPacketData);
if(PongPacketData[0] == (byte) 0x1c) {
System.out.println("PingID From Server: " + PongBuffer.getLong());
System.out.println("ServerID From Server: " + PongBuffer.getLong());
System.out.println("MAGIC From Server: " + PongBuffer.get());
System.out.println("MOTD From Server: " + PongBuffer.get());
}
else {
System.out.println("UNKNOWN PACKET");
}
ClientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
3 months and no reply. pingID and serverID are identifiers of time used on respective systems to determine the time since start. These provide a double functionality. Firstly, they allow a server to determine any latency in the communications. Secondly, they help provide a packet order to commands. Rebooting a server would generate a new serverID starting from 0, while rejoining a server would provide a new pingID starting at 0.
I would be quite interested to see the progress you've made, as I've begun working on my own.

Java UDP message exchange works over localhost but not Internet?

Client A
import java.io.*;
import java.net.*;
import java.util.*;
public class ClientA
{
final private static int PORT = 5005; // arbitrarily assigned port to use
public static void main(String args[]) throws
IOException
{
DatagramSocket socket = new DatagramSocket(PORT); // create new connection on that port
while (true)
{
byte buffer[] = new byte[256]; // data buffer
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // new packet containing buffer
socket.receive(packet); // look for packet
String clientBMsg = new String(packet.getData()); // get data from received packet
InetAddress address = packet.getAddress(); // get address of received packet
System.out.println("ClientB at " + address + " says " + clientBMsg);
buffer = null;
String msgString = "I'm ClientA, vegetables are fun";
buffer = msgString.getBytes(); // put String in buffer
int port = packet.getPort(); // get port of received packet
packet = new DatagramPacket(buffer, buffer.length, address, port); // create new packet with this data
socket.send(packet); // send packet back containing new buffer!
System.out.println("Message Sent");
socket.close();
}
}
}
Client B
import java.io.*;
import java.net.*;
public class ClientB
{
final private static int PORT = 5005; // arbitrarily assigned port - same as server
public static void main(String args[]) throws
IOException {
// if (args.length == 0) { // requires host
// System.err.println
// ("Please specify host");
// System.exit(-1);
// }
// String host = args[0]; // user defined host
DatagramSocket socket = new DatagramSocket(); // open new socket
String host = "localhost";//"86.0.164.207";
byte message[] = new byte[256]; // empty message
String msgString = "Hello, I'm client B and I like trees";
message = msgString.getBytes(); // put String in buffer
InetAddress address = InetAddress.getByName(host); // determines address
System.out.println("Sending to: " + address); // tells user it's doing something
DatagramPacket packet = new DatagramPacket(message, message.length, address, PORT); // create packet to send
socket.send(packet); // send packet
System.out.println("Message Sent");
message = new byte[256];
packet = new DatagramPacket(message, message.length);
socket.receive(packet); // wait for response
String clientAreply = new String(packet.getData());
System.out.println("ClientA at " + host + " says " + clientAreply);
socket.close();
}
}
I don't understand why this works over localhost but when I put my IP address in, it just sends the message and nothing is received.
Can anyone point me in the right direction here?
Thank you!
You should use DatagramSocket's bind method to bind it to your internet interface, otherwise it only listens on 127.0.0.1 or localhost. Like this:
DatagramSocket socket = new DatagramSocket(null);
socket.bind(new InetSocketAddress(InetAddress.getByName("your.ip.add.ress"),5005);
In case you are behind your router then your should listen on the local IP address given to you by the router and use port forwarding to this address in your router settings.
I can suggest using Socket Test tool with TCP and UDP Sockets:
http://sockettest.sourceforge.net/
I used it to trouble shoot issues with a 2-way socket program. You can end-to-end test your link with two SocketTest programs and the compare results, etc. Very useful.

How can I get the IP address from a multicast socket that is listening to broadcasts?

My server creates a multicast socket which listens for UDP packets. It is receiving packets sent to the broadcast address of the network but I can't get the ip address of the sender:
multisocket.getInetAddress().getHostAddress();
returns
"::"
(I guess its because of null getInetAddress).
How can I get the IP address of the sender?
TIPS: I guess it has to do with the socket not being bound and basically the whole broadcasting because those packets arent sent exclusively to me but to the whole network, but shouldnt they hold the IP address of the sender? Enlighten me please.
Here is the code:
public void run() {
try {
Thread.sleep(5000);
Log.i("SERVERUDP", "Connecting...");
MulticastSocket multisocket = new MulticastSocket(SERVERPORT);
multisocket.setBroadcast(true);
Log.i("SERVERUDP","Server's IP is: " + multisocket.getLocalAddress().getHostAddress());
getLocalIpAddress();
while(true){
byte[] b = new byte[65535];
ByteArrayInputStream b_in = new ByteArrayInputStream(b);
DatagramPacket dgram = new DatagramPacket(b, b.length);
multisocket.receive(dgram); // blocks
ObjectInputStream o_in = new ObjectInputStream(b_in);
Object o = o_in.readObject();
dgram.setLength(b.length);
b_in.reset();
if(o.getClass().getSimpleName().equalsIgnoreCase("Request")){
Request request = (Request)o;
String inetaddress = multisocket.getInetAddress().getHostAddress();
Log.i("SERVERUDP-if", "Sending request to IP: " + inetaddress);
new Thread(new ClientTCP(inetaddress, createRequestFromBroadcast(request))).start();
}else if(o.getClass().getSimpleName().equalsIgnoreCase("String")){
Log.e("SERVERUDP-elseif-string", "WTF received a string: " + (String)o);
}else{
Log.e("SERVERUDP-else", "Unrecognized object of type: " + o.getClass().getSimpleName());
}
o_in.close();
//iteration done only once for testing!
break;
}
multisocket.close();
} catch (Exception e) {
Log.e("SERVERUDP", "Error", e);
}
}
Each packet that you receive might have a different source address. So I'm not sure why you're trying to look at multisocket to figure out the source address.
I have to admit I haven't tried this, but does dgram.getSocketAddress () give you what you want after the multisocket.receive call returns?

Categories