UDP client gets only beginning of data (Java) - java

I made a client and server that use UDP protocol. I know that UDP is not very reliable and data can get lost but I run everything on my local host and the size of data that i send is only about 10 bytes.
The problem is that when server sends 4 bytes client receives only the first byte but when server sends
50 or 1000 bytes client receives... only first byte of data?! Yeah, exactly 1 byte (sometimes 2 or 3 but no more)! I have no idea whats going on. Is there an error in my code or this is fault of UDP?
Here is code of client
public void connect(String ip, int port) {
try {
adress = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
error("UnknownHostException");
e.printStackTrace();
}
try {
noErrors = true;
socket = new DatagramSocket();
socket.setSoTimeout(5000);
socket.connect(adress, port);
confirmation = new DatagramPacket(buf, buf.length, adress, port);
packet = new DatagramPacket(buf, buf.length, adress, port);
preapreConfirmation();
logIn();
} catch (SocketException e) {
error("Cannot connect");
e.printStackTrace();
}
}
private void logIn(){
String s;
while (noErrors) {// Sending request for connecting
sendRequest("2");
s = new String(packet.getData());
if (s.contains("2")) { // Connection was accepted
break;
} else if (s.contains("1")) {// Connection was refused
disconnect();
error("Connection refused");
break;
}
}
while(noErrors){ //loding map data: name
sendRequest("4"); // ask about world name
s = new String(packet.getData());
try {
System.out.println(s+" BYTES:"+s.getBytes().length+" "+socket.getReceiveBufferSize());
} catch (SocketException e) {
e.printStackTrace();
}
if(s.startsWith("4")){//world name was given
if( s.split("_").length>1){
mapname = s.split("_")[1];
break;
}
}
}
while (noErrors) { // loding map data: hash
sendRequest("6"); // ask about world hash
s = new String(packet.getData());
if (s.startsWith("6")) {
if (s.contains("h")) { // world hash was given
maphash = parse(s, 1);
break;
}
}
}
if(!noErrors)return;
System.out.println(maphash+" ==> "+ Screen.game.getHash(mapname)+" ("+mapname+")");
if (Screen.game.checkIfWorldExists(maphash, mapname)) { //validating world
Screen.game.loadMap(mapname);
Screen.setState(GameStates.GAME);
} else {
sendCommand("0");
}
isLogged = true;
}
public void sendRequest(){
try {
socket.send(packet);//Sending data
try{
socket.receive(packet);//receiving answer
} catch(PortUnreachableException ee){
error("Cannot connect");
} catch(SocketTimeoutException e){
error("End of stream");
}
socket.send(confirmation);//Sending confirmation of receiving answer
} catch (IOException e) {
error("IOException");
e.printStackTrace();
}
}
private void error(String message){
noErrors = false;
Screen.setErrorState(message);
}
public void sendCommand(){
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
and here is code of server:
public void run() {
System.out.println("Server is waiting for data from socket");
while (isRunning) {
try {
buf = new byte[256];
// receives request
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
System.out.println("packet received");
// creating response
setCommand(new String(packet.getData(), "UTF-8"));
String d = getNextQuote(getCommand());
// sends the response to the client. "address" and "port" are
// already saved in last received packet
InetAddress address = packet.getAddress();
int port = packet.getPort();
System.out.println(">"+getCommand()+"\n<"+d);
if(d.equals("7")){
for(String g:packages){
buf = g.getBytes();
packet = new DatagramPacket(buf, buf.length, address, port);
socket.send(packet);
}
} else {
buf = d.getBytes();
packet = new DatagramPacket(buf, buf.length, address, port);
socket.send(packet);
}
} catch (IOException e) {
e.printStackTrace();
isRunning = false;
}
}
System.err.println("Server stopped");
socket.close();
}
In my code i use commands that make the size of sent data smaller so client and server can communicate using only one byte like for e.g. client sends:
1 - disconnect me
2 - connect me
3 - send map length (chunks quantity)
4 - send map name to me
but then server must respond: 4_mapname and 1 byte is not enough

You need to reset the length of the DatagramPacket before receiving.
And I'm not crazy about this:
confirmation = new DatagramPacket(buf, buf.length, adress, port);
packet = new DatagramPacket(buf, buf.length, adress, port);
Two DatagramPackets sharing the same data buffer. You'll need to be very careful how you use them. It would probably better to only have one, then you know you have to be careful. In fact when sending confirmations or replies of any kind it's better to re-use the packet containing the request you're replying to. That way the target address and port are already set, and all you have to do is set up the data, offset, and length.

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

Java UDP packet only holds 5 bites

The following code is used to send over a simple string in the following formart "address,porttNum".
Here is the client side:
ByteArrayInputStream bin = new ByteArrayInputStream(packet.getData());
DataInputStream dis = new DataInputStream(bin);
try {
System.out.println("Data in packet: " + dis.readLine());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Here is the server side:
byte[] sbuf = data.getBytes();
// sbuf = output.getBytes();
packet = new DatagramPacket(sbuf,
sbuf.length, packet.getAddress(), packet.getPort());
try {
socket = new DatagramSocket();
socket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}
Say the server sends "abcdefghi", the client only recieves "abcde". I have tried it with multiple test cases and the client always recieves 5 bytes. Can anyone point out where I messed up?
edit:
For debugging purposes I even added the following:
try {
System.out.println("Data in packet: " + dis.readLine());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
which outputs the right data the client still does not get it.
edit 2:
I changed the client side to the following:
String data = new String(packet.getData(), StandardCharsets.UTF_8);
System.out.println("Data in packet: " + data);
It makes no difference.
Here is some example code showing construction of a Datagram packet from the bytes of a String, sending it, and reconstruction of the String.
Notice that the buffer for receiving a packet is created much larger than the message that is eventually received. This is necessary as the the UDP socket will truncate the message to the size of the buffer if it receives a message larger than the buffer. For example, if the client sent the message "ZYXWV" to the server, and only created the buffer large enough for that message, and then reused the buffer for the incoming message, only the first 5 characters of the incoming message would be received.
public class DataGram {
public static void main(String[] args) throws SocketException {
new Thread(new Client()).start();
new Thread(new Server()).start();
}
static class Client implements Runnable {
DatagramSocket socket;
Client() throws SocketException {
socket = new DatagramSocket(1234);
}
#Override
public void run() {
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
try {
socket.receive(packet);
String msg = new String(packet.getData());
System.out.println(msg);
} finally {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
static class Server implements Runnable {
#Override
public void run() {
SocketAddress address = new InetSocketAddress("localhost", 1234);
try (DatagramSocket socket = new DatagramSocket()) {
String msg = "abcdefghi";
byte[] buf = msg.getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, address);
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
No DataInputStream or ByteArrayInputStream is used here, since you can directly convert a String to byte[] and back again.

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!

MulticastSocket doesn't open port

I'm trying to create a simple multicast communication between my PC (Ubuntu, client) and my phone (Android, server).
Unicast/TCP connections work without any problem, the defined port (37659) opens both on PC and phone. When trying to use a MulticastSocket, no ports get opened. nmap tells me the specified port (36963) is a TCP port and that it is closed. (While the receive-method is being executed).
Am I doing something wrong? Or is the firewall blocking the multicast sockets? (I've tried about 20 different ports and none worked..., currently using port 36963)
EDIT: Also with the firewall completely down, nmap tells me the port is closed...
The server's code (phone):
private void multicastLoop() {
String res = Build.FINGERPRINT + "\n";
final InetAddress group;
final MulticastSocket socket;
final DatagramPacket response;
try {
group = InetAddress.getByName("224.0.0.115");
socket = new MulticastSocket(mport);
socket.setLoopbackMode(true);
socket.setSoTimeout(10000);
socket.joinGroup(group);
response = new DatagramPacket(res.getBytes(), res.length(), group, mport);
} catch (IOException e) {
e.printStackTrace();
return;
}
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while(isRunning) {
try {
byte[] data = new byte[1024];
DatagramPacket dm = new DatagramPacket(data, data.length);
socket.receive(dm);
Log.d("udp", "received");
if (Arrays.equals(dm.getData(), "someone there".getBytes())) {
socket.send(response);
}
} catch (SocketTimeoutException e) {
continue;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.leaveGroup(group);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
}
The client's code (computer):
public String[] findServers() {
String hello = "someone there";
try {
InetAddress group = InetAddress.getByName(mhost);
MulticastSocket socket = new MulticastSocket(mport);
socket.setLoopbackMode(true);
socket.setSoTimeout(60000);
socket.joinGroup(group);
DatagramPacket p = new DatagramPacket(hello.getBytes(), hello.length(), group, mport);
byte[] buffer = new byte[1024];
socket.send(p);
DatagramPacket r = new DatagramPacket(buffer, buffer.length);
socket.receive(r);
socket.leaveGroup(group);
socket.close();
String srinfo = "";
byte[] data = r.getData();
for (byte b: data)
srinfo += (char) b;
System.out.println("Server found at " + r.getAddress().getHostName() + ": " + srinfo);
} catch (SocketTimeoutException e) {
return new String[] {"timeout"};
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Make sure mhost is set to "224.0.0.115" not some machine name.
Make sure multicast is enabled on your router.
If the host is multi-homed, you need to join the multicast group via all local interfaces, not just the default one, which is what you're doing at present.
You could send the response back to the source address it came from, which is in the received datagram packet. That would also mean that the client doesn't need a MulticastSocket, only a DatagramSocket.

Categories

Resources