Reputation: 2206
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
Upvotes: 1
Views: 449
Reputation: 310957
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.
Upvotes: 2