This question already has answers here:
How to detect that UDP packet has been lost? (C#)
(5 answers)
Closed 2 years ago.
For a reliable connection you have to use TCP.
However, I would like to know if there is a way to modify my code so that I can check for lost packets in UDP
try {
DatagramSocket socket = new DatagramSocket(5000);
while(true) {
byte[] buffer = new byte[50];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
System.out.println("Text received is: " + new String(buffer, 0, packet.getLength()));
String returnString = "echo: " + new String(buffer, 0, packet.getLength());
byte[] buffer2 = returnString.getBytes();
InetAddress address = packet.getAddress();
int port = packet.getPort();
packet = new DatagramPacket(buffer2, buffer2.length, address, port);
socket.send(packet);
}
} catch(SocketException e) {
System.out.println("SocketException: " + e.getMessage());
} catch(IOException e) {
System.out.println("IOException: " + e.getMessage());
}
}
Since the transport protocol does not provide facilities, the application protocol needs to do so.
You could for example make the sender add a sequence number into each message, and the receiver would then know that a datagram had been lost, or (also possible) duplicated.
That lets you detect loss, but does nothing to allow you to recover from it.
You'd need in the receiver to track expected sequence numbers per sender, of course.
Related
This question has been asked a lot, but so far, none of the solutions that I applied from previous answers have helped me.
Main goal
I am trying to learn UDP conexions and this is my attempt. I want to have a client ask for a picture at a server via UDP and the server will send it. Then the client will create a file with that information given.
Explanation
My main idea is to ask the server for an image using a "GET" command (not the HTTP, just GET) followed by the name of the image(extension included). Then the client awaits an answer which is the image requested.
Problems
The client waits and answer which does no come
Research
From another similar question it was a problem that I was using the same PORT for both receive and connect, so I added two ports, receivingPORT and sendingPORT, no results from the Client.
From other similar questions, It was a Firewall problem. So, on a Win10 machine, I created a new rule for UDP in the Firewall for the ports that I am using for this application, and nothing was received by the Client...
I have checked that the image is loaded into byte[] and the image is sent. But on the Client, nothing is received and stays there waiting for a connection to come through
CODE from Server
public class UDPserver {
static DatagramSocket serverUDP;
static DatagramPacket packet;
static InetAddress address;
static byte[] buffer = new byte[65507];//65507
final static int receivingPORT = 6668;
final static int sendingPORT = 6669;
public static void main(String[] args) throws SocketException, IOException, InterruptedException{
boolean serverActive = true;
String order = "";
String file = "";
//Instantiate server
serverUDP = new DatagramSocket(receivingPORT);
while(serverActive){
//Kind of packet we want to receive
packet = new DatagramPacket(buffer, buffer.length);
System.out.println("Server awaiting connection...");
//Receive it
serverUDP.receive(packet);
System.out.println("Received packet from: " + packet.getAddress() + "/" + packet.getPort());
//What does the packet contain?
String msg = new String(packet.getData());
address = packet.getAddress();
System.out.println("Order from: " + address + "/" + receivingPORT + " says: " + msg);
try{
order = msg.split(" ")[0].trim();
file = msg.split(" ")[1].trim();
} catch (Exception e){
}
switch(order){
case("GET"):{
System.out.println("Sending back an image...");
buffer = loadImageFromServer(file);
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
Thread.sleep(5000);
serverUDP.send(packet);
System.out.println("Client served");
break;
}
case("DISCONNECT"):{
buffer = "Server is disconnecting...".getBytes();
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
serverUDP.send(packet);
serverActive = false;
serverUDP.close();
break;
}
}
}
}
static byte[] loadImageFromServer(String path) {
try {
System.out.println("Loading path: " + path);
//Instantiate a buffer from the image for it
BufferedImage img = ImageIO.read(UDPserver.class.getResource(path));
//Create a byte[] stream object to handle the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//Write the image data into those above with jpg format
ImageIO.write(img, "png", baos);
//Flush the information
baos.flush();
byte[] buffer = baos.toByteArray(); //Write it out on a byte string and return it
return buffer;
} catch (IOException ex) {
Logger.getLogger(UDPserver.class.getName()).log(Level.SEVERE, null, ex.fillInStackTrace());
System.exit(-1);
}
return null;
}
}
CODE client
public class Client {
static DatagramSocket clientUDP;
static InetAddress address;
static DatagramPacket packetSend;
static DatagramPacket packetReceive;
static int SIZE = 65507;
final static int receivingPORT = 6669;
final static int sendingPORT = 6668;
static byte[] buffer = new byte[SIZE];
static Scanner scan = new Scanner(System.in);
public static void main(String[] args) throws SocketException, UnknownHostException, IOException{
boolean clientLoop = true;
//Get address
address = InetAddress.getByName("localhost");
//Instantiate Client -> UDP
clientUDP = new DatagramSocket();
while(clientLoop){
System.out.print("Enter any key and press enter");
scan.next(); //Just to stop the loop
//Load the buffer
buffer = "GET imagenServidor.png".getBytes();
//buffer = "DISCONNECT".getBytes();
System.out.println("Buffer is ready");
//Arm the packet
packetSend = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
System.out.println("Packet is armed!");
//Send the packet to the server
clientUDP.send(packetSend);
System.out.println("Order sent to server");
System.out.println("Waiting an answer");
packetReceive = new DatagramPacket(buffer, buffer.length, address, receivingPORT);
clientUDP.receive(packetReceive);
System.out.println("Server answered!");
ByteArrayInputStream bais = new ByteArrayInputStream(packetReceive.getData());
BufferedImage image = ImageIO.read(bais);
System.out.println(image);
}
clientUDP.close();
}
}
NOTES
This is a UDP exercise
The Reason
MTU!
You are sending packets with long buffe through UDP directly, which may not work in most network circumstances.
A packet sent through UDP should not be longer than the network MTU, otherwise it would be dropped. The network MTU may not be more than 1500 on most net nods(routers/switchs/hosts...), and even smaller sometimes. Though some nods may do sigmentation for ip packets, but you should not count on it when you are using UDP.
Suggestions
Use TCP instead in this application, as for:
You are sending data which expected to be complete (otherwise it would be useless).
You do not care about congestion control algorithms.
So just go with TCP.
Edit Based on The Update of The Question
So, as this is an excercise, in which you have to use UDP only.
As a file might be useless unless it is complete, you have to make sure:
All packets are possible to pass the path. Which means network should be connected both physically and virtually, and packet size should always be smaller than the MTU.
If any packets are lost, both the receiver and the sender should be able to know.
If any apckets come out of order, the receiver should be able to know.
Sender should be able to cache and resend the packets which are not confirmed by the receiver yet.
Make sure your have a good network connection. Split the image buffer into buffer array with each buffer item length less than 1000bytes(should be safe).
Then let's design an amature but simple protocol for this:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | sequence number |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| payload ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
For types, we may need:
hello: 0x01
bye: 0x02
ack: 0x03
nack: 0x04
data: 0x05
feedback: 0x06
...
Sequence should be mono-increasing. e.g. 1, 2, 3, 4.... (Not necessory to start from 1 but OK)
It works like following:
Sender->Receiver: hello(seq=i)
Receiver->Sender: ack(seq=i)
# Sender->Receiver: hello(seq=i)
# if timeout and got no ack for seq=i
Sender->Receiver: data(seq=i+1)
Receiver->Sender: ack(seq=i+1)
# Sender->Receiver: hello(seq=i+1)
# if timeout and got no ack for seq=i+1
Sender->Receiver: data(seq=i+2)
Sender->Receiver: data(seq=i+3)
Receiver->Sender: ack(seq=i+2)
Receiver->Sender: ack(seq=i+3)
# Sender->Receiver: hello(seq=i+2)
# if timeout and got no ack for seq=i+2 or got nack for seq=i+2
Sender->Receiver: bye(seq=n)
Receiver->Sender: ack(seq=n)
# bye is not necessory
Firstly, I think you need to learn how to use wirshark or tcmpdump to analysis network streams when debugging, that will help you find out the problem and solve it.
As for your program, there are several problems the user207421 has mensioned. I think it's better to use TCP, but if you want to learn UDP by this way, the thing you need is to do a slim reliable UDP by yourself.
For example, you may need the following models
Build a send buffer and recive buffer, check every time if the buffer is empty, if not, send/receive and process it.(Cause UDP has MTU)
Add some extra format of information in the head of each datagram, which includes the size of the whole message, the sequence of the datagram, the left size, etc.(Cause you need to cut your message into many parts)
Build a controller, which need to have some function like retransmission, rebuild the message, etc.(Cause UDP is unreliable, you need to check the completeness of all parts)
Hope that can help you.
I am trying to create a a client and server chat program using UDP. I have followed a tutorial making a similar program using TCP and tried to then translate my knowledge over to make one in similar fashion using UDP.
I have completed a client and server side with both showing no errors and will run, but once running neither will message the other or receive messages... can someone help me see what i'm doing wrong?
Server side for sending messages:
try{
//creates the packet to be sent
byte[] buf = new byte[256];
String msgout = serverText.getText().trim();
buf = msgout.getBytes();
//uses the socet.receive method to get the packet to retrieve information to send
DatagramPacket packet = new DatagramPacket(buf, buf.length);
ss.receive(packet);
InetAddress address = packet.getAddress();
int port = packet.getPort();
//uses packet information to create and send packet
DatagramPacket packetSend = new DatagramPacket(buf, buf.length, address, port);
ss.send(packetSend);
//Displays the message in the chat area and clears the text area
serverArea.setText(serverArea.getText().trim()+"\n Server: "+msgout);
serverText.setText("");
}catch (Exception e){
}
and then the main for setting the socket and receiving/printing:
String msgin = "";
try{
ss = new DatagramSocket(1420); // Sets socket at 1420
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
ss.receive(packet); //Receives the packet from the socket
//Converts the byte array into a string
String clientMsg = new String(packet.getData(), 0, packet.getLength());
while(!msgin.equals("exit")){
//Displays the message
msgin = clientMsg;
serverArea.setText(serverArea.getText().trim()+"\n Client: "+msgin); //displays client message
}
}catch(Exception e){
}
Here is the client side code, ill combine its send and receive areas into one block:
try{
//Creates the message out using the known socket that the Server creates and the known local address
String msgout = clientText.getText().trim();
sendBuf = msgout.getBytes();
InetAddress address = InetAddress.getLocalHost();
DatagramPacket sp = new DatagramPacket(sendBuf, sendBuf.length, address, 1420);
s.send(sp);
//Displays the text and clears the text field
clientchat.setText(clientchat.getText().trim()+"\n Server: "+msgout);
clientText.setText("");
}catch (Exception e){
}
String msgin = "";
try{
//Creates a socket
DatagramSocket s = new DatagramSocket();
//Receives the message from the server
byte[] buf = new byte[256];
DatagramPacket rp = new DatagramPacket(buf, buf.length);
s.receive(rp);
//Converts byte array to message
String clientMsg = new String(rp.getData(), 0, rp.getLength());
while(!msgin.equals("exit")){
//Displays the message
msgin = clientMsg;
clientchat.setText(clientchat.getText().trim()+"\n Server: "+msgin); //displays client message
}
}catch (Exception e){
}
Any help and tips will be greatly appreciated!
If nothing is happening, and you are unable to send/receive messages, it is likely that there are exceptions that are being generated.
However, since you have a try-catch block that catches all exceptions, and then simply does nothing, you will have no idea what exceptions are thrown, if any are thrown at all.
Rather than simply ignoring exceptions, you should at least be printing their cause.
In your catch statements, add the following and you will be able to more easily debug.
e.printStackTrace();
I am attempting to add a multiplayer form to a simple pong game, but when I try to start the DatagramPacket and try to read the IP and port it says the ip is null and the port is -1. Does anyone know why it would be doing this? I thought maybe it was because the socket hadn't recieved the packet yet, but when I look I saw that all code after socket.recieve(packet) isn't running.
Code where I start the server:
public GameServer(PongEngine engine) {
this.engine = engine;
try {
this.socket = new DatagramSocket(4269);
} catch (SocketException e) {
e.printStackTrace();
}
}
public void run() {
while(true) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
System.out.println(packet.getAddress() + ":" + packet.getPort());
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
if(message.trim().equalsIgnoreCase("ping")) {
System.out.println("CLIENT[" + packet.getAddress() + ":" + packet.getPort() + "] > " + message);
sendData("pong".getBytes(), packet.getAddress(), packet.getPort());
}
}
}
DatagramPacket's getAddress returns the IP address of the machine to which this datagram is being sent or from which the datagram was received.
In the first System.out.println you have just created the object, but have not done any network I/O with it.
Then you ignore the exception and just try to work with the datagram. If there was an I/O error, it's likely that the datagram was not initialized and hence still has IP address null and port -1.
If nothing happens after socket.receive() I'd assume the call is blocked, waiting for a packet to come in. Do you actually run the client that connects to your server code?
To add to Roberts answer, your code is simply out of order. Once you have that fixed then you can address why you might not be recieving a packet form the other PC like ccarton suggested.
Try this, and note the two comments
public void run() {
while(true) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
//Wait for packet (The code will not move on until a packet is received or there is an error)
System.out.println("Waiting for packet");
socket.receive(packet);
//Move your socket/port info after receiving a packet so you don't get null or -1
System.out.println("Packet received: "+ packet.getAddress() + ":" + packet.getPort());
//Move your code inside try, rather than after
String message = new String(packet.getData());
if(message.trim().equalsIgnoreCase("ping")) {
System.out.println("CLIENT[" + packet.getAddress() + ":" + packet.getPort() + "] > " + message);
sendData("pong".getBytes(), packet.getAddress(), packet.getPort());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Now do you still get the same issues?
In my Android App I'm using DatagramSockets to send messages to a server like this:
InetAddress address = InetAddress.getByName(host);
byte[] byteMessage = (" " + message + "\r\n##!!##").getBytes();
DatagramPacket packet = new DatagramPacket(byteMessage, byteMessage.length, address, port);
DatagramSocket socket = new DatagramSocket();
try
{
socket.send(packet);
}
finally
{
socket.close();
}
But only every N-1th packet is sent.
Meaning if I send 1 packet, nothing is sent. If I send the second, the first one gets send. If I send the third, the second gets send etc etc.
EDIT:
So after the first comments I a) got rid of the useless throw-statement b) tried not closing the socket after sending. It doesn't help.
So as an example for clarification: The following code works perfectly and I receive the package server-side. But it's obviously not a pretty solution...
InetAddress address = InetAddress.getByName(host);
byte[] byteMessage = (" " + message + "\r\n##!!##").getBytes();
DatagramPacket packet = new DatagramPacket(byteMessage, byteMessage.length, address, port);
DatagramSocket socket = new DatagramSocket();
try
{
socket.send(packet);
String emptyMessage = " ";
socket.send(new DatagramPacket(emptyMessage.getBytes(), emptyMessage.getBytes().length, address, port));
}
finally
{
socket.close();
}
I'm just sending a second "empty" message afterwards. I first tried sending an empty byte array, but that does not work.
I am triying to comunicate 2 machines through datagram sockets but I guess I am missing something...
Machine A is runs an Android App (client)
Machine B is a server writen in Python
I can send a message from A to B without any problem, but A never gets the answer from B, the code is the following:
Client (Java) :
InetAddress serverAddr = InetAddress.getByName("10.0.0.10");
DatagramSocket socket = new DatagramSocket();
byte[] bufSent = "register".getBytes();
DatagramPacket dpSent = new DatagramPacket(bufSent,bufSent.length, serverAddr, 8088);
socket.send(dpSent);
byte[] bufRecv = new byte[1024];
DatagramPacket dpReceive = new DatagramPacket(bufRecv, bufRecv.length);
socket.receive(dpReceive);
String serverMessage = new String(dpReceive.getData(), 0, dpReceive.getLength());
Log.v(LOGTAG, "Received " + serverMessage);
Server (Python):
import socket
UDP_IP_DEST = "10.0.0.11"
UDP_IP = "10.0.0.10"
UDP_PORT = 8088
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
if data:
print "received message:", data
sock.sendto("I got the message", (UDP_IP_DEST, UDP_PORT))
Does anyone see where is the mistake? The point is that I have tried to send the answer to another machine instead of the mobile and it works fine.
Thanks a lot.
I had a similar problem with receiving, here's some code we use in our app for Datagrams modified with your values, you can see we do a few things differently in the socket set up. mSocket is just a private DatagramSocket member variable. Give it a try. I think you might need to bind, and possible set the reuse address flag.
try
{
mSocket = new DatagramSocket(null);
mSocket.setReuseAddress(true);
mSocket.setBroadcast(false);
mSocket.bind(new InetSocketAddress(8088));
//Set a 1.5 second timeout for the coming receive calls
mSocket.setSoTimeout(1500);
String data = "myData";
DatagramPacket udpPacket = new DatagramPacket(data.getBytes(), data.length(), InetAddress.getByName("10.0.0.10"), 8088);
mSocket.send(udpPacket);
byte[] buf = new byte[1024];
DatagramPacket recvPacket = new DatagramPacket(buf, buf.length);
mSocket.receive(recvPacket);
String response = new String(recvPacket.getData());
}
catch (SocketException e)
{
e.printStackTrace();
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}